@@ -89,6 +89,9 @@ D_FRONTEND_OBJS = \
d/canthrow.o \
d/chkformat.o \
d/clone.o \
+ d/common-file.o \
+ d/common-outbuffer.o \
+ d/common-string.o \
d/compiler.o \
d/complex.o \
d/cond.o \
@@ -120,6 +123,7 @@ D_FRONTEND_OBJS = \
d/escape.o \
d/expression.o \
d/expressionsem.o \
+ d/file_manager.o \
d/foreachvar.o \
d/func.o \
d/globals.o \
@@ -131,6 +135,7 @@ D_FRONTEND_OBJS = \
d/identifier.o \
d/impcnvtab.o \
d/imphint.o \
+ d/importc.o \
d/init.o \
d/initsem.o \
d/inline.o \
@@ -157,7 +162,6 @@ D_FRONTEND_OBJS = \
d/root-filename.o \
d/root-hash.o \
d/root-longdouble.o \
- d/root-outbuffer.o \
d/root-port.o \
d/root-region.o \
d/root-rmem.o \
@@ -393,6 +397,10 @@ d/%.o: d/dmd/%.d
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
+d/common-%.o: d/dmd/common/%.d
+ $(DCOMPILE) $(D_INCLUDES) $<
+ $(DPOSTCOMPILE)
+
d/root-%.o: d/dmd/root/%.d
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
@@ -236,7 +236,7 @@ build_frontend_type (tree type)
sdecl->parent = stubmod;
sdecl->structsize = int_size_in_bytes (type);
sdecl->alignsize = TYPE_ALIGN_UNIT (type);
- sdecl->alignment = STRUCTALIGN_DEFAULT;
+ sdecl->alignment.setDefault ();
sdecl->sizeok = Sizeok::done;
sdecl->type = (TypeStruct::create (sdecl))->addMod (mod);
sdecl->type->ctype = type;
@@ -275,7 +275,7 @@ build_frontend_type (tree type)
NULL);
vd->parent = sdecl;
vd->offset = tree_to_uhwi (byte_position (field));
- vd->semanticRun = PASSsemanticdone;
+ vd->semanticRun = PASS::semanticdone;
vd->csym = field;
sdecl->members->push (vd);
sdecl->fields.push (vd);
@@ -856,6 +856,9 @@ d_build_d_type_nodes (void)
ireal_type_node = build_distinct_type_copy (long_double_type_node);
TYPE_IMAGINARY_FLOAT (ireal_type_node) = 1;
+ /* Noreturn type. */
+ noreturn_type_node = build_distinct_type_copy (void_type_node);
+
/* Calling build_ctype() links the front-end Type to the GCC node,
and sets the TYPE_NAME to the D language type. */
for (unsigned ty = 0; ty < (unsigned) TY::TMAX; ty++)
@@ -2140,6 +2140,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
/* Build the argument list for the call. */
vec <tree, va_gc> *args = NULL;
tree saved_args = NULL_TREE;
+ bool noreturn_call = false;
/* If this is a delegate call or a nested function being called as
a delegate, the object should not be NULL. */
@@ -2165,9 +2166,9 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
}
}
- size_t nparams = tf->parameterList.length ();
+ const size_t nparams = tf->parameterList.length ();
/* if _arguments[] is the first argument. */
- size_t varargs = tf->isDstyleVariadic ();
+ const size_t varargs = tf->isDstyleVariadic ();
/* Assumes arguments->length <= formal_args->length if (!tf->varargs). */
for (size_t i = 0; i < arguments->length; ++i)
@@ -2206,6 +2207,11 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
build_address (targ));
}
+ /* Type `noreturn` is a terminator, as no other arguments can possibly
+ be evaluated after it. */
+ if (TREE_TYPE (targ) == noreturn_type_node)
+ noreturn_call = true;
+
vec_safe_push (args, targ);
}
}
@@ -2217,13 +2223,27 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
saved_args = compound_expr (callee, saved_args);
}
+ /* If we saw a `noreturn` parameter, any unreachable argument evaluations
+ after it are discarded, as well as the function call itself. */
+ if (noreturn_call)
+ {
+ if (TREE_SIDE_EFFECTS (callee))
+ saved_args = compound_expr (callee, saved_args);
+
+ tree arg;
+ unsigned int ix;
+
+ FOR_EACH_VEC_SAFE_ELT (args, ix, arg)
+ saved_args = compound_expr (saved_args, arg);
+
+ /* Add a stub result type for the expression. */
+ tree result = build_zero_cst (TREE_TYPE (ctype));
+ return compound_expr (saved_args, result);
+ }
+
tree result = build_call_vec (TREE_TYPE (ctype), callee, args);
SET_EXPR_LOCATION (result, input_location);
- /* Enforce left to right evaluation. */
- if (tf->linkage == LINK::d)
- CALL_EXPR_ARGS_ORDERED (result) = 1;
-
result = maybe_expand_intrinsic (result);
/* Return the value in a temporary slot so that it can be evaluated
@@ -2296,6 +2316,10 @@ build_vthis_function (tree basetype, tree type)
TYPE_ARG_TYPES (type));
tree fntype = build_function_type (TREE_TYPE (type), argtypes);
+ /* Copy volatile qualifiers from the original function type. */
+ if (TYPE_QUALS (type) & TYPE_QUAL_VOLATILE)
+ fntype = build_qualified_type (fntype, TYPE_QUAL_VOLATILE);
+
if (RECORD_OR_UNION_TYPE_P (basetype))
TYPE_METHOD_BASETYPE (fntype) = TYPE_MAIN_VARIANT (basetype);
else
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic.h"
#include "d-tree.h"
+#include "d-frontend.h"
/* Implements back-end specific interfaces used by the frontend. */
@@ -51,7 +52,7 @@ isBuiltin (FuncDeclaration *fd)
Return result; NULL if cannot evaluate it. */
Expression *
-eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
+eval_builtin (const Loc &loc, FuncDeclaration *fd, Expressions *arguments)
{
if (fd->builtin == BUILTIN::unimp)
return NULL;
@@ -78,10 +79,16 @@ eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
/* Build and return typeinfo type for TYPE. */
Type *
-getTypeInfoType (Loc loc, Type *type, Scope *sc)
+getTypeInfoType (const Loc &loc, Type *type, Scope *sc)
{
gcc_assert (type->ty != TY::Terror);
check_typeinfo_type (loc, sc);
create_typeinfo (type, sc ? sc->_module->importedFrom : NULL);
return type->vtinfo->type;
}
+
+void
+toObjFile (Dsymbol *ds, bool)
+{
+ build_decl_tree (ds);
+}
@@ -120,52 +120,47 @@ d_gimplify_addr_expr (tree *expr_p)
static gimplify_status
d_gimplify_call_expr (tree *expr_p, gimple_seq *pre_p)
{
- if (CALL_EXPR_ARGS_ORDERED (*expr_p))
- {
- /* Strictly evaluate all arguments from left to right. */
- int nargs = call_expr_nargs (*expr_p);
- location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
+ /* Strictly evaluate all arguments from left to right. */
+ int nargs = call_expr_nargs (*expr_p);
+ location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
- /* No need to enforce evaluation order if only one argument. */
- if (nargs < 2)
- return GS_UNHANDLED;
+ /* No need to enforce evaluation order if only one argument. */
+ if (nargs < 2)
+ return GS_UNHANDLED;
- /* Or if all arguments are already free of side-effects. */
- bool has_side_effects = false;
- for (int i = 0; i < nargs; i++)
+ /* Or if all arguments are already free of side-effects. */
+ bool has_side_effects = false;
+ for (int i = 0; i < nargs; i++)
+ {
+ if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i)))
{
- if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i)))
- {
- has_side_effects = true;
- break;
- }
+ has_side_effects = true;
+ break;
}
+ }
- if (!has_side_effects)
- return GS_UNHANDLED;
-
- /* Leave the last argument for gimplify_call_expr. */
- for (int i = 0; i < nargs - 1; i++)
- {
- tree new_arg = CALL_EXPR_ARG (*expr_p, i);
+ if (!has_side_effects)
+ return GS_UNHANDLED;
- /* If argument has a side-effect, gimplify_arg will handle it. */
- if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR)
- return GS_ERROR;
+ /* Leave the last argument for gimplify_call_expr. */
+ for (int i = 0; i < nargs - 1; i++)
+ {
+ tree new_arg = CALL_EXPR_ARG (*expr_p, i);
- /* Even if an argument itself doesn't have any side-effects, it
- might be altered by another argument in the list. */
- if (new_arg == CALL_EXPR_ARG (*expr_p, i)
- && !really_constant_p (new_arg))
- new_arg = get_formal_tmp_var (new_arg, pre_p);
+ /* If argument has a side-effect, gimplify_arg will handle it. */
+ if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR)
+ return GS_ERROR;
- CALL_EXPR_ARG (*expr_p, i) = new_arg;
- }
+ /* Even if an argument itself doesn't have any side-effects, it
+ might be altered by another argument in the list. */
+ if (new_arg == CALL_EXPR_ARG (*expr_p, i)
+ && !really_constant_p (new_arg))
+ new_arg = get_formal_tmp_var (new_arg, pre_p);
- return GS_OK;
+ CALL_EXPR_ARG (*expr_p, i) = new_arg;
}
- return GS_UNHANDLED;
+ return GS_OK;
}
/* Gimplify an UNSIGNED_RSHIFT_EXPR node. */
@@ -674,6 +674,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
case OPT_ftransition_all:
global.params.vfield = value;
global.params.vgc = value;
+ global.params.vin = value;
global.params.vmarkdown= value;
global.params.vtls = value;
break;
@@ -682,6 +683,10 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.vfield = value;
break;
+ case OPT_ftransition_in:
+ global.params.vin = value;
+ break;
+
case OPT_ftransition_nogc:
global.params.vgc = value;
break;
@@ -1176,6 +1181,14 @@ d_parse_file (void)
{
Module *m = modules[i];
+ /* If this is the `__main` module, check that `D main` hasn't already
+ been declared in user code before running semantic on it. */
+ if (m == main_module && global.hasMainFunction)
+ {
+ modules.remove (i);
+ continue;
+ }
+
if (global.params.verbose)
message ("semantic %s", m->toChars ());
@@ -1357,6 +1370,9 @@ d_parse_file (void)
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
+
+ /* Skip generating code for header files, or when the module wasn't
+ specified by `-fonly=`. */
if ((m->isHdrFile && m != main_module)
|| (d_option.fonly && m != Module::rootModule))
continue;
@@ -47,7 +47,6 @@ typedef Array <Expression *> Expressions;
/* Usage of TREE_LANG_FLAG_?:
0: METHOD_CALL_EXPR
- 1: CALL_EXPR_ARGS_ORDERED (in CALL_EXPR).
Usage of TYPE_LANG_FLAG_?:
0: TYPE_SHARED
@@ -351,11 +350,6 @@ lang_tree_node
#define METHOD_CALL_EXPR(NODE) \
(TREE_LANG_FLAG_0 (NODE))
-/* True if all arguments in a call expression should be evaluated in the
- order they are given (left to right). */
-#define CALL_EXPR_ARGS_ORDERED(NODE) \
- (TREE_LANG_FLAG_1 (CALL_EXPR_CHECK (NODE)))
-
/* True if the type was declared 'shared'. */
#define TYPE_SHARED(NODE) \
(TYPE_LANG_FLAG_0 (NODE))
@@ -430,6 +424,7 @@ enum d_tree_index
DTI_ARRAY_TYPE,
DTI_NULL_ARRAY,
+ DTI_BOTTOM_TYPE,
DTI_MAX
};
@@ -465,6 +460,8 @@ extern GTY(()) tree d_global_trees[DTI_MAX];
#define array_type_node d_global_trees[DTI_ARRAY_TYPE]
/* Null initializer for dynamic arrays. */
#define null_array_node d_global_trees[DTI_NULL_ARRAY]
+/* The bottom type, referred to as `noreturn` in code. */
+#define noreturn_type_node d_global_trees[DTI_BOTTOM_TYPE]
/* A prefix for internal variables, which are not user-visible. */
#if !defined (NO_DOT_IN_LABEL)
@@ -116,59 +116,6 @@ gcc_attribute_p (Dsymbol *decl)
return false;
}
-/* Subroutine of pragma declaration visitor for marking the function in the
- defined in SYM as a global constructor or destructor. If ISCTOR is true,
- then we're applying pragma(crt_constructor). */
-
-static int
-apply_pragma_crt (Dsymbol *sym, bool isctor)
-{
- AttribDeclaration *ad = sym->isAttribDeclaration ();
- if (ad != NULL)
- {
- int nested = 0;
-
- /* Walk all declarations of the attribute scope. */
- Dsymbols *ds = ad->include (NULL);
- if (ds)
- {
- for (size_t i = 0; i < ds->length; i++)
- nested += apply_pragma_crt ((*ds)[i], isctor);
- }
-
- return nested;
- }
-
- FuncDeclaration *fd = sym->isFuncDeclaration ();
- if (fd != NULL)
- {
- tree decl = get_decl_tree (fd);
-
- /* Apply flags to the function. */
- if (isctor)
- {
- DECL_STATIC_CONSTRUCTOR (decl) = 1;
- decl_init_priority_insert (decl, DEFAULT_INIT_PRIORITY);
- }
- else
- {
- DECL_STATIC_DESTRUCTOR (decl) = 1;
- decl_fini_priority_insert (decl, DEFAULT_INIT_PRIORITY);
- }
-
- if (fd->linkage != LINK::c)
- {
- error_at (make_location_t (fd->loc),
- "must be %<extern(C)%> for %<pragma(%s)%>",
- isctor ? "crt_constructor" : "crt_destructor");
- }
-
- return 1;
- }
-
- return 0;
-}
-
/* Implements the visitor interface to lower all Declaration AST classes
emitted from the D Front-end to GCC trees.
All visit methods accept one parameter D, which holds the frontend AST
@@ -210,18 +157,18 @@ public:
void visit (Module *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
build_module_tree (d);
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Write the imported symbol to debug. */
void visit (Import *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
/* Implements import declarations by telling the debug back-end we are
@@ -266,7 +213,7 @@ public:
false, false);
}
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Expand any local variables found in tuples. */
@@ -312,18 +259,6 @@ public:
"pragma(%s) not implemented", d->ident->toChars ());
}
}
- else if (d->ident == Identifier::idPool ("crt_constructor")
- || d->ident == Identifier::idPool ("crt_destructor"))
- {
- /* Handle pragma(crt_constructor) and pragma(crt_destructor). Apply
- flag to indicate that the functions enclosed should run automatically
- at the beginning or end of execution. */
- bool isctor = (d->ident == Identifier::idPool ("crt_constructor"));
-
- if (apply_pragma_crt (d, isctor) > 1)
- error_at (make_location_t (d->loc),
- "can only apply to a single declaration");
- }
visit ((AttribDeclaration *) d);
}
@@ -422,7 +357,7 @@ public:
void visit (StructDeclaration *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
if (d->type->ty == TY::Terror)
@@ -470,7 +405,7 @@ public:
if (d->xhash)
this->build_dsymbol (d->xhash);
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Finish semantic analysis of functions in vtbl for class CD. */
@@ -537,7 +472,7 @@ public:
void visit (ClassDeclaration *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
if (d->type->ty == TY::Terror)
@@ -603,7 +538,7 @@ public:
if (TYPE_NAME (ctype))
d_pushdecl (TYPE_NAME (ctype));
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Write out compiler generated TypeInfo and vtables for the given interface
@@ -611,7 +546,7 @@ public:
void visit (InterfaceDeclaration *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
if (d->type->ty == TY::Terror)
@@ -646,7 +581,7 @@ public:
if (TYPE_NAME (ctype))
d_pushdecl (TYPE_NAME (ctype));
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Write out compiler generated TypeInfo and initializer for the given
@@ -654,7 +589,7 @@ public:
void visit (EnumDeclaration *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
if (d->errors || d->type->ty == TY::Terror)
@@ -685,7 +620,7 @@ public:
if (TYPE_NAME (ctype))
d_pushdecl (TYPE_NAME (ctype));
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Finish up a variable declaration and push it into the current scope.
@@ -693,7 +628,7 @@ public:
void visit (VarDeclaration *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
if (d->type->ty == TY::Terror)
@@ -703,6 +638,21 @@ public:
return;
}
+ /* Variables of type `noreturn` are just placeholders, and evaluate to
+ `assert(0)` if ever read. */
+ if (d->type->isTypeNoreturn ())
+ {
+ if (!d->isDataseg () && !d->isMember ()
+ && d->_init && !d->_init->isVoidInitializer ())
+ {
+ Expression *e = d->type->defaultInitLiteral (d->loc);
+ tree exp = build_expr (e);
+ add_stmt (exp);
+ }
+
+ return;
+ }
+
if (d->aliassym)
{
this->build_dsymbol (d->toAlias ());
@@ -762,7 +712,7 @@ public:
/* Frontend should have already caught this. */
gcc_assert (!integer_zerop (size)
- || d->type->toBasetype ()->ty == TY::Tsarray);
+ || d->type->toBasetype ()->isTypeSArray ());
d_finish_decl (decl);
@@ -797,7 +747,7 @@ public:
}
}
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Generate and compile a static TypeInfo declaration, but only if it is
@@ -805,7 +755,7 @@ public:
void visit (TypeInfoDeclaration *d)
{
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
if (speculative_type_p (d->tinfo))
@@ -814,7 +764,7 @@ public:
tree t = get_typeinfo_decl (d);
DECL_INITIAL (t) = layout_typeinfo (d);
d_finish_decl (t);
- d->semanticRun = PASSobj;
+ d->semanticRun = PASS::obj;
}
/* Finish up a function declaration and compile it all the way
@@ -823,7 +773,7 @@ public:
void visit (FuncDeclaration *d)
{
/* Already generated the function. */
- if (d->semanticRun >= PASSobj)
+ if (d->semanticRun >= PASS::obj)
return;
/* Don't emit any symbols from gcc.attribute module. */
@@ -861,7 +811,7 @@ public:
}
/* Ensure all semantic passes have run. */
- if (d->semanticRun < PASSsemantic3)
+ if (d->semanticRun < PASS::semantic3)
{
d->functionSemantic3 ();
Module::runDeferredSemantic3 ();
@@ -887,8 +837,8 @@ public:
message ("function %s", d->toPrettyChars ());
/* Start generating code for this function. */
- gcc_assert (d->semanticRun == PASSsemantic3done);
- d->semanticRun = PASSobj;
+ gcc_assert (d->semanticRun == PASS::semantic3done);
+ d->semanticRun = PASS::obj;
tree old_context = start_function (d);
@@ -927,12 +877,19 @@ public:
}
/* formal function parameters. */
- size_t n_parameters = d->parameters ? d->parameters->length : 0;
+ const size_t n_parameters = d->parameters ? d->parameters->length : 0;
for (size_t i = 0; i < n_parameters; i++)
{
VarDeclaration *param = (*d->parameters)[i];
+
parm_decl = get_symbol_decl (param);
+
+ /* Type `noreturn` is a terminator, as no other arguments can possibly
+ be evaluated after it. */
+ if (TREE_TYPE (parm_decl) == noreturn_type_node)
+ break;
+
/* Chain them in the correct order. */
param_list = chainon (param_list, parm_decl);
}
@@ -1136,9 +1093,9 @@ get_symbol_decl (Declaration *decl)
declaration_type (vd));
/* If any alignment was set on the declaration. */
- if (vd->alignment != STRUCTALIGN_DEFAULT)
+ if (!vd->alignment.isDefault ())
{
- SET_DECL_ALIGN (decl->csym, vd->alignment * BITS_PER_UNIT);
+ SET_DECL_ALIGN (decl->csym, vd->alignment.get () * BITS_PER_UNIT);
DECL_USER_ALIGN (decl->csym) = 1;
}
@@ -1321,6 +1278,20 @@ get_symbol_decl (Declaration *decl)
else if (fd->inlining == PINLINE::never)
DECL_UNINLINABLE (decl->csym) = 1;
+ /* In [pragma/crtctor], Annotates a function so it is run after the C
+ runtime library is initialized and before the D runtime library is
+ initialized. */
+ if (fd->isCrtCtorDtor == 1)
+ {
+ DECL_STATIC_CONSTRUCTOR (decl->csym) = 1;
+ decl_init_priority_insert (decl->csym, DEFAULT_INIT_PRIORITY);
+ }
+ else if (fd->isCrtCtorDtor == 2)
+ {
+ DECL_STATIC_DESTRUCTOR (decl->csym) = 1;
+ decl_fini_priority_insert (decl->csym, DEFAULT_INIT_PRIORITY);
+ }
+
/* Function was declared `naked'. */
if (fd->naked)
{
@@ -1342,7 +1313,7 @@ get_symbol_decl (Declaration *decl)
DECL_FINAL_P (decl->csym) = 1;
/* Function is of type `noreturn' or `typeof(*null)'. */
- if (fd->type->nextOf ()->ty == TY::Tnoreturn)
+ if (fd->type->nextOf ()->isTypeNoreturn ())
TREE_THIS_VOLATILE (decl->csym) = 1;
/* Check whether this function is expanded by the frontend. */
@@ -2246,9 +2217,9 @@ aggregate_initializer_decl (AggregateDeclaration *decl)
TREE_READONLY (sinit) = 1;
/* Honor struct alignment set by user. */
- if (sd && sd->alignment != STRUCTALIGN_DEFAULT)
+ if (sd && !sd->alignment.isDefault ())
{
- SET_DECL_ALIGN (sinit, sd->alignment * BITS_PER_UNIT);
+ SET_DECL_ALIGN (sinit, sd->alignment.get () * BITS_PER_UNIT);
DECL_USER_ALIGN (sinit) = true;
}
@@ -1,4 +1,4 @@
-b8384668f28741ad5884fc055a2bdb9c05fd95ec
+568496d5b6ed02d577dfa86f73c7bb4edee05813
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.
@@ -20,6 +20,7 @@
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [dmd/](https://github.com/dlang/dmd/tree/master/src/dmd) | The dmd driver and front-end |
| [dmd/backend/](https://github.com/dlang/dmd/tree/master/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). |
+| [dmd/common/](https://github.com/dlang/dmd/tree/master/src/dmd/common) | Code shared by the front-end and back-end |
| [dmd/root/](https://github.com/dlang/dmd/tree/master/src/dmd/root) | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516). |
DMD has a mostly flat directory structure, so this section aims to divide all source files into logical groups for easier navigation.
@@ -126,6 +127,7 @@
| [optimize.d](https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d) | Do constant folding more generally |
| [dcast.d](https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d) | Implicit or explicit cast(), finding common types e.g. in `x ? a : b`, integral promotions |
| [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types |
+| [importc.d](https://github.com/dlang/dmd/blob/master/src/dmd/importc.d) | Helpers specific to ImportC |
| [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. |
**Compile Time Function Execution (CTFE)**
@@ -243,14 +245,14 @@
Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/master/src/dmd/root).
-| File | Purpose |
-|-----------------------------------------------------------------------------|---------------------------------------------------|
-| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/env.d) | Modify environment variables |
-| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color |
-| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/utf.d) | Encoding/decoding Unicode text |
-| [filecache.d](https://github.com/dlang/dmd/blob/master/src/dmd/filecache.d) | Keep file contents in memory |
-| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths |
-| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/complex.d) | A complex number type |
+| File | Purpose |
+|-----------------------------------------------------------------------------------|---------------------------------------------------|
+| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/env.d) | Modify environment variables |
+| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color |
+| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/utf.d) | Encoding/decoding Unicode text |
+| [file_manager.d](https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d) | Keep file contents in memory |
+| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths |
+| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/complex.d) | A complex number type |
| File | Purpose |
|---------------------------------------------------------------------------------|---------------------------------------------------------------|
@@ -1 +1 @@
-v2.097.2
+v2.098.0
@@ -21,6 +21,7 @@ import dmd.aliasthis;
import dmd.apply;
import dmd.arraytypes;
import dmd.astenums;
+import dmd.attrib;
import dmd.declaration;
import dmd.dscope;
import dmd.dstruct;
@@ -115,11 +116,12 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
AliasThis aliasthis; /// forward unresolved lookups to aliasthis
- DtorDeclarations dtors; /// Array of destructors
- DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors
- DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D
+ DtorDeclarations userDtors; /// user-defined destructors (`~this()`) - mixins can yield multiple ones
+ DtorDeclaration aggrDtor; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
+ DtorDeclaration dtor; /// the aggregate destructor exposed as `__xdtor` alias
+ /// (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
- FuncDeclaration fieldDtor; /// aggregate destructor for just the fields
+ DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields
Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
@@ -177,7 +179,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
* Returns:
* false if failed to determine the size.
*/
- final bool determineSize(Loc loc)
+ final bool determineSize(const ref Loc loc)
{
//printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
@@ -331,7 +333,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
* false if any errors occur.
* Otherwise, returns true and the missing arguments will be pushed in elements[].
*/
- final bool fill(Loc loc, Expressions* elements, bool ctorinit)
+ final bool fill(const ref Loc loc, Expressions* elements, bool ctorinit)
{
//printf("AggregateDeclaration::fill() %s\n", toChars());
assert(sizeok == Sizeok.done);
@@ -482,45 +484,52 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
* Align sizes of 0, as we may not know array sizes yet.
* Params:
* alignment = struct alignment that is in effect
- * size = alignment requirement of field
+ * memalignsize = natural alignment of field
* poffset = pointer to offset to be aligned
*/
- extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe
+ extern (D) static void alignmember(structalign_t alignment, uint memalignsize, uint* poffset) pure nothrow @safe
{
- //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
- switch (alignment)
- {
- case cast(structalign_t)1:
- // No alignment
- break;
+ //debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, *poffset);
+ uint alignvalue;
- case cast(structalign_t)STRUCTALIGN_DEFAULT:
+ if (alignment.isDefault())
+ {
// Alignment in Target::fieldalignsize must match what the
// corresponding C compiler's default alignment behavior is.
- assert(size > 0 && !(size & (size - 1)));
- *poffset = (*poffset + size - 1) & ~(size - 1);
- break;
-
- default:
+ alignvalue = memalignsize;
+ }
+ else if (alignment.isPack()) // #pragma pack semantics
+ {
+ alignvalue = alignment.get();
+ if (memalignsize < alignvalue)
+ alignvalue = memalignsize; // align to min(memalignsize, alignment)
+ }
+ else if (alignment.get() > 1)
+ {
// Align on alignment boundary, which must be a positive power of 2
- assert(alignment > 0 && !(alignment & (alignment - 1)));
- *poffset = (*poffset + alignment - 1) & ~(alignment - 1);
- break;
+ alignvalue = alignment.get();
}
+ else
+ return;
+
+ assert(alignvalue > 0 && !(alignvalue & (alignvalue - 1)));
+ *poffset = (*poffset + alignvalue - 1) & ~(alignvalue - 1);
}
/****************************************
- * Place a member (mem) into an aggregate (agg), which can be a struct, union or class
+ * Place a field (mem) into an aggregate (agg), which can be a struct, union or class
+ * Params:
+ * nextoffset = location just past the end of the previous field in the aggregate.
+ * Updated to be just past the end of this field to be placed, i.e. the future nextoffset
+ * memsize = size of field
+ * memalignsize = natural alignment of field
+ * alignment = alignment in effect for this field
+ * paggsize = size of aggregate (updated)
+ * paggalignsize = alignment of aggregate (updated)
+ * isunion = the aggregate is a union
* Returns:
- * offset to place field at
+ * aligned offset to place field at
*
- * nextoffset: next location in aggregate
- * memsize: size of member
- * memalignsize: natural alignment of member
- * alignment: alignment in effect for this member
- * paggsize: size of aggregate (updated)
- * paggalignsize: alignment of aggregate (updated)
- * isunion: the aggregate is a union
*/
extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
@@ -528,7 +537,8 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
uint ofs = *nextoffset;
const uint actualAlignment =
- alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
+ alignment.isDefault() || alignment.isPack() && memalignsize < alignment.get()
+ ? memalignsize : alignment.get();
// Ensure no overflow
bool overflow;
@@ -536,7 +546,10 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
addu(ofs, sz, overflow);
if (overflow) assert(0);
- alignmember(alignment, memalignsize, &ofs);
+ // Skip no-op for noreturn without custom aligment
+ if (memsize != 0 || !alignment.isDefault())
+ alignmember(alignment, memalignsize, &ofs);
+
uint memoffset = ofs;
ofs += memsize;
if (ofs > *paggsize)
@@ -105,11 +105,12 @@ public:
AliasThis *aliasthis; // forward unresolved lookups to aliasthis
- DtorDeclarations dtors; // Array of destructors
- DtorDeclaration *dtor; // aggregate destructor
- DtorDeclaration *primaryDtor; // non-deleting C++ destructor, same as dtor for D
+ DtorDeclarations userDtors; // user-defined destructors (`~this()`) - mixins can yield multiple ones
+ DtorDeclaration *aggrDtor; // aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
+ DtorDeclaration *dtor; // the aggregate destructor exposed as `__xdtor` alias
+ // (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
DtorDeclaration *tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI)
- FuncDeclaration *fieldDtor; // aggregate destructor for just the fields
+ DtorDeclaration *fieldDtor; // function destructing (non-inherited) fields
Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this)
@@ -121,10 +122,10 @@ public:
virtual Scope *newScope(Scope *sc);
void setScope(Scope *sc);
size_t nonHiddenFields();
- bool determineSize(Loc loc);
+ bool determineSize(const Loc &loc);
virtual void finalizeSize() = 0;
d_uns64 size(const Loc &loc);
- bool fill(Loc loc, Expressions *elements, bool ctorinit);
+ bool fill(const Loc &loc, Expressions *elements, bool ctorinit);
Type *getType();
bool isDeprecated() const; // is aggregate deprecated?
void setDeprecated();
@@ -184,7 +185,7 @@ public:
// ABI-specific type(s) if the struct can be passed in registers
TypeTuple *argTypes;
- static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
+ static StructDeclaration *create(const Loc &loc, Identifier *id, bool inObject);
StructDeclaration *syntaxCopy(Dsymbol *s);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
const char *kind() const;
@@ -277,7 +278,7 @@ public:
ObjcClassDeclaration objc; // Data for a class declaration that is needed for the Objective-C integration
Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
- static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
+ static ClassDeclaration *create(const Loc &loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
const char *toPrettyChars(bool QualifyTypes = false);
ClassDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
@@ -72,17 +72,32 @@ extern (C++) final class AliasThis : Dsymbol
}
}
-Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false)
+/*************************************
+ * Find the `alias this` symbol of e's type.
+ * Params:
+ * sc = context
+ * e = expression forming the `this`
+ * gag = if true 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)
{
+ import dmd.typesem : dotExp;
for (AggregateDeclaration ad = isAggregate(e.type); ad;)
{
if (ad.aliasthis)
{
- uint olderrors = gag ? global.startGagging() : 0;
Loc loc = e.loc;
Type tthis = (e.op == TOK.type ? e.type : null);
- e = new DotIdExp(loc, e, ad.aliasthis.ident);
- e = e.expressionSemantic(sc);
+ const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0);
+ uint olderrors = gag ? global.startGagging() : 0;
+ e = dotExp(e.type, sc, e, ad.aliasthis.ident, flags);
+ if (!e || findOnly)
+ return gag && global.endGagging(olderrors) ? null : e;
+
if (tthis && ad.aliasthis.sym.needThis())
{
if (e.op == TOK.variable)
@@ -26,7 +26,7 @@ import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.mtype;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.statement;
import dmd.tokens;
import dmd.visitor;
@@ -42,7 +42,7 @@ import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.objc; // for objc.addSymbols
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.target; // for target.systemLinkage
import dmd.tokens;
import dmd.visitor;
@@ -696,12 +696,9 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
{
Expressions* exps; /// Expression(s) yielding the desired alignment,
/// the largest value wins
- enum structalign_t UNKNOWN = 0; /// alignment not yet computed
- static assert(STRUCTALIGN_DEFAULT != UNKNOWN);
-
- /// the actual alignment, `UNKNOWN` until it's either set to the value of `ealign`
- /// or `STRUCTALIGN_DEFAULT` if `ealign` is null ( / an error ocurred)
- structalign_t salign = UNKNOWN;
+ /// the actual alignment is Unknown until it's either set to the value of `ealign`
+ /// or the default if `ealign` is null ( / an error ocurred)
+ structalign_t salign;
extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl)
@@ -709,8 +706,7 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
super(loc, null, decl);
if (exp)
{
- if (!exps)
- exps = new Expressions();
+ exps = new Expressions();
exps.push(exp);
}
}
@@ -721,6 +717,12 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
this.exps = exps;
}
+ extern (D) this(const ref Loc loc, structalign_t salign, Dsymbols* decl)
+ {
+ super(loc, null, decl);
+ this.salign = salign;
+ }
+
override AlignDeclaration syntaxCopy(Dsymbol s)
{
assert(!s);
@@ -1196,7 +1198,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration
// expand static foreach
import dmd.statementsem: makeTupleForeach;
- Dsymbols* d = makeTupleForeach!(true,true)(_scope, sfe.aggrfe, decl, sfe.needExpansion);
+ Dsymbols* d = makeTupleForeach!(true,true)(_scope, sfe.aggrfe, decl, sfe.needExpansion).decl;
if (d) // process generated declarations
{
// Add members lazily.
@@ -108,7 +108,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
return;
}
}
- if (s.exp.type.toBasetype().isTypeNoreturn())
+ if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn())
result = BE.halt;
if (canThrow(s.exp, func, mustNotThrow))
result |= BE.throw_;
@@ -146,7 +146,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement()))
{
}
- else
+ else if (!func.getModule().isCFile)
{
const(char)* gototype = s.isCaseStatement() ? "case" : "default";
s.deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
@@ -30,4 +30,4 @@ public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd);
* Evaluate builtin function.
* Return result; NULL if cannot evaluate it.
*/
-public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments);
+public extern (C++) Expression eval_builtin(const ref Loc loc, FuncDeclaration fd, Expressions* arguments);
@@ -690,8 +690,8 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx,
return error();
while ('0' <= format[i] && format[i] <= '9')
{
- ++i;
- if (i == length)
+ ++i;
+ if (i == length)
return error();
}
}
@@ -720,8 +720,8 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx,
return error();
while ('0' <= format[i] && format[i] <= '9')
{
- ++i;
- if (i == length)
+ ++i;
+ if (i == length)
return error();
}
}
@@ -794,6 +794,19 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
if (!needToHash(sd))
return null;
+ /* The trouble is that the following code relies on .tupleof, but .tupleof
+ * is not allowed for C files. If we allow it for C files, then that turns on
+ * the other D properties, too, such as .dup which will then conflict with allowed
+ * field names.
+ * One way to fix it is to replace the following foreach and .tupleof with C
+ * statements and expressions.
+ * But, it's debatable whether C structs should even need toHash().
+ * Note that it would only be necessary if it has floating point fields.
+ * For now, we'll just not generate a toHash() for C files.
+ */
+ if (sc.flags & SCOPE.Cfile)
+ return null;
+
//printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
Loc loc; // internal code should have no loc to prevent coverage
@@ -831,31 +844,31 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
}
/*****************************************
- * Create inclusive destructor for struct/class by aggregating
- * all the destructors in dtors[] with the destructors for
+ * Create aggregate destructor for struct/class by aggregating
+ * all the destructors in userDtors[] with the destructors for
* all the members.
+ * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
* Params:
* ad = struct or class to build destructor for
* sc = context
- * Returns:
- * generated function, null if none needed
* Note:
* Close similarity with StructDeclaration::buildPostBlit(),
* and the ordering changes (runs backward instead of forwards).
*/
-DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
+void buildDtors(AggregateDeclaration ad, Scope* sc)
{
//printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
if (ad.isUnionDeclaration())
- return null; // unions don't have destructors
+ return; // unions don't have destructors
StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
- Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc;
+ Loc declLoc = ad.userDtors.dim ? ad.userDtors[0].loc : ad.loc;
Loc loc; // internal code should have no loc to prevent coverage
FuncDeclaration xdtor_fwd = null;
- // if the dtor is an extern(C++) prototype, then we expect it performs a full-destruction; we don't need to build a full-dtor
- const bool dtorIsCppPrototype = ad.dtors.dim == 1 && ad.dtors[0].linkage == LINK.cpp && !ad.dtors[0].fbody;
+ // Build the field destructor (`ad.fieldDtor`), if needed.
+ // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
+ const bool dtorIsCppPrototype = ad.userDtors.dim && ad.userDtors[0].linkage == LINK.cpp && !ad.userDtors[0].fbody;
if (!dtorIsCppPrototype)
{
Expression e = null;
@@ -936,36 +949,6 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
e = Expression.combine(ex, e); // combine in reverse order
}
- /* extern(C++) destructors call into super to destruct the full hierarchy
- */
- ClassDeclaration cldec = ad.isClassDeclaration();
- if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.primaryDtor)
- {
- // WAIT BUT: do I need to run `cldec.baseClass.dtor` semantic? would it have been run before?
- cldec.baseClass.dtor.functionSemantic();
-
- stc = mergeFuncAttrs(stc, cldec.baseClass.primaryDtor);
- if (!(stc & STC.disable))
- {
- // super.__xdtor()
-
- Expression ex = new SuperExp(loc);
-
- // This is a hack so we can call destructors on const/immutable objects.
- // Do it as a type 'paint'.
- ex = new CastExp(loc, ex, cldec.baseClass.type.mutableOf());
- if (stc & STC.safe)
- stc = (stc & ~STC.safe) | STC.trusted;
-
- ex = new DotVarExp(loc, ex, cldec.baseClass.primaryDtor, false);
- ex = new CallExp(loc, ex);
-
- e = Expression.combine(e, ex); // super dtor last
- }
- }
-
- /* Build our own "destructor" which executes e
- */
if (e || (stc & STC.disable))
{
//printf("Building __fieldDtor(), %s\n", e.toChars());
@@ -973,29 +956,45 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
dd.generated = true;
dd.storage_class |= STC.inference;
dd.fbody = new ExpStatement(loc, e);
- ad.dtors.shift(dd);
ad.members.push(dd);
dd.dsymbolSemantic(sc);
ad.fieldDtor = dd;
}
}
- DtorDeclaration xdtor = null;
- switch (ad.dtors.dim)
+ // Generate list of dtors to call in that order
+ DtorDeclarations dtors;
+ foreach_reverse (userDtor; ad.userDtors[])
+ dtors.push(userDtor);
+ if (ad.fieldDtor)
+ dtors.push(ad.fieldDtor);
+ if (!dtorIsCppPrototype)
+ {
+ // extern(C++) destructors call into super to destruct the full hierarchy
+ ClassDeclaration cldec = ad.isClassDeclaration();
+ if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor)
+ dtors.push(cldec.baseClass.aggrDtor);
+ }
+
+ // Set/build `ad.aggrDtor`
+ switch (dtors.dim)
{
case 0:
break;
case 1:
- xdtor = ad.dtors[0];
+ // Use the single existing dtor directly as aggregate dtor.
+ // Note that this might be `cldec.baseClass.aggrDtor`.
+ ad.aggrDtor = dtors[0];
break;
default:
+ // Build the aggregate destructor, calling all dtors in order.
assert(!dtorIsCppPrototype);
Expression e = null;
e = null;
stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
- foreach (FuncDeclaration fd; ad.dtors)
+ foreach (FuncDeclaration fd; dtors)
{
stc = mergeFuncAttrs(stc, fd);
if (stc & STC.disable)
@@ -1005,8 +1004,9 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
}
Expression ex = new ThisExp(loc);
ex = new DotVarExp(loc, ex, fd, false);
- ex = new CallExp(loc, ex);
- e = Expression.combine(ex, e);
+ CallExp ce = new CallExp(loc, ex);
+ ce.directcall = true;
+ e = Expression.combine(e, ce);
}
auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor);
dd.generated = true;
@@ -1014,19 +1014,20 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
dd.fbody = new ExpStatement(loc, e);
ad.members.push(dd);
dd.dsymbolSemantic(sc);
- xdtor = dd;
+ ad.aggrDtor = dd;
break;
}
- ad.primaryDtor = xdtor;
-
- if (xdtor && xdtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
- xdtor = buildWindowsCppDtor(ad, xdtor, sc);
+ // Set/build `ad.dtor`.
+ // On Windows, the dtor in the vtable is a shim with different signature.
+ ad.dtor = (ad.aggrDtor && ad.aggrDtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
+ ? buildWindowsCppDtor(ad, ad.aggrDtor, sc)
+ : ad.aggrDtor;
- // Add an __xdtor alias to make the inclusive dtor accessible
- if (xdtor)
+ // Add an __xdtor alias to make `ad.dtor` accessible
+ if (ad.dtor)
{
- auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor);
+ auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor);
_alias.dsymbolSemantic(sc);
ad.members.push(_alias);
if (xdtor_fwd)
@@ -1035,7 +1036,8 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
_alias.addMember(sc, ad); // add to symbol table
}
- return xdtor;
+ // Set/build `ad.tidtor`
+ ad.tidtor = buildExternDDtor(ad, sc);
}
/**
@@ -1069,17 +1071,16 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara
auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, dtor.storage_class);
auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.cppdtor);
func.type = ftype;
- if (dtor.fbody)
- {
- const loc = dtor.loc;
- auto stmts = new Statements;
- auto call = new CallExp(loc, dtor, null);
- call.directcall = true;
- stmts.push(new ExpStatement(loc, call));
- stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
- func.fbody = new CompoundStatement(loc, stmts);
- func.generated = true;
- }
+
+ // Always generate the function with body, because it is not exported from DLLs.
+ const loc = dtor.loc;
+ auto stmts = new Statements;
+ auto call = new CallExp(loc, dtor, null);
+ call.directcall = true;
+ stmts.push(new ExpStatement(loc, call));
+ stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr)));
+ func.fbody = new CompoundStatement(loc, stmts);
+ func.generated = true;
auto sc2 = sc.push();
sc2.stc &= ~STC.static_; // not a static destructor
@@ -1094,7 +1095,7 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara
}
/**
- * build a shim function around the compound dtor that translates
+ * build a shim function around the aggregate dtor that translates
* a C++ destructor to a destructor with extern(D) calling convention
*
* Params:
@@ -1104,9 +1105,9 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara
* Returns:
* the shim destructor, semantically analyzed and added to the class as a member
*/
-DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
+private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc)
{
- auto dtor = ad.primaryDtor;
+ auto dtor = ad.aggrDtor;
if (!dtor)
return null;
new file mode 100644
@@ -0,0 +1,7 @@
+# Table of contents
+
+| File | Purpose |
+|------------------------------------------------------------------------------------|-----------------------------------------------------------------|
+| [file.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d) | Functions and objects dedicated to file I/O and management |
+| [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/outbuffer.d) | An expandable buffer in which you can write text or binary data |
+| [string.d](https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d) | Common string functions including filename manipulation |
new file mode 100644
@@ -0,0 +1,576 @@
+/**
+ * File utilities.
+ *
+ * Functions and objects dedicated to file I/O and management. TODO: Move here artifacts
+ * from places such as root/ so both the frontend and the backend have access to them.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d, common/_file.d)
+ * Documentation: https://dlang.org/phobos/dmd_common_file.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/file.d
+ */
+
+module dmd.common.file;
+
+import core.stdc.errno : errno;
+import core.stdc.stdio : fprintf, remove, rename, stderr;
+import core.stdc.stdlib : exit;
+import core.stdc.string : strerror;
+import core.sys.windows.winbase;
+import core.sys.windows.winnt;
+import core.sys.posix.fcntl;
+import core.sys.posix.unistd;
+
+import dmd.common.string;
+
+/**
+Encapsulated management of a memory-mapped file.
+
+Params:
+Datum = the mapped data type: Use a POD of size 1 for read/write mapping
+and a `const` version thereof for read-only mapping. Other primitive types
+should work, but have not been yet tested.
+*/
+struct FileMapping(Datum)
+{
+ static assert(__traits(isPOD, Datum) && Datum.sizeof == 1,
+ "Not tested with other data types yet. Add new types with care.");
+
+ version(Posix) enum invalidHandle = -1;
+ else version(Windows) enum invalidHandle = INVALID_HANDLE_VALUE;
+
+ // state {
+ /// Handle of underlying file
+ private auto handle = invalidHandle;
+ /// File mapping object needed on Windows
+ version(Windows) private HANDLE fileMappingObject = invalidHandle;
+ /// Memory-mapped array
+ private Datum[] data;
+ /// Name of underlying file, zero-terminated
+ private const(char)* name;
+ // state }
+
+ /**
+ Open `filename` and map it in memory. If `Datum` is `const`, opens for
+ read-only and maps the content in memory; no error is issued if the file
+ does not exist. This makes it easy to treat a non-existing file as empty.
+
+ If `Datum` is mutable, opens for read/write (creates file if it does not
+ exist) and fails fatally on any error.
+
+ Due to quirks in `mmap`, if the file is empty, `handle` is valid but `data`
+ is `null`. This state is valid and accounted for.
+
+ Params:
+ filename = the name of the file to be mapped in memory
+ */
+ this(const char* filename)
+ {
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman;
+ import core.sys.posix.fcntl : open, O_CREAT, O_RDONLY, O_RDWR, S_IRGRP, S_IROTH, S_IRUSR, S_IWUSR;
+
+ handle = open(filename, is(Datum == const) ? O_RDONLY : (O_CREAT | O_RDWR),
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ if (handle == invalidHandle)
+ {
+ static if (is(Datum == const))
+ {
+ // No error, nonexisting file in read mode behaves like an empty file.
+ return;
+ }
+ else
+ {
+ fprintf(stderr, "open(\"%s\") failed: %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ }
+
+ const size = fileSize(handle);
+
+ if (size > 0 && size != ulong.max && size <= size_t.max)
+ {
+ auto p = mmap(null, cast(size_t) size, is(Datum == const) ? PROT_READ : PROT_WRITE, MAP_SHARED, handle, 0);
+ if (p == MAP_FAILED)
+ {
+ fprintf(stderr, "mmap(null, %zu) for \"%s\" failed: %s\n", cast(size_t) size, filename, strerror(errno));
+ exit(1);
+ }
+ // The cast below will always work because it's gated by the `size <= size_t.max` condition.
+ data = cast(Datum[]) p[0 .. cast(size_t) size];
+ }
+ }
+ else version(Windows)
+ {
+ static if (is(Datum == const))
+ {
+ enum createFileMode = GENERIC_READ;
+ enum openFlags = OPEN_EXISTING;
+ }
+ else
+ {
+ enum createFileMode = GENERIC_READ | GENERIC_WRITE;
+ enum openFlags = CREATE_ALWAYS;
+ }
+
+ handle = filename.asDString.extendedPathThen!(p => CreateFileW(p.ptr, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null));
+ if (handle == invalidHandle)
+ {
+ static if (is(Datum == const))
+ {
+ return;
+ }
+ else
+ {
+ fprintf(stderr, "CreateFileW() failed for \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ }
+ createMapping(filename, fileSize(handle));
+ }
+ else static assert(0);
+
+ // Save the name for later. Technically there's no need: on Linux one can use readlink on /proc/self/fd/NNN.
+ // On BSD and OSX one can use fcntl with F_GETPATH. On Windows one can use GetFileInformationByHandleEx.
+ // But just saving the name is simplest, fastest, and most portable...
+ import core.stdc.string : strlen;
+ import core.stdc.stdlib : malloc;
+ import core.stdc.string : memcpy;
+ auto totalNameLength = filename.strlen() + 1;
+ name = cast(char*) memcpy(malloc(totalNameLength), filename, totalNameLength);
+ name || assert(0, "FileMapping: Out of memory.");
+ }
+
+ /**
+ Common code factored opportunistically. Windows only. Assumes `handle` is
+ already pointing to an opened file. Initializes the `fileMappingObject`
+ and `data` members.
+
+ Params:
+ filename = the file to be mapped
+ size = the size of the file in bytes
+ */
+ version(Windows) private void createMapping(const char* filename, ulong size)
+ {
+ assert(size <= size_t.max || size == ulong.max);
+ assert(handle != invalidHandle);
+ assert(data is null);
+ assert(fileMappingObject == invalidHandle);
+
+ if (size == 0 || size == ulong.max)
+ return;
+
+ static if (is(Datum == const))
+ {
+ enum fileMappingFlags = PAGE_READONLY;
+ enum mapViewFlags = FILE_MAP_READ;
+ }
+ else
+ {
+ enum fileMappingFlags = PAGE_READWRITE;
+ enum mapViewFlags = FILE_MAP_WRITE;
+ }
+
+ fileMappingObject = CreateFileMappingW(handle, null, fileMappingFlags, 0, 0, null);
+ if (!fileMappingObject)
+ {
+ fprintf(stderr, "CreateFileMappingW(%p) failed for %llu bytes of \"%s\": %d\n",
+ handle, size, filename, GetLastError());
+ fileMappingObject = invalidHandle; // by convention always use invalidHandle, not null
+ exit(1);
+ }
+ auto p = MapViewOfFile(fileMappingObject, mapViewFlags, 0, 0, 0);
+ if (!p)
+ {
+ fprintf(stderr, "MapViewOfFile() failed for \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ data = cast(Datum[]) p[0 .. cast(size_t) size];
+ }
+
+ // Not copyable or assignable (for now).
+ @disable this(const FileMapping!Datum rhs);
+ @disable void opAssign(const ref FileMapping!Datum rhs);
+
+ /**
+ Frees resources associated with this mapping. However, it does not deallocate the name.
+ */
+ ~this() pure nothrow
+ {
+ if (!active)
+ return;
+ fakePure({
+ version (Posix)
+ {
+ import core.sys.posix.sys.mman : munmap;
+ import core.sys.posix.unistd : close;
+
+ // Cannot call fprintf from inside a destructor, so exiting silently.
+
+ if (data.ptr && munmap(cast(void*) data.ptr, data.length) != 0)
+ {
+ exit(1);
+ }
+ data = null;
+ if (handle != invalidHandle && close(handle) != 0)
+ {
+ exit(1);
+ }
+ handle = invalidHandle;
+ }
+ else version(Windows)
+ {
+ if (data.ptr !is null && UnmapViewOfFile(cast(void*) data.ptr) == 0)
+ {
+ exit(1);
+ }
+ data = null;
+ if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
+ {
+ exit(1);
+ }
+ fileMappingObject = invalidHandle;
+ if (handle != invalidHandle && CloseHandle(handle) == 0)
+ {
+ exit(1);
+ }
+ handle = invalidHandle;
+ }
+ else static assert(0);
+ });
+ }
+
+ /**
+ Returns the zero-terminated file name associated with the mapping. Can NOT
+ be saved beyond the lifetime of `this`.
+ */
+ private const(char)* filename() const pure @nogc @safe nothrow { return name; }
+
+ /**
+ Frees resources associated with this mapping. However, it does not deallocate the name.
+ Reinitializes `this` as a fresh object that can be reused.
+ */
+ void close()
+ {
+ __dtor();
+ handle = invalidHandle;
+ version(Windows) fileMappingObject = invalidHandle;
+ data = null;
+ name = null;
+ }
+
+ /**
+ Deletes the underlying file and frees all resources associated.
+ Reinitializes `this` as a fresh object that can be reused.
+
+ This function does not abort if the file cannot be deleted, but does print
+ a message on `stderr` and returns `false` to the caller. The underlying
+ rationale is to give the caller the option to continue execution if
+ deleting the file is not important.
+
+ Returns: `true` iff the file was successfully deleted. If the file was not
+ deleted, prints a message to `stderr` and returns `false`.
+ */
+ static if (!is(Datum == const))
+ bool discard()
+ {
+ // Truncate file to zero so unflushed buffers are not flushed unnecessarily.
+ resize(0);
+ auto deleteme = name;
+ close();
+ // In-memory resource freed, now get rid of the underlying temp file.
+ version(Posix)
+ {
+ import core.sys.posix.unistd : unlink;
+ if (unlink(deleteme) != 0)
+ {
+ fprintf(stderr, "unlink(\"%s\") failed: %s\n", filename, strerror(errno));
+ return false;
+ }
+ }
+ else version(Windows)
+ {
+ import core.sys.windows.winbase;
+ if (deleteme.asDString.extendedPathThen!(p => DeleteFileW(p.ptr)) == 0)
+ {
+ fprintf(stderr, "DeleteFileW error %d\n", GetLastError());
+ return false;
+ }
+ }
+ else static assert(0);
+ return true;
+ }
+
+ /**
+ Queries whether `this` is currently associated with a file.
+
+ Returns: `true` iff there is an active mapping.
+ */
+ bool active() const pure @nogc nothrow
+ {
+ return handle !is invalidHandle;
+ }
+
+ /**
+ Queries the length of the file associated with this mapping. If not
+ active, returns 0.
+
+ Returns: the length of the file, or 0 if no file associated.
+ */
+ size_t length() const pure @nogc @safe nothrow { return data.length; }
+
+ /**
+ Get a slice to the contents of the entire file.
+
+ Returns: the contents of the file. If not active, returns the `null` slice.
+ */
+ auto opSlice() pure @nogc @safe nothrow { return data; }
+
+ /**
+ Resizes the file and mapping to the specified `size`.
+
+ Params:
+ size = new length requested
+ */
+ static if (!is(Datum == const))
+ void resize(size_t size) pure
+ {
+ assert(handle != invalidHandle);
+ fakePure({
+ version(Posix)
+ {
+ import core.sys.posix.unistd : ftruncate;
+ import core.sys.posix.sys.mman;
+
+ if (data.length)
+ {
+ assert(data.ptr, "Corrupt memory mapping");
+ // assert(0) here because it would indicate an internal error
+ munmap(cast(void*) data.ptr, data.length) == 0 || assert(0);
+ data = null;
+ }
+ if (ftruncate(handle, size) != 0)
+ {
+ fprintf(stderr, "ftruncate() failed for \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ if (size > 0)
+ {
+ auto p = mmap(null, size, PROT_WRITE, MAP_SHARED, handle, 0);
+ if (cast(ssize_t) p == -1)
+ {
+ fprintf(stderr, "mmap() failed for \"%s\": %s\n", filename, strerror(errno));
+ exit(1);
+ }
+ data = cast(Datum[]) p[0 .. size];
+ }
+ }
+ else version(Windows)
+ {
+ // Per documentation, must unmap first.
+ if (data.length > 0 && UnmapViewOfFile(cast(void*) data.ptr) == 0)
+ {
+ fprintf(stderr, "UnmapViewOfFile(%p) failed for memory mapping of \"%s\": %d\n",
+ data.ptr, filename, GetLastError());
+ exit(1);
+ }
+ data = null;
+ if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
+ {
+ fprintf(stderr, "CloseHandle() failed for memory mapping of \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ fileMappingObject = invalidHandle;
+ LARGE_INTEGER biggie;
+ biggie.QuadPart = size;
+ if (SetFilePointerEx(handle, biggie, null, FILE_BEGIN) == 0 || SetEndOfFile(handle) == 0)
+ {
+ fprintf(stderr, "SetFilePointer() failed for \"%s\": %d\n", filename, GetLastError());
+ exit(1);
+ }
+ createMapping(name, size);
+ }
+ else static assert(0);
+ });
+ }
+
+ /**
+ Unconditionally and destructively moves the underlying file to `filename`.
+ If the operation succeeds, returns true. Upon failure, prints a message to
+ `stderr` and returns `false`. In all cases it closes the underlying file.
+
+ Params: filename = zero-terminated name of the file to move to.
+
+ Returns: `true` iff the operation was successful.
+ */
+ bool moveToFile(const char* filename)
+ {
+ assert(name !is null);
+
+ // Fetch the name and then set it to `null` so it doesn't get deallocated
+ auto oldname = name;
+ import core.stdc.stdlib;
+ scope(exit) free(cast(void*) oldname);
+ name = null;
+ close();
+
+ // Rename the underlying file to the target, no copy necessary.
+ version(Posix)
+ {
+ if (.rename(oldname, filename) != 0)
+ {
+ fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", oldname, filename, strerror(errno));
+ return false;
+ }
+ }
+ else version(Windows)
+ {
+ import core.sys.windows.winbase;
+ auto r = oldname.asDString.extendedPathThen!(
+ p1 => filename.asDString.extendedPathThen!(p2 => MoveFileExW(p1.ptr, p2.ptr, MOVEFILE_REPLACE_EXISTING))
+ );
+ if (r == 0)
+ {
+ fprintf(stderr, "MoveFileExW(\"%s\", \"%s\") failed: %d\n", oldname, filename, GetLastError());
+ return false;
+ }
+ }
+ else static assert(0);
+ return true;
+ }
+}
+
+/// Write a file, returning `true` on success.
+extern(D) static bool writeFile(const(char)* name, const void[] data) nothrow
+{
+ version (Posix)
+ {
+ int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
+ if (fd == -1)
+ goto err;
+ if (.write(fd, data.ptr, data.length) != data.length)
+ goto err2;
+ if (close(fd) == -1)
+ goto err;
+ return true;
+ err2:
+ close(fd);
+ .remove(name);
+ err:
+ return false;
+ }
+ else version (Windows)
+ {
+ DWORD numwritten; // here because of the gotos
+ const nameStr = name.asDString;
+ // work around Windows file path length limitation
+ // (see documentation for extendedPathThen).
+ HANDLE h = nameStr.extendedPathThen!
+ (p => CreateFileW(p.ptr,
+ GENERIC_WRITE,
+ 0,
+ null,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
+ null));
+ if (h == INVALID_HANDLE_VALUE)
+ goto err;
+
+ if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
+ goto err2;
+ if (numwritten != data.length)
+ goto err2;
+ if (!CloseHandle(h))
+ goto err;
+ return true;
+ err2:
+ CloseHandle(h);
+ nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
+ err:
+ return false;
+ }
+ else
+ {
+ static assert(0);
+ }
+}
+
+/// Touch a file to current date
+bool touchFile(const char* namez)
+{
+ version (Windows)
+ {
+ FILETIME ft = void;
+ SYSTEMTIME st = void;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &ft);
+
+ import core.stdc.string : strlen;
+
+ // get handle to file
+ HANDLE h = namez[0 .. namez.strlen()].extendedPathThen!(p => CreateFile(p.ptr,
+ FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ null, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, null));
+ if (h == INVALID_HANDLE_VALUE)
+ return false;
+
+ const f = SetFileTime(h, null, null, &ft); // set last write time
+
+ if (!CloseHandle(h))
+ return false;
+
+ return f != 0;
+ }
+ else version (Posix)
+ {
+ import core.sys.posix.utime;
+ return utime(namez, null) == 0;
+ }
+ else
+ static assert(0);
+}
+
+// Feel free to make these public if used elsewhere.
+/**
+Size of a file in bytes.
+Params: fd = file handle
+Returns: file size in bytes, or `ulong.max` on any error.
+*/
+version (Posix)
+private ulong fileSize(int fd)
+{
+ import core.sys.posix.sys.stat;
+ stat_t buf;
+ if (fstat(fd, &buf) == 0)
+ return buf.st_size;
+ return ulong.max;
+}
+
+/// Ditto
+version (Windows)
+private ulong fileSize(HANDLE fd)
+{
+ ulong result;
+ if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0)
+ return result;
+ return ulong.max;
+}
+
+/**
+Runs a non-pure function or delegate as pure code. Use with caution.
+
+Params:
+fun = the delegate to run, usually inlined: `fakePure({ ... });`
+
+Returns: whatever `fun` returns.
+*/
+private auto ref fakePure(F)(scope F fun) pure
+{
+ mixin("alias PureFun = " ~ F.stringof ~ " pure;");
+ return (cast(PureFun) fun)();
+}
similarity index 77%
rename from gcc/d/dmd/root/outbuffer.d
rename to gcc/d/dmd/common/outbuffer.d
@@ -9,14 +9,21 @@
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/outbuffer.d
*/
-module dmd.root.outbuffer;
+module dmd.common.outbuffer;
import core.stdc.stdarg;
import core.stdc.stdio;
import core.stdc.string;
-import dmd.root.rmem;
-import dmd.root.rootobject;
-import dmd.root.string;
+import core.stdc.stdlib;
+
+// In theory these functions should also restore errno, but we don't care because
+// we abort application on error anyway.
+extern (C) private pure @system @nogc nothrow
+{
+ pragma(mangle, "malloc") void* pureMalloc(size_t);
+ pragma(mangle, "realloc") void* pureRealloc(void* ptr, size_t size);
+ pragma(mangle, "free") void pureFree(void* ptr);
+}
debug
{
@@ -29,7 +36,7 @@ a contiguous array or a memory-mapped file.
*/
struct OutBuffer
{
- import dmd.root.file : FileMapping;
+ import dmd.common.file : FileMapping, touchFile, writeFile;
// IMPORTANT: PLEASE KEEP STATE AND DESTRUCTOR IN SYNC WITH DEFINITION IN ./outbuffer.h.
// state {
@@ -47,6 +54,14 @@ struct OutBuffer
int level;
// state }
+ /**
+ Construct given size.
+ */
+ this(size_t initialSize) nothrow
+ {
+ reserve(initialSize);
+ }
+
/**
Construct from filename. Will map the file into memory (or create it anew
if necessary) and start writing at the beginning of it.
@@ -56,14 +71,36 @@ struct OutBuffer
*/
@trusted this(const(char)* filename)
{
- fileMapping = new FileMapping!ubyte(filename);
+ FileMapping!ubyte model;
+ fileMapping = cast(FileMapping!ubyte*) malloc(model.sizeof);
+ memcpy(fileMapping, &model, model.sizeof);
+ fileMapping.__ctor(filename);
+ //fileMapping = new FileMapping!ubyte(filename);
data = (*fileMapping)[];
}
+ /**
+ Frees resources associated.
+ */
+ extern (C++) void dtor() nothrow @trusted
+ {
+ if (fileMapping)
+ {
+ if (fileMapping.active)
+ fileMapping.close();
+ fileMapping = null;
+ }
+ else
+ {
+ debug (stomp) memset(data.ptr, 0xFF, data.length);
+ free(data.ptr);
+ }
+ }
+
/**
Frees resources associated automatically.
*/
- extern (C++) ~this() pure nothrow
+ extern (C++) ~this() pure nothrow @trusted
{
if (fileMapping)
{
@@ -74,10 +111,23 @@ struct OutBuffer
else
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
- mem.xfree(data.ptr);
+ pureFree(data.ptr);
}
}
+ /// For porting with ease from dmd.backend.outbuf.Outbuffer
+ ubyte* buf() nothrow {
+ return data.ptr;
+ }
+
+ /// For porting with ease from dmd.backend.outbuf.Outbuffer
+ ubyte** bufptr() nothrow {
+ static struct Array { size_t length; ubyte* ptr; }
+ auto a = cast(Array*) &data;
+ assert(a.length == data.length && a.ptr == data.ptr);
+ return &a.ptr;
+ }
+
extern (C++) size_t length() const pure @nogc @safe nothrow { return offset; }
/**********************
@@ -109,7 +159,7 @@ struct OutBuffer
else
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
- mem.xfree(extractData());
+ pureFree(extractData());
}
}
@@ -141,17 +191,18 @@ struct OutBuffer
{
debug (stomp)
{
- auto p = cast(ubyte*)mem.xmalloc(size);
+ auto p = cast(ubyte*) pureMalloc(size);
+ p || assert(0, "OutBuffer: out of memory.");
memcpy(p, data.ptr, offset);
memset(data.ptr, 0xFF, data.length); // stomp old location
- mem.xfree(data.ptr);
+ pureFree(data.ptr);
memset(p + offset, 0xff, size - offset); // stomp unused data
}
else
{
- auto p = cast(ubyte*)mem.xrealloc(data.ptr, size);
- if (mem.isGCEnabled) // clear currently unused data to avoid false pointers
- memset(p + offset + nbytes, 0xff, size - offset - nbytes);
+ auto p = cast(ubyte*) pureRealloc(data.ptr, size);
+ p || assert(0, "OutBuffer: out of memory.");
+ memset(p + offset + nbytes, 0xff, size - offset - nbytes);
}
data = p[0 .. size];
}
@@ -164,7 +215,7 @@ struct OutBuffer
*/
extern (C++) void setsize(size_t size) pure nothrow @nogc @safe
{
- assert(size <= offset);
+ assert(size <= data.length);
offset = size;
}
@@ -185,6 +236,14 @@ struct OutBuffer
notlinehead = true;
}
+ // Write an array to the buffer, no reserve check
+ @trusted nothrow
+ void writen(const void *b, size_t len)
+ {
+ memcpy(data.ptr + offset, b, len);
+ offset += len;
+ }
+
extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow
{
write(data[0 .. nbytes]);
@@ -199,27 +258,90 @@ struct OutBuffer
offset += buf.length;
}
- extern (C++) void writestring(const(char)* string) pure nothrow
+ /**
+ * Writes a 16 bit value, no reserve check.
+ */
+ @trusted nothrow
+ void write16n(int v)
{
- write(string.toDString);
+ auto x = cast(ushort) v;
+ data[offset] = x & 0x00FF;
+ data[offset + 1] = x >> 8u;
+ offset += 2;
}
+ /**
+ * Writes a 16 bit value.
+ */
+ void write16(int v) nothrow
+ {
+ auto u = cast(ushort) v;
+ write(&u, u.sizeof);
+ }
+
+ /**
+ * Writes a 32 bit int.
+ */
+ void write32(int v) nothrow @trusted
+ {
+ write(&v, v.sizeof);
+ }
+
+ /**
+ * Writes a 64 bit int.
+ */
+ @trusted void write64(long v) nothrow
+ {
+ write(&v, v.sizeof);
+ }
+
+ /// NOT zero-terminated
+ extern (C++) void writestring(const(char)* s) pure nothrow
+ {
+ if (!s)
+ return;
+ import core.stdc.string : strlen;
+ write(s[0 .. strlen(s)]);
+ }
+
+ /// ditto
void writestring(const(char)[] s) pure nothrow
{
write(s);
}
+ /// ditto
void writestring(string s) pure nothrow
{
write(s);
}
+ /// NOT zero-terminated, followed by newline
void writestringln(const(char)[] s) pure nothrow
{
writestring(s);
writenl();
}
+ // Zero-terminated
+ void writeString(const(char)* s) pure nothrow @trusted
+ {
+ write(s[0 .. strlen(s)+1]);
+ }
+
+ /// ditto
+ void writeString(const(char)[] s) pure nothrow
+ {
+ write(s);
+ writeByte(0);
+ }
+
+ /// ditto
+ void writeString(string s) pure nothrow
+ {
+ writeString(cast(const(char)[])(s));
+ }
+
extern (C++) void prependstring(const(char)* string) pure nothrow
{
size_t len = strlen(string);
@@ -244,6 +366,38 @@ struct OutBuffer
notlinehead = false;
}
+ // Write n zeros; return pointer to start of zeros
+ @trusted
+ void *writezeros(size_t n) nothrow
+ {
+ reserve(n);
+ auto result = memset(data.ptr + offset, 0, n);
+ offset += n;
+ return result;
+ }
+
+ // Position buffer to accept the specified number of bytes at offset
+ @trusted
+ void position(size_t where, size_t nbytes) nothrow
+ {
+ if (where + nbytes > data.length)
+ {
+ reserve(where + nbytes - offset);
+ }
+ offset = where;
+
+ debug assert(offset + nbytes <= data.length);
+ }
+
+ /**
+ * Writes an 8 bit byte, no reserve check.
+ */
+ extern (C++) @trusted nothrow
+ void writeByten(int b)
+ {
+ this.data[offset++] = cast(ubyte) b;
+ }
+
extern (C++) void writeByte(uint b) pure nothrow
{
if (doindent && !notlinehead && b != '\n')
@@ -369,14 +523,6 @@ struct OutBuffer
}
}
- extern (C++) void write(RootObject obj) /*nothrow*/
- {
- if (obj)
- {
- writestring(obj.toChars());
- }
- }
-
extern (C++) void fill0(size_t nbytes) pure nothrow
{
reserve(nbytes);
@@ -428,8 +574,8 @@ struct OutBuffer
break;
}
offset += count;
- if (mem.isGCEnabled)
- memset(data.ptr + offset, 0xff, psize - count);
+ // if (mem.isGCEnabled)
+ memset(data.ptr + offset, 0xff, psize - count);
}
static if (__VERSION__ < 2092)
@@ -460,7 +606,6 @@ struct OutBuffer
*/
extern (C++) void print(ulong u) pure nothrow
{
- //import core.internal.string; // not available
UnsignedStringBuf buf = void;
writestring(unsignedToTempString(u, buf));
}
@@ -558,6 +703,11 @@ struct OutBuffer
return extractData()[0 .. length];
}
+ extern (D) byte[] extractUbyteSlice(bool nullTerminate = false) pure nothrow
+ {
+ return cast(byte[]) extractSlice(nullTerminate);
+ }
+
// Append terminating null if necessary and get view of internal buffer
extern (C++) char* peekChars() pure nothrow
{
@@ -577,6 +727,36 @@ struct OutBuffer
return extractData();
}
+ void writesLEB128(int value) pure nothrow
+ {
+ while (1)
+ {
+ ubyte b = value & 0x7F;
+
+ value >>= 7; // arithmetic right shift
+ if ((value == 0 && !(b & 0x40)) ||
+ (value == -1 && (b & 0x40)))
+ {
+ writeByte(b);
+ break;
+ }
+ writeByte(b | 0x80);
+ }
+ }
+
+ void writeuLEB128(uint value) pure nothrow
+ {
+ do
+ {
+ ubyte b = value & 0x7F;
+
+ value >>= 7;
+ if (value)
+ b |= 0x80;
+ writeByte(b);
+ } while (value);
+ }
+
/**
Destructively saves the contents of `this` to `filename`. As an
optimization, if the file already has identical contents with the buffer,
@@ -591,7 +771,6 @@ struct OutBuffer
*/
extern(D) bool moveToFile(const char* filename)
{
- import dmd.root.file;
bool result = true;
const bool identical = this[] == FileMapping!(const ubyte)(filename)[];
@@ -615,12 +794,12 @@ struct OutBuffer
else
{
if (!identical)
- File.write(filename, this[]);
+ writeFile(filename, this[]);
destroy();
}
return identical
- ? result && File.touch(filename)
+ ? result && touchFile(filename)
: result;
}
}
@@ -645,7 +824,7 @@ char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe pure
else
{
ubyte x = cast(ubyte)(value % radix);
- value = value / radix;
+ value /= radix;
buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a');
}
} while (value);
similarity index 90%
rename from gcc/d/dmd/root/outbuffer.h
rename to gcc/d/dmd/common/outbuffer.h
@@ -4,14 +4,14 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/common/outbuffer.h
*/
#pragma once
-#include "dsystem.h"
-#include "dcompat.h"
-#include "rmem.h"
+#include "../root/dsystem.h"
+#include "../root/dcompat.h"
+#include "../root/rmem.h"
class RootObject;
@@ -22,7 +22,7 @@ private:
DArray<unsigned char> data;
d_size_t offset;
bool notlinehead;
- void* fileMapping; // pointer to a file mapping object not used on the C++ side
+ void *fileMapping; // pointer to a file mapping object not used on the C++ side
public:
bool doindent;
bool spaces;
new file mode 100644
@@ -0,0 +1,209 @@
+/**
+ * Common string functions including filename manipulation.
+ *
+ * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
+ * Authors: Walter Bright, http://www.digitalmars.com
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/string.d, common/_string.d)
+ * Documentation: https://dlang.org/phobos/dmd_common_string.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/string.d
+ */
+module dmd.common.string;
+
+/**
+Defines a temporary array using a fixed-length buffer as back store. If the length
+of the buffer suffices, it is readily used. Otherwise, `malloc` is used to
+allocate memory for the array and `free` is used for deallocation in the
+destructor.
+
+This type is meant to use exclusively as an automatic variable. It is not
+default constructible or copyable.
+*/
+struct SmallBuffer(T)
+{
+ import core.stdc.stdlib : malloc, free;
+
+ private T[] _extent;
+ private bool needsFree;
+
+ @disable this(); // no default ctor
+ @disable this(ref const SmallBuffer!T); // noncopyable, nonassignable
+
+ this(size_t len, T[] buffer)
+ {
+ if (len <= buffer.length)
+ {
+ _extent = buffer[0 .. len];
+ }
+ else
+ {
+ _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len];
+ _extent.ptr || assert(0, "Out of memory.");
+ needsFree = true;
+ }
+ assert(this.length == len);
+ }
+
+ ~this()
+ {
+ if (needsFree)
+ free(_extent.ptr);
+ }
+
+ void create(size_t len)
+ {
+ if (len <= _extent.length)
+ {
+ _extent = _extent[0 .. len];
+ }
+ else
+ {
+ __dtor();
+ _extent = (cast(typeof(_extent.ptr)) malloc(len * _extent[0].sizeof))[0 .. len];
+ _extent.ptr || assert(0, "Out of memory.");
+ needsFree = true;
+ }
+ assert(this.length == len);
+ }
+
+ // Force accesses to extent to be scoped.
+ scope inout extent()
+ {
+ return _extent;
+ }
+
+ alias extent this;
+}
+
+/// ditto
+unittest
+{
+ char[230] buf = void;
+ auto a = SmallBuffer!char(10, buf);
+ assert(a[] is buf[0 .. 10]);
+ auto b = SmallBuffer!char(1000, buf);
+ assert(b[] !is buf[]);
+ b.create(1000);
+ assert(b.length == 1000);
+ assert(b[] !is buf[]);
+}
+
+/**
+Converts a zero-terminated C string to a D slice. Takes linear time and allocates no memory.
+
+Params:
+stringz = the C string to be converted
+
+Returns:
+a slice comprehending the string. The terminating 0 is not part of the slice.
+*/
+auto asDString(C)(C* stringz) pure @nogc nothrow
+{
+ import core.stdc.string : strlen;
+ return stringz[0 .. strlen(stringz)];
+}
+
+///
+unittest
+{
+ const char* p = "123".ptr;
+ assert(p.asDString == "123");
+}
+
+/**
+(Windows only) Converts a narrow string to a wide string using `buffer` as strorage. Returns a slice managed by
+`buffer` containing the converted string. The terminating zero is not part of the returned slice,
+but is guaranteed to follow it.
+*/
+version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow
+{
+ import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar;
+ // assume filenames encoded in system default Windows ANSI code page
+ enum CodePage = CP_ACP;
+
+ if (narrow is null)
+ return null;
+
+ const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
+ if (requiredLength < cast(int) buffer.length)
+ {
+ buffer[requiredLength] = 0;
+ return buffer[0 .. requiredLength];
+ }
+
+ buffer.create(requiredLength + 1);
+ const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, requiredLength);
+ assert(length == requiredLength);
+ buffer[length] = 0;
+ return buffer[0 .. length];
+}
+
+/**************************************
+* Converts a path to one suitable to be passed to Win32 API
+* functions that can deal with paths longer than 248
+* characters then calls the supplied function on it.
+*
+* Params:
+* path = The Path to call F on.
+*
+* Returns:
+* The result of calling F on path.
+*
+* References:
+* https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+*/
+version(Windows) auto extendedPathThen(alias F)(const(char)[] path)
+{
+ import core.sys.windows.winbase;
+ import core.sys.windows.winnt;
+
+ if (!path.length)
+ return F((wchar[]).init);
+
+ wchar[1024] buf = void;
+ auto store = SmallBuffer!wchar(buf.length, buf);
+ auto wpath = toWStringz(path, store);
+
+ // GetFullPathNameW expects a sized buffer to store the result in. Since we don't
+ // know how large it has to be, we pass in null and get the needed buffer length
+ // as the return code.
+ const pathLength = GetFullPathNameW(&wpath[0],
+ 0 /*length8*/,
+ null /*output buffer*/,
+ null /*filePartBuffer*/);
+ if (pathLength == 0)
+ {
+ return F((wchar[]).init);
+ }
+
+ // wpath is the UTF16 version of path, but to be able to use
+ // extended paths, we need to prefix with `\\?\` and the absolute
+ // path.
+ static immutable prefix = `\\?\`w;
+
+ // prefix only needed for long names and non-UNC names
+ const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\');
+ const prefixLength = needsPrefix ? prefix.length : 0;
+
+ // +1 for the null terminator
+ const bufferLength = pathLength + prefixLength + 1;
+
+ wchar[1024] absBuf = void;
+ auto absPath = SmallBuffer!wchar(bufferLength, absBuf);
+
+ absPath[0 .. prefixLength] = prefix[0 .. prefixLength];
+
+ const absPathRet = GetFullPathNameW(&wpath[0],
+ cast(uint)(absPath.length - prefixLength - 1),
+ &absPath[prefixLength],
+ null /*filePartBuffer*/);
+
+ if (absPathRet == 0 || absPathRet > absPath.length - prefixLength)
+ {
+ return F((wchar[]).init);
+ }
+
+ absPath[$ - 1] = '\0';
+ // Strip null terminator from the slice
+ return F(absPath[0 .. $ - 1]);
+}
@@ -28,7 +28,7 @@ import dmd.globals;
import dmd.identifier;
import dmd.mtype;
import dmd.typesem;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.tokens;
@@ -452,7 +452,6 @@ extern (C++) final class StaticForeach : RootObject
sc = sc.startCTFE();
aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
sc = sc.endCTFE();
- aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
}
if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror)
@@ -23,7 +23,7 @@ import dmd.lexer;
import dmd.parse;
import dmd.errors;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.root.string;
@@ -72,6 +72,7 @@ final class CParser(AST) : Parser!AST
{
//printf("cparseTranslationUnit()\n");
symbols = new AST.Dsymbols();
+ addBuiltinDeclarations();
while (1)
{
if (token.value == TOK.endOfFile)
@@ -756,7 +757,6 @@ final class CParser(AST) : Parser!AST
switch (token.value)
{
case TOK.dot:
- case TOK.arrow:
nextToken();
if (token.value == TOK.identifier)
{
@@ -767,6 +767,19 @@ final class CParser(AST) : Parser!AST
error("identifier expected following `.`, not `%s`", token.toChars());
break;
+ case TOK.arrow:
+ nextToken();
+ if (token.value == TOK.identifier)
+ {
+ Identifier id = token.ident;
+ auto die = new AST.DotIdExp(loc, e, id);
+ die.arrow = true;
+ e = die;
+ break;
+ }
+ error("identifier expected following `->`, not `%s`", token.toChars());
+ break;
+
case TOK.plusPlus:
e = new AST.PostExp(TOK.plusPlus, loc, e);
break;
@@ -949,6 +962,7 @@ final class CParser(AST) : Parser!AST
nextToken();
auto t = cparseTypeName();
check(TOK.rightParenthesis);
+ pt = &token;
if (token.value == TOK.leftCurly)
{
@@ -957,6 +971,17 @@ final class CParser(AST) : Parser!AST
auto ce = new AST.CompoundLiteralExp(loc, t, ci);
return cparsePostfixOperators(ce);
}
+ else if (t.isTypeIdentifier() &&
+ token.value == TOK.leftParenthesis &&
+ !isCastExpression(pt))
+ {
+ /* this might actually be a function
+ * call that looks like `(a)(b)` or even `(a)(b,c)`
+ */
+ auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
+ ie.parens = true; // disambiguate it from being a declaration
+ return new AST.CallExp(loc, ie, cparseArguments());
+ }
else
{
// ( type-name ) cast-expression
@@ -1451,6 +1476,7 @@ final class CParser(AST) : Parser!AST
auto symbolsSave = symbols;
Specifier specifier;
+ specifier.packalign = this.packalign;
auto tspec = cparseDeclarationSpecifiers(level, specifier);
/* If a declarator does not follow, it is unnamed
@@ -1459,7 +1485,8 @@ final class CParser(AST) : Parser!AST
{
nextToken();
auto tt = tspec.isTypeTag();
- if (!tt || !tt.id)
+ if (!tt ||
+ !tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
return; // legal but meaningless empty declaration, ignore it
/* `struct tag;` and `struct tag { ... };`
@@ -1493,7 +1520,7 @@ final class CParser(AST) : Parser!AST
{
Identifier id;
AST.Expression asmname;
- auto dt = cparseDeclarator(DTR.xdirect, tspec, id);
+ auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
if (!dt)
{
panic();
@@ -1674,6 +1701,8 @@ final class CParser(AST) : Parser!AST
return;
case TOK.comma:
+ if (!symbolsSave)
+ symbolsSave = symbols;
nextToken();
break;
@@ -1720,8 +1749,9 @@ final class CParser(AST) : Parser!AST
*/
auto pl = ft.parameterList;
pl.hasIdentifierList = true; // semantic needs to know to adjust parameter types
- if (pl.varargs != AST.VarArg.none)
+ if (pl.varargs != AST.VarArg.none && pl.length)
error("function identifier-list cannot end with `...`");
+ ft.parameterList.varargs = AST.VarArg.variadic; // but C11 allows extra arguments
auto plLength = pl.length;
if (symbols.length != plLength)
error("%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
@@ -1756,7 +1786,10 @@ final class CParser(AST) : Parser!AST
}
}
if (!p.type)
+ {
error("no declaration for identifier `%s`", p.ident.toChars());
+ p.type = AST.Type.terror;
+ }
}
}
@@ -2240,14 +2273,14 @@ final class CParser(AST) : Parser!AST
* declarator = declarator kind
* t = base type to start with
* pident = set to Identifier if there is one, null if not
- * storageClass = any storage classes seen so far that apply to a function
+ * specifier = specifiers in and out
* Returns:
* type declared. If a TypeFunction is returned, this.symbols is the
* symbol table for the parameter-type-list, which will contain any
* declared struct, union or enum tags.
*/
private AST.Type cparseDeclarator(DTR declarator, AST.Type t,
- out Identifier pident, StorageClass storageClass = 0)
+ out Identifier pident, ref Specifier specifier)
{
//printf("cparseDeclarator(%d)\n", declarator);
AST.Types constTypes; // all the Types that will need `const` applied to them
@@ -2285,6 +2318,8 @@ final class CParser(AST) : Parser!AST
const mod = cparseTypeQualifierList();
if (mod & MOD.xconst)
constTypes.push(t);
+ if (token.value == TOK.__attribute__)
+ cparseGnuAttributes(specifier);
continue;
default:
@@ -2352,8 +2387,9 @@ final class CParser(AST) : Parser!AST
}
else
{
- // An array of unknown size, fake it with a DArray
- ta = new AST.TypeDArray(t); // []
+ /* C11 6.7.6.2-4 An [ ] array is an incomplete array type
+ */
+ ta = new AST.TypeSArray(t);
}
check(TOK.rightBracket);
@@ -2388,7 +2424,7 @@ final class CParser(AST) : Parser!AST
/* C11 6.7.6.2-1: the element type shall not be an incomplete or
* function type.
*/
- if (ta.isTypeDArray() && !isVLA)
+ if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
error("array type has incomplete element type `%s`", ta.toChars());
}
@@ -2489,9 +2525,10 @@ final class CParser(AST) : Parser!AST
AST.Type cparseTypeName()
{
Specifier specifier;
+ specifier.packalign.setDefault();
auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
Identifier id;
- return cparseDeclarator(DTR.xabstract, tspec, id);
+ return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
}
/***********************************
@@ -2525,13 +2562,19 @@ final class CParser(AST) : Parser!AST
StorageClass varargsStc;
check(TOK.leftParenthesis);
- if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis)
+ if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
{
nextToken();
nextToken();
return AST.ParameterList(parameters, varargs, varargsStc);
}
+ if (token.value == TOK.rightParenthesis) // func()
+ {
+ nextToken();
+ return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc);
+ }
+
/* The check for identifier-list comes later,
* when doing the trailing declaration-list (opt)
*/
@@ -2541,6 +2584,8 @@ final class CParser(AST) : Parser!AST
break;
if (token.value == TOK.dotDotDot)
{
+ if (parameters.length == 0) // func(...)
+ error("named parameter required before `...`");
varargs = AST.VarArg.variadic; // C-style variadics
nextToken();
check(TOK.rightParenthesis);
@@ -2548,10 +2593,16 @@ final class CParser(AST) : Parser!AST
}
Specifier specifier;
+ specifier.packalign.setDefault();
auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
+ if (tspec && specifier.mod & MOD.xconst)
+ {
+ tspec = toConst(tspec);
+ specifier.mod = MOD.xnone; // 'used' it
+ }
Identifier id;
- auto t = cparseDeclarator(DTR.xparameter, tspec, id);
+ auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
if (specifier.mod & MOD.xconst)
t = toConst(t);
auto param = new AST.Parameter(STC.parameter, t, id, null, null);
@@ -2920,6 +2971,7 @@ final class CParser(AST) : Parser!AST
* enum gnu-attributes (opt) identifier
*/
Specifier specifier;
+ specifier.packalign.setDefault();
if (token.value == TOK.__attribute__)
cparseGnuAttributes(specifier);
@@ -2950,6 +3002,16 @@ final class CParser(AST) : Parser!AST
nextToken();
auto mloc = token.loc;
+ if (token.value == TOK.__attribute__)
+ {
+ /* gnu-attributes can appear here, but just scan and ignore them
+ * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
+ */
+ Specifier specifierx;
+ specifierx.packalign.setDefault();
+ cparseGnuAttributes(specifierx);
+ }
+
AST.Expression value;
if (token.value == TOK.assign)
{
@@ -2958,6 +3020,16 @@ final class CParser(AST) : Parser!AST
// TODO C11 6.7.2.2-2 value must fit into an int
}
+ if (token.value == TOK.__attribute__)
+ {
+ /* gnu-attributes can appear here, but just scan and ignore them
+ * https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
+ */
+ Specifier specifierx;
+ specifierx.packalign.setDefault();
+ cparseGnuAttributes(specifierx);
+ }
+
auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
members.push(em);
@@ -3037,14 +3109,12 @@ final class CParser(AST) : Parser!AST
check(TOK.rightCurly);
if ((*members).length == 0) // C11 6.7.2.1-8
- /* TODO: not strict enough, should really be contains "no named members",
- * not just "no members".
- * I.e. an unnamed bit field, _Static_assert, etc, are not named members,
- * but will pass this check.
- * Be careful to detect named members that come anonymous structs.
- * Correctly doing this will likely mean moving it to typesem.d.
+ {
+ /* allow empty structs as an extension
+ * struct-declarator-list:
+ * struct-declarator (opt)
*/
- error("empty struct-declaration-list for `%s %s`", Token.toChars(structOrUnion), tag ? tag.toChars() : "Anonymous".ptr);
+ }
}
else if (!tag)
error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
@@ -3083,7 +3153,13 @@ final class CParser(AST) : Parser!AST
auto symbolsSave = symbols;
Specifier specifier;
+ specifier.packalign = this.packalign;
auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
+ if (tspec && specifier.mod & MOD.xconst)
+ {
+ tspec = toConst(tspec);
+ specifier.mod = MOD.xnone; // 'used' it
+ }
/* If a declarator does not follow, it is unnamed
*/
@@ -3139,12 +3215,14 @@ final class CParser(AST) : Parser!AST
dt = tspec;
}
else
- dt = cparseDeclarator(DTR.xdirect, tspec, id);
- if (!dt)
{
- panic();
- nextToken();
- break; // error recovery
+ dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
+ if (!dt)
+ {
+ panic();
+ nextToken();
+ break; // error recovery
+ }
}
AST.Expression width;
@@ -3155,9 +3233,6 @@ final class CParser(AST) : Parser!AST
width = cparseConstantExp();
}
- if (specifier.mod & MOD.xconst)
- dt = toConst(dt);
-
/* GNU Extensions
* struct-declarator:
* declarator gnu-attributes (opt)
@@ -3234,8 +3309,8 @@ final class CParser(AST) : Parser!AST
*/
private bool isCDeclaration(ref Token* pt)
{
- //printf("isCDeclaration()\n");
auto t = pt;
+ //printf("isCDeclaration() %s\n", t.toChars());
if (!isDeclarationSpecifiers(t))
return false;
@@ -3360,8 +3435,8 @@ final class CParser(AST) : Parser!AST
*/
private bool isAssignmentExpression(ref Token* pt)
{
- //printf("isAssignmentExpression()\n");
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
@@ -3384,6 +3459,15 @@ final class CParser(AST) : Parser!AST
case TOK.leftParenthesis:
if (!skipParens(t, &t))
return false;
+ /*
+ https://issues.dlang.org/show_bug.cgi?id=22267
+ Fix issue 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:
@@ -3391,6 +3475,11 @@ final class CParser(AST) : Parser!AST
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);
@@ -3427,6 +3516,7 @@ final class CParser(AST) : Parser!AST
auto t = pt;
+ bool seenType;
bool any;
while (1)
{
@@ -3445,9 +3535,19 @@ final class CParser(AST) : Parser!AST
case TOK._Bool:
//case TOK._Imaginary:
case TOK._Complex:
- case TOK.identifier: // typedef-name
t = peek(t);
+ seenType = true;
any = true;
+ continue;
+
+ case TOK.identifier: // typedef-name
+ if (!seenType)
+ {
+ t = peek(t);
+ seenType = true;
+ any = true;
+ continue;
+ }
break;
case TOK.struct_:
@@ -3878,6 +3978,10 @@ final class CParser(AST) : Parser!AST
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 )
@@ -4071,6 +4175,7 @@ final class CParser(AST) : Parser!AST
SCW scw; /// storage-class specifiers
MOD mod; /// type qualifiers
AST.Expressions* alignExps; /// alignment
+ structalign_t packalign; /// #pragma pack alignment value
}
/***********************
@@ -4089,19 +4194,19 @@ final class CParser(AST) : Parser!AST
if (level == LVL.global)
{
if (specifier.scw & SCW.xextern)
- stc = AST.STC.extern_;
+ stc = AST.STC.extern_;
}
else if (level == LVL.local)
{
if (specifier.scw & SCW.xextern)
- stc = AST.STC.extern_;
+ stc = AST.STC.extern_;
else if (specifier.scw & SCW.xstatic)
stc = AST.STC.static_;
}
else if (level == LVL.member)
{
if (specifier.scw & SCW.xextern)
- stc = AST.STC.extern_;
+ stc = AST.STC.extern_;
else if (specifier.scw & SCW.xstatic)
stc = AST.STC.static_;
}
@@ -4111,21 +4216,23 @@ final class CParser(AST) : Parser!AST
if (level == LVL.global)
{
if (specifier.scw & SCW.xextern)
- stc = AST.STC.extern_ | AST.STC.gshared;
+ stc = AST.STC.extern_ | AST.STC.gshared;
+ else if (specifier.scw & SCW.xstatic)
+ stc = AST.STC.gshared | AST.STC.static_;
else
stc = AST.STC.gshared;
}
else if (level == LVL.local)
{
if (specifier.scw & SCW.xextern)
- stc = AST.STC.extern_ | AST.STC.gshared;
+ stc = AST.STC.extern_ | AST.STC.gshared;
else if (specifier.scw & SCW.xstatic)
stc = AST.STC.gshared;
}
else if (level == LVL.member)
{
if (specifier.scw & SCW.xextern)
- stc = AST.STC.extern_ | AST.STC.gshared;
+ stc = AST.STC.extern_ | AST.STC.gshared;
else if (specifier.scw & SCW.xstatic)
stc = AST.STC.gshared;
}
@@ -4235,15 +4342,59 @@ final class CParser(AST) : Parser!AST
*/
private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
{
+ //printf("applySpecifier() %s\n", s.toChars());
if (specifier.alignExps)
{
+ //printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[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);
}
+ else if (!specifier.packalign.isDefault())
+ {
+ //printf(" applying packalign %d\n", cast(int)specifier.packalign);
+ // Wrap #pragma pack in an AlignDeclaration
+ auto decls = new AST.Dsymbols(1);
+ (*decls)[0] = s;
+ s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
+ }
return s;
}
+ /***********************************
+ * Add global target-dependent builtin declarations.
+ */
+ private void addBuiltinDeclarations()
+ {
+ void genBuiltinFunc(Identifier id, AST.VarArg va)
+ {
+ auto tva_list = new AST.TypeIdentifier(Loc.initial, Id.builtin_va_list);
+ auto parameters = new AST.Parameters();
+ parameters.push(new AST.Parameter(STC.parameter | STC.ref_, tva_list, null, null, null));
+ auto pl = AST.ParameterList(parameters, va, 0);
+ auto tf = new AST.TypeFunction(pl, AST.Type.tvoid, LINK.c, 0);
+ auto s = new AST.FuncDeclaration(Loc.initial, Loc.initial, id, AST.STC.static_, tf, false);
+ symbols.push(s);
+ }
+
+ /* void __builtin_va_start(__builtin_va_list, ...);
+ * The second argument is supposed to be of any type, so fake it with the ...
+ */
+ genBuiltinFunc(Id.builtin_va_start, AST.VarArg.variadic);
+
+ /* void __builtin_va_end(__builtin_va_list);
+ */
+ genBuiltinFunc(Id.builtin_va_end, AST.VarArg.none);
+
+ /* struct __va_list_tag
+ * {
+ * uint, uint, void*, void*
+ * }
+ */
+ auto s = new AST.StructDeclaration(Loc.initial, Id.va_list_tag, false);
+ symbols.push(s);
+ }
+
//}
}
@@ -41,7 +41,7 @@ import dmd.identifier;
import dmd.mtype;
import dmd.nspace;
import dmd.root.array;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.target;
@@ -98,21 +98,20 @@ extern(C++) const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset)
}
/******************************
- * Determine if sym is the 'primary' destructor, that is,
- * the most-aggregate destructor (the one that is defined as __xdtor)
+ * Determine if sym is a full aggregate destructor.
* Params:
* sym = Dsymbol
* Returns:
- * true if sym is the primary destructor for an aggregate
+ * true if sym is an aggregate destructor
*/
-bool isPrimaryDtor(const Dsymbol sym)
+bool isAggregateDtor(const Dsymbol sym)
{
const dtor = sym.isDtorDeclaration();
if (!dtor)
return false;
const ad = dtor.isMember();
assert(ad);
- return dtor == ad.primaryDtor;
+ return dtor == ad.aggrDtor;
}
/// Context used when processing pre-semantic AST
@@ -1069,7 +1068,7 @@ private final class CppMangleVisitor : Visitor
if (auto ctor = d.isCtorDeclaration())
buf.writestring(ctor.isCpCtor ? "C2" : "C1");
- else if (d.isPrimaryDtor())
+ else if (d.isAggregateDtor())
buf.writestring("D1");
else if (d.ident && d.ident == Id.assign)
buf.writestring("aS");
@@ -1184,7 +1183,7 @@ private final class CppMangleVisitor : Visitor
mangleFunctionParameters(tf.parameterList);
return;
}
- else if (d.isPrimaryDtor())
+ else if (d.isAggregateDtor())
{
buf.writestring("D1");
mangleFunctionParameters(tf.parameterList);
@@ -685,6 +685,11 @@ bool isSafePointerCast(Type srcPointee, Type destPointee)
// It's OK if both are the same (modulo const)
if (srcPointee.constConv(destPointee))
return true;
+
+ // It's ok to cast from/to shared because CTFE is single threaded anyways
+ if (srcPointee.unSharedOf() == destPointee.unSharedOf())
+ return true;
+
// It's OK if function pointers differ only in safe/pure/nothrow
if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
return srcPointee.covariant(destPointee) == Covariant.yes ||
@@ -31,12 +31,13 @@ import dmd.func;
import dmd.globals;
import dmd.impcnvtab;
import dmd.id;
+import dmd.importc;
import dmd.init;
import dmd.intrange;
import dmd.mtype;
import dmd.opover;
import dmd.root.ctfloat;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.tokens;
import dmd.typesem;
@@ -1445,6 +1446,29 @@ MATCH implicitConvTo(Expression e, Type t)
if (tb.ty == Tpointer && e.e1.op == TOK.string_)
e.e1.accept(this);
}
+
+ override void visit(TupleExp e)
+ {
+ result = e.type.implicitConvTo(t);
+ if (result != MATCH.nomatch)
+ return;
+
+ /* If target type is a tuple of same length, test conversion of
+ * each expression to the corresponding type in the tuple.
+ */
+ TypeTuple totuple = t.isTypeTuple();
+ if (totuple && e.exps.length == totuple.arguments.length)
+ {
+ result = MATCH.exact;
+ foreach (i, ex; *e.exps)
+ {
+ auto to = (*totuple.arguments)[i].type;
+ MATCH mi = ex.implicitConvTo(to);
+ if (mi < result)
+ result = mi;
+ }
+ }
+ }
}
scope ImplicitConvTo v = new ImplicitConvTo(t);
@@ -1476,12 +1500,8 @@ MATCH cimplicitConvTo(Expression e, Type t)
return MATCH.convert;
if (tb.isintegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6
return MATCH.convert;
- if (tb.ty == Tpointer && typeb.ty == Tpointer)
- {
- if (tb.isTypePointer().next.ty == Tvoid ||
- typeb.isTypePointer().next.ty == Tvoid)
- return MATCH.convert; // convert to/from void* C11 6.3.2.3-1
- }
+ if (tb.ty == Tpointer && typeb.ty == Tpointer) // C11 6.3.2.3-7
+ return MATCH.convert;
return implicitConvTo(e, t);
}
@@ -2189,13 +2209,20 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
return;
}
+ /* If target type is a tuple of same length, cast each expression to
+ * the corresponding type in the tuple.
+ */
+ TypeTuple totuple;
+ if (auto tt = t.isTypeTuple())
+ totuple = e.exps.length == tt.arguments.length ? tt : null;
+
TupleExp te = e.copy().isTupleExp();
te.e0 = e.e0 ? e.e0.copy() : null;
te.exps = e.exps.copy();
for (size_t i = 0; i < te.exps.dim; i++)
{
Expression ex = (*te.exps)[i];
- ex = ex.castTo(sc, t);
+ ex = ex.castTo(sc, totuple ? (*totuple.arguments)[i].type : t);
(*te.exps)[i] = ex;
}
result = te;
@@ -2821,6 +2848,13 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
Expression e1 = pe1;
Expression e2 = pe2;
+ // ImportC: do array/function conversions
+ if (sc)
+ {
+ e1 = e1.arrayFuncConv(sc);
+ e2 = e2.arrayFuncConv(sc);
+ }
+
Type Lret(Type result)
{
pe1 = e1;
@@ -2838,7 +2872,7 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
return result;
}
- /// Converts one of the expression too the other
+ /// Converts one of the expression to the other
Type convert(ref Expression from, Type to)
{
from = from.castTo(sc, to);
@@ -2856,6 +2890,22 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
Type t1b = e1.type.toBasetype();
Type t2b = e2.type.toBasetype();
+ if (sc && sc.flags & SCOPE.Cfile)
+ {
+ // Integral types can be implicitly converted to pointers
+ if ((t1b.ty == Tpointer) != (t2b.ty == Tpointer))
+ {
+ if (t1b.isintegral())
+ {
+ return convert(e1, t2b);
+ }
+ else if (t2b.isintegral())
+ {
+ return convert(e2, t1b);
+ }
+ }
+ }
+
if (op != TOK.question || t1b.ty != t2b.ty && (t1b.isTypeBasic() && t2b.isTypeBasic()))
{
if (op == TOK.question && t1b.ty.isSomeChar() && t2b.ty.isSomeChar())
@@ -3132,6 +3182,14 @@ Lagain:
Lcc:
while (1)
{
+ MATCH i1woat = MATCH.exact;
+ MATCH i2woat = MATCH.exact;
+
+ if (auto t2c = t2.isTypeClass())
+ i1woat = t2c.implicitConvToWithoutAliasThis(t1);
+ if (auto t1c = t1.isTypeClass())
+ i2woat = t1c.implicitConvToWithoutAliasThis(t2);
+
MATCH i1 = e2.implicitConvTo(t1);
MATCH i2 = e1.implicitConvTo(t2);
@@ -3144,11 +3202,26 @@ Lagain:
i2 = MATCH.nomatch;
}
- if (i2)
+ // Match but without 'alias this' on classes
+ if (i2 && i2woat)
return coerce(t2);
- if (i1)
+ if (i1 && i1woat)
return coerce(t1);
+ // Here use implicitCastTo() instead of castTo() to try 'alias this' on classes
+ Type coerceImplicit(Type towards)
+ {
+ e1 = e1.implicitCastTo(sc, towards);
+ e2 = e2.implicitCastTo(sc, towards);
+ return Lret(towards);
+ }
+
+ // Implicit conversion with 'alias this'
+ if (i2)
+ return coerceImplicit(t2);
+ if (i1)
+ return coerceImplicit(t1);
+
if (t1.ty == Tclass && t2.ty == Tclass)
{
TypeClass tc1 = t1.isTypeClass();
@@ -3257,29 +3330,26 @@ Lagain:
}
}
- if (t1.ty == Tstruct || t2.ty == Tstruct)
+ if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
{
- if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
- {
- if (isRecursiveAliasThis(att1, e1.type))
- return null;
- //printf("att tmerge(s || s) e1 = %s\n", e1.type.toChars());
- e1 = resolveAliasThis(sc, e1);
- t1 = e1.type;
- t = t1;
- goto Lagain;
- }
- if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
- {
- if (isRecursiveAliasThis(att2, e2.type))
- return null;
- //printf("att tmerge(s || s) e2 = %s\n", e2.type.toChars());
- e2 = resolveAliasThis(sc, e2);
- t2 = e2.type;
- t = t2;
- goto Lagain;
- }
- return null;
+ if (isRecursiveAliasThis(att1, e1.type))
+ return null;
+ //printf("att tmerge(s || s) e1 = %s\n", e1.type.toChars());
+ e1 = resolveAliasThis(sc, e1);
+ t1 = e1.type;
+ t = t1;
+ goto Lagain;
+ }
+
+ if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
+ {
+ if (isRecursiveAliasThis(att2, e2.type))
+ return null;
+ //printf("att tmerge(s || s) e2 = %s\n", e2.type.toChars());
+ e2 = resolveAliasThis(sc, e2);
+ t2 = e2.type;
+ t = t2;
+ goto Lagain;
}
if ((e1.op == TOK.string_ || e1.op == TOK.null_) && e1.implicitConvTo(t2))
@@ -20,6 +20,7 @@ import dmd.aggregate;
import dmd.apply;
import dmd.arraytypes;
import dmd.astenums;
+import dmd.attrib;
import dmd.gluelayer;
import dmd.declaration;
import dmd.dscope;
@@ -367,7 +368,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
baseok = Baseok.none;
}
- static ClassDeclaration create(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
+ static ClassDeclaration create(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
{
return new ClassDeclaration(loc, id, baseclasses, members, inObject);
}
@@ -607,7 +608,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
if (!b.sym.alignsize)
b.sym.alignsize = target.ptrsize;
- alignmember(b.sym.alignsize, b.sym.alignsize, &offset);
+ alignmember(structalign_t(cast(ushort)b.sym.alignsize), b.sym.alignsize, &offset);
assert(bi < vtblInterfaces.dim);
BaseClass* bv = (*vtblInterfaces)[bi];
@@ -725,6 +726,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
void searchVtbl(ref Dsymbols vtbl)
{
+ bool seenInterfaceVirtual;
foreach (s; vtbl)
{
auto fd = s.isFuncDeclaration();
@@ -748,6 +750,23 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
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;
@@ -16,6 +16,7 @@ import core.stdc.stdio;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
+import dmd.attrib;
import dmd.ctorflow;
import dmd.dclass;
import dmd.delegatize;
@@ -34,7 +35,7 @@ import dmd.init;
import dmd.initsem;
import dmd.intrange;
import dmd.mtype;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.target;
import dmd.tokens;
@@ -705,7 +706,7 @@ extern (C++) final class AliasDeclaration : Declaration
assert(s);
}
- static AliasDeclaration create(Loc loc, Identifier id, Type type)
+ static AliasDeclaration create(const ref Loc loc, Identifier id, Type type)
{
return new AliasDeclaration(loc, id, type);
}
@@ -1192,14 +1193,7 @@ extern (C++) class VarDeclaration : Declaration
/* If coming after a bit field in progress,
* advance past the field
*/
- if (fieldState.inFlight)
- {
- fieldState.inFlight = false;
- if (0 && target.os & Target.OS.Posix)
- fieldState.offset += (fieldState.bitOffset + 7) / 8;
- else if (0 &&target.os == Target.OS.Windows)
- fieldState.offset += fieldState.fieldSize;
- }
+ fieldState.inFlight = false;
const sz = t.size(loc);
assert(sz != SIZE_INVALID && sz < uint.max);
@@ -1743,13 +1737,23 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
{
- //printf("BitFieldDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
+ //printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), toChars());
+ static void print(const ref FieldState fieldState)
+ {
+ printf("FieldState.offset = %d bytes\n", fieldState.offset);
+ printf(" .fieldOffset = %d bytes\n", fieldState.fieldOffset);
+ printf(" .bitOffset = %d bits\n", fieldState.bitOffset);
+ printf(" .fieldSize = %d bytes\n", fieldState.fieldSize);
+ printf(" .inFlight = %d\n\n", fieldState.inFlight);
+ }
+ //print(fieldState);
Type t = type.toBasetype();
+ const bool anon = isAnonymous();
// List in ad.fields. Even if the type is error, it's necessary to avoid
// pointless error diagnostic "more initializers than fields" on struct literal.
- if (!isAnonymous())
+ if (!anon)
ad.fields.push(this);
if (t.ty == Terror)
@@ -1760,17 +1764,36 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
uint memsize = cast(uint)sz; // size of member
uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
- if (fieldWidth == 0 && !isAnonymous())
+ if (fieldWidth == 0 && !anon)
error(loc, "named bit fields cannot have 0 width");
if (fieldWidth > memsize * 8)
error(loc, "bit field width %d is larger than type", fieldWidth);
+ const style = target.c.bitFieldStyle;
+
void startNewField()
{
+ uint alignsize;
+ if (style == TargetC.BitFieldStyle.Gcc_Clang)
+ {
+ if (fieldWidth > 32)
+ alignsize = memalignsize;
+ else if (fieldWidth > 16)
+ alignsize = 4;
+ else if (fieldWidth > 8)
+ alignsize = 2;
+ else
+ alignsize = 1;
+ }
+ else
+ alignsize = memsize; // not memalignsize
+
+ uint dummy;
offset = AggregateDeclaration.placeField(
&fieldState.offset,
- memsize, memalignsize, alignment,
- &ad.structsize, &ad.alignsize,
+ memsize, alignsize, alignment,
+ &ad.structsize,
+ (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? &dummy : &ad.alignsize,
isunion);
fieldState.inFlight = true;
@@ -1779,19 +1802,92 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
fieldState.fieldSize = memsize;
}
- if (!fieldState.inFlight || fieldWidth == 0)
+ if (style == TargetC.BitFieldStyle.Gcc_Clang)
{
- startNewField();
+ if (fieldWidth == 0)
+ {
+ if (!isunion)
+ {
+ // Use type of zero width field to align to next field
+ fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1);
+ ad.structsize = fieldState.offset;
+ }
+
+ fieldState.inFlight = false;
+ return;
+ }
+
+ if (ad.alignsize == 0)
+ ad.alignsize = 1;
+ if (!anon &&
+ ad.alignsize < memalignsize)
+ ad.alignsize = memalignsize;
+ }
+ else if (style == TargetC.BitFieldStyle.MS)
+ {
+ if (ad.alignsize == 0)
+ ad.alignsize = 1;
+ if (fieldWidth == 0)
+ {
+ if (fieldState.inFlight && !isunion)
+ {
+ // documentation says align to next int
+ //const alsz = cast(uint)Type.tint32.size();
+ const alsz = memsize; // but it really does this
+ fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1);
+ ad.structsize = fieldState.offset;
+ }
+
+ fieldState.inFlight = false;
+ return;
+ }
+ }
+ else if (style == TargetC.BitFieldStyle.DM)
+ {
+ if (anon && fieldWidth && (!fieldState.inFlight || fieldState.bitOffset == 0))
+ return; // this probably should be a bug in DMC
+ if (ad.alignsize == 0)
+ ad.alignsize = 1;
+ if (fieldWidth == 0)
+ {
+ if (fieldState.inFlight && !isunion)
+ {
+ const alsz = memsize;
+ fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1);
+ ad.structsize = fieldState.offset;
+ }
+
+ fieldState.inFlight = false;
+ return;
+ }
}
- if (0 && target.os & Target.OS.Posix)
+ if (!fieldState.inFlight)
+ {
+ startNewField();
+ }
+ else if (style == TargetC.BitFieldStyle.Gcc_Clang)
{
- if ((fieldState.offset%4 * 8) + fieldState.bitOffset + fieldWidth > int.sizeof * 8)
+ if (fieldState.bitOffset + fieldWidth > memsize * 8)
{
+ //printf("start1 fieldState.bitOffset:%u fieldWidth:%u memsize:%u\n", fieldState.bitOffset, fieldWidth, memsize);
startNewField();
}
+ else
+ {
+ // if alignment boundary is crossed
+ uint start = fieldState.fieldOffset * 8 + fieldState.bitOffset;
+ uint end = start + fieldWidth;
+ //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize);
+ if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8))
+ {
+ //printf("alignment is crossed\n");
+ startNewField();
+ }
+ }
}
- else if (1 || target.os == Target.OS.Windows)
+ else if (style == TargetC.BitFieldStyle.DM ||
+ style == TargetC.BitFieldStyle.MS)
{
if (memsize != fieldState.fieldSize ||
fieldState.bitOffset + fieldWidth > fieldState.fieldSize * 8)
@@ -1799,22 +1895,29 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
startNewField();
}
}
+ else
+ assert(0);
offset = fieldState.fieldOffset;
bitOffset = fieldState.bitOffset;
- if (0 && target.os & Target.OS.Posix)
+
+ const pastField = bitOffset + fieldWidth;
+ if (style == TargetC.BitFieldStyle.Gcc_Clang)
{
- while (bitOffset > memsize * 8)
- {
- bitOffset -= 8;
- offset += 1;
- }
+ auto size = (pastField + 7) / 8;
+ fieldState.fieldSize = size;
+ //printf(" offset: %d, size: %d\n", offset, size);
+ ad.structsize = offset + size;
}
+ else
+ fieldState.fieldSize = memsize;
+ //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize);
+ //print(fieldState);
- //fieldState.fieldSize = memsize;
if (!isunion)
{
- fieldState.bitOffset += fieldWidth;
+ fieldState.offset = offset + fieldState.fieldSize;
+ fieldState.bitOffset = pastField;
}
//printf("\t%s: memalignsize = %d\n", toChars(), memalignsize);
@@ -1861,7 +1964,7 @@ extern (C++) class TypeInfoDeclaration : VarDeclaration
storage_class = STC.static_ | STC.gshared;
visibility = Visibility(Visibility.Kind.public_);
linkage = LINK.c;
- alignment = target.ptrsize;
+ alignment.set(target.ptrsize);
}
static TypeInfoDeclaration create(Type tinfo)
@@ -188,7 +188,7 @@ public:
Dsymbol *overnext; // next in overload list
Dsymbol *_import; // !=NULL if unresolved internal alias for selective import
- static AliasDeclaration *create(Loc loc, Identifier *id, Type *type);
+ static AliasDeclaration *create(const Loc &loc, Identifier *id, Type *type);
AliasDeclaration *syntaxCopy(Dsymbol *);
bool overloadInsert(Dsymbol *s);
const char *kind() const;
@@ -511,7 +511,7 @@ enum class BUILTIN : unsigned char
toPrecReal
};
-Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments);
+Expression *eval_builtin(const Loc &loc, FuncDeclaration *fd, Expressions *arguments);
BUILTIN isBuiltin(FuncDeclaration *fd);
class FuncDeclaration : public Declaration
@@ -535,6 +535,8 @@ public:
VarDeclaration *vresult; // result variable for out contracts
LabelDsymbol *returnLabel; // where the return goes
+ void *isTypeIsolatedCache; // An AA on the D side to cache an expensive check result
+
// used to prevent symbols in different
// scopes from having the same name
DsymbolTable *localsymtab;
@@ -839,9 +841,6 @@ public:
class NewDeclaration : public FuncDeclaration
{
public:
- Parameters *parameters;
- VarArg varargs;
-
NewDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
bool isVirtual() const;
@@ -42,6 +42,7 @@ import dmd.mtype;
import dmd.printast;
import dmd.root.rmem;
import dmd.root.array;
+import dmd.root.ctfloat;
import dmd.root.region;
import dmd.root.rootobject;
import dmd.statement;
@@ -75,6 +76,7 @@ public Expression ctfeInterpret(Expression e)
case TOK.template_: // non-eponymous template/instance
case TOK.scope_: // ditto
case TOK.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
+ case TOK.dotTemplateInstance: // ditto
case TOK.dot: // ditto
if (e.type.ty == Terror)
return ErrorExp.get();
@@ -2167,26 +2169,20 @@ public:
return;
}
- // Note: This is a workaround for
- // https://issues.dlang.org/show_bug.cgi?id=17351
- // The aforementioned bug triggers when passing manifest constant by `ref`.
- // If there was not a previous reference to them, they are
- // not cached and trigger a "cannot be read at compile time".
- // This fix is a crude solution to get it to work. A more proper
- // approach would be to resolve the forward reference, but that is
- // much more involved.
- if (goal == CTFEGoal.LValue && e.var.type.isMutable())
+ if (goal == CTFEGoal.LValue)
{
if (auto v = e.var.isVarDeclaration())
{
- if (!v.isDataseg() && !v.isCTFE() && !istate)
- {
- e.error("variable `%s` cannot be read at compile time", v.toChars());
- result = CTFEExp.cantexp;
- return;
- }
if (!hasValue(v))
{
+ // Compile-time known non-CTFE variable from an outer context
+ // e.g. global or from a ref argument
+ if (v.isConst() || v.isImmutable())
+ {
+ result = getVarExp(e.loc, istate, v, goal);
+ return;
+ }
+
if (!v.isCTFE() && v.isDataseg())
e.error("static variable `%s` cannot be read at compile time", v.toChars());
else // CTFE initiated from inside a function
@@ -2201,7 +2197,7 @@ public:
Expression ev = getValue(v);
if (ev.op == TOK.variable ||
ev.op == TOK.index ||
- ev.op == TOK.slice ||
+ (ev.op == TOK.slice && ev.type.toBasetype().ty == Tsarray) ||
ev.op == TOK.dotVariable)
{
result = interpret(pue, ev, istate, goal);
@@ -2836,8 +2832,7 @@ public:
auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype);
se.origin = se;
se.ownedByCtfe = OwnedBy.ctfe;
- emplaceExp!(ClassReferenceExp)(pue, e.loc, se, e.type);
- Expression eref = pue.exp();
+ Expression eref = ctfeEmplaceExp!ClassReferenceExp(e.loc, se, e.type);
if (e.member)
{
// Call constructor
@@ -6024,12 +6019,23 @@ public:
}
if (e.to.ty == Tsarray)
e1 = resolveSlice(e1);
- if (e.to.toBasetype().ty == Tbool && e1.type.ty == Tpointer)
+
+ auto tobt = e.to.toBasetype();
+ if (tobt.ty == Tbool && e1.type.ty == Tpointer)
{
emplaceExp!(IntegerExp)(pue, e.loc, e1.op != TOK.null_, e.to);
result = pue.exp();
return;
}
+ else if (tobt.isTypeBasic() && e1.op == TOK.null_)
+ {
+ if (tobt.isintegral())
+ emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to);
+ else if (tobt.isreal())
+ emplaceExp!(RealExp)(pue, e.loc, CTFloat.zero, e.to);
+ result = pue.exp();
+ return;
+ }
result = ctfeCast(pue, e.loc, e.type, e.to, e1);
}
@@ -6306,7 +6312,7 @@ public:
auto tsa = cast(TypeSArray)v.type;
auto len = cast(size_t)tsa.dim.toInteger();
UnionExp ue = void;
- result = createBlockDuplicatedArrayLiteral(&ue, ex.loc, v.type, ex, len);
+ result = createBlockDuplicatedArrayLiteral(&ue, e.loc, v.type, result, len);
if (result == ue.exp())
result = ue.copy();
(*se.elements)[i] = result;
@@ -16,7 +16,7 @@ import core.stdc.string;
import dmd.doc;
import dmd.errors;
import dmd.globals;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
extern (C++) struct MacroTable
@@ -139,7 +139,7 @@ import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.root.ctfloat;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.aav;
import dmd.root.string;
import dmd.root.stringtable;
@@ -1259,14 +1259,49 @@ public:
override void visit(Parameter p)
{
- if (p.storageClass & STC.scope_ && !(p.storageClass & STC.scopeinferred))
- buf.writeByte('M');
+ // https://dlang.org/spec/abi.html#Parameter
+
+ auto stc = p.storageClass;
+
+ // Inferred storage classes don't get mangled in
+ if (stc & STC.scopeinferred)
+ stc &= ~(STC.scope_ | STC.scopeinferred);
+ if (stc & STC.returninferred)
+ stc &= ~(STC.return_ | STC.returninferred);
// 'return inout ref' is the same as 'inout ref'
- if ((p.storageClass & (STC.return_ | STC.wild)) == STC.return_ &&
- !(p.storageClass & STC.returninferred))
- buf.writestring("Nk");
- switch (p.storageClass & (STC.IOR | STC.lazy_))
+ if ((stc & (STC.return_ | STC.wild)) == (STC.return_ | STC.wild))
+ stc &= ~STC.return_;
+
+ // much like hdrgen.stcToBuffer()
+ string rrs;
+ const isout = (stc & STC.out_) != 0;
+ final switch (buildScopeRef(stc))
+ {
+ case ScopeRef.None:
+ case ScopeRef.Scope:
+ case ScopeRef.Ref:
+ case ScopeRef.Return:
+ case ScopeRef.RefScope:
+ break;
+
+ case ScopeRef.ReturnScope: rrs = "NkM"; goto L1; // return scope
+ case ScopeRef.ReturnRef: rrs = isout ? "NkJ" : "NkK"; goto L1; // return ref
+ case ScopeRef.ReturnRef_Scope: rrs = isout ? "MNkJ" : "MNkK"; goto L1; // scope return ref
+ case ScopeRef.Ref_ReturnScope: rrs = isout ? "NkMJ" : "NkMK"; goto L1; // return scope ref
+ L1:
+ buf.writestring(rrs);
+ stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
+ break;
+ }
+
+ if (stc & STC.scope_)
+ buf.writeByte('M'); // scope
+
+ if (stc & STC.return_)
+ buf.writestring("Nk"); // return
+
+ switch (stc & (STC.IOR | STC.lazy_))
{
case 0:
break;
@@ -1288,10 +1323,10 @@ public:
default:
debug
{
- printf("storageClass = x%llx\n", p.storageClass & (STC.IOR | STC.lazy_));
+ printf("storageClass = x%llx\n", stc & (STC.IOR | STC.lazy_));
}
assert(0);
}
- visitWithMask(p.type, (p.storageClass & STC.in_) ? MODFlags.const_ : 0);
+ visitWithMask(p.type, (stc & STC.in_) ? MODFlags.const_ : 0);
}
}
@@ -31,6 +31,7 @@ import dmd.dsymbolsem;
import dmd.errors;
import dmd.expression;
import dmd.expressionsem;
+import dmd.file_manager;
import dmd.globals;
import dmd.id;
import dmd.identifier;
@@ -39,7 +40,7 @@ import dmd.cparse;
import dmd.root.array;
import dmd.root.file;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.port;
import dmd.root.rmem;
import dmd.root.rootobject;
@@ -50,113 +51,6 @@ import dmd.target;
import dmd.utils;
import dmd.visitor;
-enum package_d = "package." ~ mars_ext;
-enum package_di = "package." ~ hdr_ext;
-
-/********************************************
- * Look for the source file if it's different from filename.
- * Look for .di, .d, directory, and along global.path.
- * Does not open the file.
- * Params:
- * filename = as supplied by the user
- * path = path to look for filename
- * Returns:
- * the found file name or
- * `null` if it is not different from filename.
- */
-private const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
-{
- //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
- /* Search along path[] for .di file, then .d file, then .i file, then .c file.
- */
- const sdi = FileName.forceExt(filename, hdr_ext);
- if (FileName.exists(sdi) == 1)
- return sdi;
- scope(exit) FileName.free(sdi.ptr);
-
- const sd = FileName.forceExt(filename, mars_ext);
- if (FileName.exists(sd) == 1)
- return sd;
- scope(exit) FileName.free(sd.ptr);
-
- const si = FileName.forceExt(filename, i_ext);
- if (FileName.exists(si) == 1)
- return si;
- scope(exit) FileName.free(si.ptr);
-
- const sc = FileName.forceExt(filename, c_ext);
- if (FileName.exists(sc) == 1)
- return sc;
- scope(exit) FileName.free(sc.ptr);
-
- if (FileName.exists(filename) == 2)
- {
- /* The filename exists and it's a directory.
- * Therefore, the result should be: filename/package.d
- * iff filename/package.d is a file
- */
- const ni = FileName.combine(filename, package_di);
- if (FileName.exists(ni) == 1)
- return ni;
- FileName.free(ni.ptr);
-
- const n = FileName.combine(filename, package_d);
- if (FileName.exists(n) == 1)
- return n;
- FileName.free(n.ptr);
- }
- if (FileName.absolute(filename))
- return null;
- if (!path.length)
- return null;
- foreach (entry; path)
- {
- const p = entry.toDString();
-
- const(char)[] n = FileName.combine(p, sdi);
- if (FileName.exists(n) == 1) {
- return n;
- }
- FileName.free(n.ptr);
-
- n = FileName.combine(p, sd);
- if (FileName.exists(n) == 1) {
- return n;
- }
- FileName.free(n.ptr);
-
- n = FileName.combine(p, si);
- if (FileName.exists(n) == 1) {
- return n;
- }
- FileName.free(n.ptr);
-
- n = FileName.combine(p, sc);
- if (FileName.exists(n) == 1) {
- return n;
- }
- FileName.free(n.ptr);
-
- const b = FileName.removeExt(filename);
- n = FileName.combine(p, b);
- FileName.free(b.ptr);
- if (FileName.exists(n) == 2)
- {
- const n2i = FileName.combine(n, package_di);
- if (FileName.exists(n2i) == 1)
- return n2i;
- FileName.free(n2i.ptr);
- const n2 = FileName.combine(n, package_d);
- if (FileName.exists(n2) == 1) {
- return n2;
- }
- FileName.free(n2.ptr);
- }
- FileName.free(n.ptr);
- }
- return null;
-}
-
// function used to call semantic3 on a module's dependencies
void semantic3OnDependencies(Module m)
{
@@ -414,8 +308,8 @@ extern (C++) class Package : ScopeDsymbol
packages ~= s.ident;
reverse(packages);
- if (lookForSourceFile(getFilename(packages, ident), global.path ? (*global.path)[] : null))
- Module.load(Loc(), packages, this.ident);
+ if (FileManager.lookForSourceFile(getFilename(packages, ident), global.path ? (*global.path)[] : null))
+ Module.load(Loc.initial, packages, this.ident);
else
isPkgMod = PKG.package_;
}
@@ -598,12 +492,12 @@ extern (C++) final class Module : Package
return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
}
- extern (C++) static Module load(Loc loc, Identifiers* packages, Identifier ident)
+ extern (C++) static Module load(const ref Loc loc, Identifiers* packages, Identifier ident)
{
return load(loc, packages ? (*packages)[] : null, ident);
}
- extern (D) static Module load(Loc loc, Identifier[] packages, Identifier ident)
+ extern (D) static Module load(const ref Loc loc, Identifier[] packages, Identifier ident)
{
//printf("Module::load(ident = '%s')\n", ident.toChars());
// Build module filename by turning:
@@ -612,7 +506,7 @@ extern (C++) final class Module : Package
// foo\bar\baz
const(char)[] filename = getFilename(packages, ident);
// Look for the source file
- if (const result = lookForSourceFile(filename, global.path ? (*global.path)[] : null))
+ if (const result = FileManager.lookForSourceFile(filename, global.path ? (*global.path)[] : null))
filename = result; // leaks
auto m = new Module(loc, filename, ident, 0, 0);
@@ -737,7 +631,12 @@ extern (C++) final class Module : Package
if (isPackageMod)
.error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
else
- error(loc, "is in file '%s' which cannot be read", srcfile.toChars());
+ {
+ .error(loc, "unable to read module `%s`", toChars());
+ const pkgfile = FileName.combine(FileName.removeExt(srcfile.toString()), package_d);
+ .errorSupplemental(loc, "Expected '%s' or '%s' in one of the following import paths:",
+ srcfile.toChars(), pkgfile.ptr);
+ }
}
if (!global.gag)
{
@@ -776,14 +675,25 @@ extern (C++) final class Module : Package
return true; // already read
//printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
- auto readResult = File.read(srcfile.toChars());
if (global.params.emitMakeDeps)
{
global.params.makeDeps.push(srcfile.toChars());
}
- return loadSourceBuffer(loc, readResult);
+ if (auto readResult = FileManager.fileManager.lookup(srcfile))
+ {
+ srcBuffer = readResult;
+ return true;
+ }
+
+ auto readResult = File.read(srcfile.toChars());
+ if (loadSourceBuffer(loc, readResult))
+ {
+ FileManager.fileManager.add(srcfile, srcBuffer);
+ return true;
+ }
+ return false;
}
/// syntactic parse
@@ -45,7 +45,7 @@ import dmd.mtype;
import dmd.root.array;
import dmd.root.file;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.port;
import dmd.root.rmem;
import dmd.root.string;
@@ -3995,8 +3995,8 @@ private size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const r
const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments);
if (iDelimiterRowEnd)
{
- const delta = replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true);
- if (delta)
+ size_t delta;
+ if (replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true, delta))
{
buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd);
buf.insert(iEnd + delta, "$(TBODY ");
@@ -4023,12 +4023,15 @@ private size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const r
* headerRow = if `true` then the number of columns will be enforced to match
* `columnAlignments.length` and the row will be surrounded by a
* `THEAD` macro
- * Returns: the number of characters added by replacing the row, or `0` if unchanged
+ * delta = the number of characters added by replacing the row, or `0` if unchanged
+ * Returns: `true` if a table row was found and replaced
*/
-private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow)
+private bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta)
{
+ delta = 0;
+
if (!columnAlignments.length || iStart == iEnd)
- return 0;
+ return false;
iStart = skipChars(buf, iStart, " \t");
int cellCount = 0;
@@ -4045,7 +4048,7 @@ private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, co
++cellCount;
if (headerRow && cellCount != columnAlignments.length)
- return 0;
+ return false;
if (headerRow && global.params.vmarkdown)
{
@@ -4053,8 +4056,6 @@ private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, co
message(loc, "Ddoc: formatting table '%.*s'", cast(int)s.length, s.ptr);
}
- size_t delta = 0;
-
void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di)
{
const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di);
@@ -4146,7 +4147,7 @@ private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, co
delta += 9;
}
- return delta;
+ return true;
}
/****************************************************
@@ -4182,7 +4183,8 @@ private size_t endTable(ref OutBuffer buf, size_t i, ref TableColumnAlignment[]
*/
private size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments)
{
- size_t delta = replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false);
+ size_t delta;
+ replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false, delta);
delta += endTable(buf, iEnd + delta, columnAlignments);
return delta;
}
@@ -4263,8 +4265,8 @@ private void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, s
i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments);
else if (columnAlignments.length)
{
- const delta = replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false);
- if (delta)
+ size_t delta;
+ if (replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false, delta))
i += delta;
else
i += endTable(buf, i, columnAlignments);
@@ -33,7 +33,7 @@ import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.speller;
import dmd.statement;
@@ -730,12 +730,21 @@ struct Scope
}
}
+ /******************************
+ */
structalign_t alignment()
{
if (aligndecl)
- return aligndecl.getAlignment(&this);
+ {
+ auto ad = aligndecl.getAlignment(&this);
+ return ad.salign;
+ }
else
- return STRUCTALIGN_DEFAULT;
+ {
+ structalign_t sa;
+ sa.setDefault();
+ return sa;
+ }
}
/**********************************
@@ -16,6 +16,7 @@ module dmd.dstruct;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
+import dmd.attrib;
import dmd.declaration;
import dmd.dmodule;
import dmd.dscope;
@@ -127,7 +128,7 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t)
*/
if (!sd.members)
return; // opaque struct
- if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.dtor && !sd.xhash && !search_toString(sd))
+ if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd))
return; // none of TypeInfo-specific members
// If the struct is in a non-root module, run semantic3 to get
@@ -232,7 +233,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
}
}
- static StructDeclaration create(Loc loc, Identifier id, bool inObject)
+ static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject)
{
return new StructDeclaration(loc, id, inObject);
}
@@ -297,22 +298,46 @@ extern (C++) class StructDeclaration : AggregateDeclaration
return;
}
- // 0 sized struct's are set to 1 byte
if (structsize == 0)
{
hasNoFields = true;
alignsize = 1;
- if (classKind != classKind.c) // C gets a struct size of 0
- structsize = 1;
+
+ // A fine mess of what size a zero sized struct should be
+ final switch (classKind)
+ {
+ case ClassKind.d:
+ case ClassKind.cpp:
+ structsize = 1;
+ break;
+
+ case ClassKind.c:
+ case ClassKind.objc:
+ if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS)
+ {
+ /* Undocumented MS behavior for:
+ * struct S { int :0; };
+ */
+ structsize = 4;
+ }
+ else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM)
+ {
+ structsize = 0;
+ alignsize = 0;
+ }
+ else
+ structsize = 0;
+ break;
+ }
}
// Round struct size up to next alignsize boundary.
// This will ensure that arrays of structs will get their internals
// aligned properly.
- if (alignment == STRUCTALIGN_DEFAULT)
+ if (alignment.isDefault() || alignment.isPack())
structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
else
- structsize = (structsize + alignment - 1) & ~(alignment - 1);
+ structsize = (structsize + alignment.get() - 1) & ~(alignment.get() - 1);
sizeok = Sizeok.done;
@@ -187,7 +187,7 @@ struct Visibility
}
}
-enum PASS : int
+enum PASS : ubyte
{
init, // initial state
semantic, // semantic() started
@@ -225,11 +225,13 @@ enum : int
*/
struct FieldState
{
- uint offset; /// offset for next field
+ uint offset; /// byte offset for next field
- uint fieldOffset; /// offset for the start of the bit field
+ uint fieldOffset; /// byte offset for the start of the bit field
+ uint fieldSize; /// byte size of field
+ uint fieldAlign; /// byte alignment of field
uint bitOffset; /// bit offset for field
- uint fieldSize; /// size of field in bytes
+
bool inFlight; /// bit field is in flight
}
@@ -793,10 +795,18 @@ extern (C++) class Dsymbol : ASTNode
Dsymbol s2 = sds.symtabLookup(this,ident);
// If using C tag/prototype/forward declaration rules
- if (sc.flags & SCOPE.Cfile &&
- handleTagSymbols(*sc, this, s2, sds))
+ if (sc.flags & SCOPE.Cfile)
+ {
+ if (handleTagSymbols(*sc, this, s2, sds))
+ return;
+ if (handleSymbolRedeclarations(*sc, this, s2, sds))
return;
+ sds.multiplyDefined(Loc.initial, this, s2); // ImportC doesn't allow overloading
+ errors = true;
+ return;
+ }
+
if (!s2.overloadInsert(this))
{
sds.multiplyDefined(Loc.initial, this, s2);
@@ -2384,3 +2394,91 @@ Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds)
}
+/**********************************************
+ * ImportC allows redeclarations of C variables, functions and typedefs.
+ * extern int x;
+ * int x = 3;
+ * and:
+ * extern void f();
+ * void f() { }
+ * Attempt to merge them.
+ * Params:
+ * sc = context
+ * s = symbol to add to symbol table
+ * s2 = existing declaration
+ * sds = symbol table
+ * Returns:
+ * if s and s2 are successfully put in symbol table then return the merged symbol,
+ * null if they conflict
+ */
+Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds)
+{
+ enum log = false;
+ if (log) printf("handleSymbolRedeclarations('%s')\n", s.toChars());
+
+ static Dsymbol collision()
+ {
+ if (log) printf(" collision\n");
+ return null;
+ }
+
+ auto vd = s.isVarDeclaration(); // new declaration
+ auto vd2 = s2.isVarDeclaration(); // existing declaration
+ if (vd && vd2)
+ {
+ // if one is `static` and the other isn't
+ if ((vd.storage_class ^ vd2.storage_class) & STC.static_)
+ return collision();
+
+ const i1 = vd._init && ! vd._init.isVoidInitializer();
+ const i2 = vd2._init && !vd2._init.isVoidInitializer();
+
+ if (i1 && i2)
+ return collision(); // can't both have initializers
+
+ if (i1)
+ return vd;
+
+ /* BUG: the types should match, which needs semantic() to be run on it
+ * extern int x;
+ * int x; // match
+ * typedef int INT;
+ * INT x; // match
+ * long x; // collision
+ * We incorrectly ignore these collisions
+ */
+ return vd2;
+ }
+
+ auto fd = s.isFuncDeclaration(); // new declaration
+ auto fd2 = s2.isFuncDeclaration(); // existing declaration
+ if (fd && fd2)
+ {
+ // if one is `static` and the other isn't
+ if ((fd.storage_class ^ fd2.storage_class) & STC.static_)
+ return collision();
+
+ if (fd.fbody && fd2.fbody)
+ return collision(); // can't both have bodies
+
+ if (fd.fbody)
+ return fd;
+
+ /* BUG: just like with VarDeclaration, the types should match, which needs semantic() to be run on it.
+ * FuncDeclaration::semantic2() can detect this, but it relies overnext being set.
+ */
+ return fd2;
+ }
+
+ auto td = s.isAliasDeclaration(); // new declaration
+ auto td2 = s2.isAliasDeclaration(); // existing declaration
+ if (td && td2)
+ {
+ /* BUG: just like with variables and functions, the types should match, which needs semantic() to be run on it.
+ * FuncDeclaration::semantic2() can detect this, but it relies overnext being set.
+ */
+ return td2;
+ }
+
+ return collision();
+}
@@ -108,7 +108,21 @@ struct Visibility
/* State of symbol in winding its way through the passes of the compiler
*/
-enum PASS
+enum class PASS : uint8_t
+{
+ init, // initial state
+ semantic, // semantic() started
+ semanticdone, // semantic() done
+ semantic2, // semantic2() started
+ semantic2done, // semantic2() done
+ semantic3, // semantic3() started
+ semantic3done, // semantic3() done
+ inline_, // inline started
+ inlinedone, // inline done
+ obj // toObjFile() run
+};
+
+enum
{
PASSinit, // initial state
PASSsemantic, // semantic() started
@@ -145,8 +159,10 @@ struct FieldState
unsigned offset;
unsigned fieldOffset;
+ unsigned fieldSize;
+ unsigned fieldAlign;
unsigned bitOffset;
- unsigned fieldSice;
+
bool inFlight;
};
@@ -56,7 +56,7 @@ import dmd.objc;
import dmd.opover;
import dmd.parse;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.semantic2;
@@ -109,17 +109,18 @@ extern(C++) void dsymbolSemantic(Dsymbol dsym, Scope* sc)
* ad = AlignmentDeclaration
* sc = context
* Returns:
- * alignment as numerical value that is never 0.
- * STRUCTALIGN_DEFAULT is used instead.
- * STRUCTALIGN_DEFAULT is returned for errors
+ * ad with alignment value determined
*/
-structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
+AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
{
- if (ad.salign != ad.UNKNOWN) // UNKNOWN is 0
- return ad.salign;
+ if (!ad.salign.isUnknown()) // UNKNOWN is 0
+ return ad;
if (!ad.exps)
- return ad.salign = STRUCTALIGN_DEFAULT;
+ {
+ ad.salign.setDefault();
+ return ad;
+ }
dinteger_t strictest = 0; // strictest alignment
bool errors;
@@ -140,7 +141,7 @@ structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
continue;
- if (n < 1 || n & (n - 1) || structalign_t.max < n || !e.type.isintegral())
+ 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;
@@ -150,10 +151,12 @@ structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
}
}
- ad.salign = (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
- ? STRUCTALIGN_DEFAULT
- : cast(structalign_t) strictest;
- return ad.salign;
+ 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);
+
+ return ad;
}
const(char)* getMessage(DeprecatedDeclaration dd)
@@ -365,16 +368,31 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
dsym.semanticRun = PASS.semantic;
- /* 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_));
+ // '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();
+ }
+
if (dsym.storage_class & STC.extern_ && dsym._init)
dsym.error("extern symbols cannot have initializers");
- dsym.userAttribDecl = sc.userAttribDecl;
- dsym.cppnamespace = sc.namespace;
-
AggregateDeclaration ad = dsym.isThis();
if (ad)
dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
@@ -431,16 +449,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
dsym.errors = true;
dsym.type.checkDeprecated(dsym.loc, sc);
- dsym.linkage = sc.linkage;
dsym.parent = sc.parent;
//printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
- dsym.visibility = sc.visibility;
/* If scope's alignment is the default, use the type's alignment,
* otherwise the scope overrrides.
*/
- dsym.alignment = sc.alignment();
- if (dsym.alignment == STRUCTALIGN_DEFAULT)
+ if (dsym.alignment.isDefault())
dsym.alignment = dsym.type.alignment(); // use type's alignment
//printf("sc.stc = %x\n", sc.stc);
@@ -935,6 +950,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
sc = sc.push();
sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
+ if (sc.flags & SCOPE.Cfile &&
+ dsym.type.isTypeSArray() &&
+ dsym.type.isTypeSArray().isIncomplete() &&
+ dsym._init.isVoidInitializer() &&
+ !(dsym.storage_class & STC.field))
+ {
+ dsym.error("incomplete array type must have initializer");
+ }
+
ExpInitializer ei = dsym._init.isExpInitializer();
if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424
@@ -971,6 +995,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
ei = new ExpInitializer(dsym._init.loc, e);
dsym._init = ei;
}
+ else if (sc.flags & SCOPE.Cfile && 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);
+ }
Expression exp = ei.exp;
Expression e1 = new VarExp(dsym.loc, dsym);
@@ -1056,18 +1087,29 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
// could crash (inf. recursion) on a mod/pkg referencing itself
if (ei && (ei.exp.op != TOK.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
{
- Expression exp = ei.exp.syntaxCopy();
-
- bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
- if (needctfe)
- sc = sc.startCTFE();
- exp = exp.expressionSemantic(sc);
- exp = resolveProperties(sc, exp);
- if (needctfe)
- sc = sc.endCTFE();
+ 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();
+
+ bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest);
+ if (needctfe)
+ sc = sc.startCTFE();
+ exp = exp.expressionSemantic(sc);
+ exp = resolveProperties(sc, exp);
+ if (needctfe)
+ sc = sc.endCTFE();
+ ei.exp = exp;
+ }
Type tb2 = dsym.type.toBasetype();
- Type ti = exp.type.toBasetype();
+ Type ti = ei.exp.type.toBasetype();
/* The problem is the following code:
* struct CopyTest {
@@ -1091,11 +1133,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (sd.postblit && tb2.toDsymbol(null) == sd)
{
// The only allowable initializer is a (non-copy) constructor
- if (exp.isLvalue())
+ if (ei.exp.isLvalue())
dsym.error("of type struct `%s` uses `this(this)`, which is not allowed in static initialization", tb2.toChars());
}
}
- ei.exp = exp;
}
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
@@ -1708,6 +1749,36 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
{
if (pd.args && pd.args.dim != 0)
pd.error("takes no argument");
+ else
+ {
+ immutable isCtor = pd.ident == Id.crt_constructor;
+
+ static uint recurse(Dsymbol s, bool isCtor)
+ {
+ if (auto ad = s.isAttribDeclaration())
+ {
+ uint nestedCount;
+ auto decls = ad.include(null);
+ if (decls)
+ {
+ for (size_t i = 0; i < decls.dim; ++i)
+ nestedCount += recurse((*decls)[i], isCtor);
+ }
+ return nestedCount;
+ }
+ else if (auto f = s.isFuncDeclaration())
+ {
+ f.isCrtCtorDtor |= isCtor ? 1 : 2;
+ return 1;
+ }
+ else
+ return 0;
+ assert(0);
+ }
+
+ if (recurse(pd, isCtor) > 1)
+ pd.error("can only apply to a single declaration");
+ }
return declarations();
}
else if (pd.ident == Id.printf || pd.ident == Id.scanf)
@@ -2445,6 +2516,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
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_);
@@ -2973,6 +3045,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
if (auto pragmadecl = sc.inlining)
funcdecl.inlining = pragmadecl.evalPragmaInline(sc);
+ // check pragma(crt_constructor)
+ if (funcdecl.isCrtCtorDtor)
+ {
+ if (funcdecl.linkage != LINK.c)
+ {
+ funcdecl.error("must be `extern(C)` for `pragma(%s)`",
+ funcdecl.isCrtCtorDtor == 1 ? "crt_constructor".ptr : "crt_destructor".ptr);
+ }
+ }
+
funcdecl.visibility = sc.visibility;
funcdecl.userAttribDecl = sc.userAttribDecl;
UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl.linkage);
@@ -3799,6 +3881,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
}
+ if (funcdecl.fbody && sc._module.isRoot() &&
+ (funcdecl.isMain() || funcdecl.isWinMain() || funcdecl.isDllMain() || funcdecl.isCMain()))
+ global.hasMainFunction = true;
+
if (funcdecl.fbody && funcdecl.isMain() && sc._module.isRoot())
{
// check if `_d_cmain` is defined
@@ -3995,7 +4081,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
return;
}
if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic)
- ad.dtors.push(dd);
+ ad.userDtors.push(dd);
if (!dd.type)
{
dd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dd.storage_class);
@@ -4417,8 +4503,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
// Look for the constructor
sd.ctor = sd.searchCtor();
- sd.dtor = buildDtor(sd, sc2);
- sd.tidtor = buildExternDDtor(sd, sc2);
+ buildDtors(sd, sc2);
+
sd.hasCopyCtor = buildCopyCtor(sd, sc2);
sd.postblit = buildPostBlit(sd, sc2);
@@ -5057,8 +5143,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
}
}
- cldec.dtor = buildDtor(cldec, sc2);
- cldec.tidtor = buildExternDDtor(cldec, sc2);
+ buildDtors(cldec, sc2);
if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1)
{
@@ -67,7 +67,7 @@ import dmd.initsem;
import dmd.mtype;
import dmd.opover;
import dmd.root.array;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.semantic2;
import dmd.semantic3;
@@ -985,9 +985,9 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
buf.writenl();
buf.writestring(" ");
}
- buf.write((*parameters)[i]);
+ write(buf, (*parameters)[i]);
buf.writestring(" = ");
- buf.write(tiargs[i]);
+ write(buf, tiargs[i]);
}
// write remaining variadic arguments on the last line
if (variadic)
@@ -998,16 +998,16 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
buf.writenl();
buf.writestring(" ");
}
- buf.write((*parameters)[end]);
+ write(buf, (*parameters)[end]);
buf.writestring(" = ");
buf.writeByte('(');
if (cast(int)tiargs.length - end > 0)
{
- buf.write(tiargs[end]);
+ write(buf, tiargs[end]);
foreach (j; parameters.length .. tiargs.length)
{
buf.writestring(", ");
- buf.write(tiargs[j]);
+ write(buf, tiargs[j]);
}
}
buf.writeByte(')');
@@ -1919,8 +1919,15 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
/* If a semantic error occurs while doing alias this,
* eg purity(https://issues.dlang.org/show_bug.cgi?id=7295),
* just regard it as not a match.
- */
- if (auto e = resolveAliasThis(sc, farg, true))
+ *
+ * We also save/restore sc.func.flags to avoid messing up
+ * attribute inference in the evaluation.
+ */
+ const oldflags = sc.func ? sc.func.flags : 0;
+ auto e = resolveAliasThis(sc, farg, true);
+ if (sc.func)
+ sc.func.flags = oldflags;
+ if (e)
{
farg = e;
goto Lretry;
@@ -5340,7 +5347,7 @@ extern (C++) class TemplateParameter : ASTNode
abstract RootObject specialization();
- abstract RootObject defaultArg(Loc instLoc, Scope* sc);
+ abstract RootObject defaultArg(const ref Loc instLoc, Scope* sc);
abstract bool hasDefaultArg();
@@ -5422,7 +5429,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter
return specType;
}
- override final RootObject defaultArg(Loc instLoc, Scope* sc)
+ override final RootObject defaultArg(const ref Loc instLoc, Scope* sc)
{
Type t = defaultType;
if (t)
@@ -5541,7 +5548,7 @@ extern (C++) final class TemplateValueParameter : TemplateParameter
return specValue;
}
- override RootObject defaultArg(Loc instLoc, Scope* sc)
+ override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
{
Expression e = defaultValue;
if (e)
@@ -5640,7 +5647,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter
return specAlias;
}
- override RootObject defaultArg(Loc instLoc, Scope* sc)
+ override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
{
RootObject da = defaultAlias;
Type ta = isType(defaultAlias);
@@ -5741,7 +5748,7 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter
return null;
}
- override RootObject defaultArg(Loc instLoc, Scope* sc)
+ override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
{
return null;
}
@@ -8413,3 +8420,11 @@ private struct MATCHpair
this.mfa = mfa;
}
}
+
+private void write(ref OutBuffer buf, RootObject obj)
+{
+ if (obj)
+ {
+ buf.writestring(obj.toChars());
+ }
+}
@@ -17,6 +17,7 @@ import core.stdc.ctype;
import dmd.astcodegen;
import dmd.arraytypes;
+import dmd.attrib;
import dmd.dsymbol;
import dmd.errors;
import dmd.globals;
@@ -25,7 +26,7 @@ import dmd.root.filename;
import dmd.visitor;
import dmd.tokens;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.utils;
//debug = Debug_DtoH;
@@ -254,7 +255,7 @@ public:
Identifier ident;
/// Original type of the currently visited declaration
- AST.Type* origType;
+ AST.Type origType;
/// Last written visibility level applying to the current scope
AST.Visibility.Kind currentVisibility;
@@ -706,6 +707,10 @@ public:
// printf("FuncDeclaration %s %s\n", fd.toPrettyChars(), fd.type.toChars());
visited[cast(void*)fd] = true;
+ // silently ignore non-user-defined destructors
+ if (fd.generated && fd.isDtorDeclaration())
+ return;
+
// Note that tf might be null for templated (member) functions
auto tf = cast(AST.TypeFunction)fd.type;
if ((tf && tf.linkage != LINK.c && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration()))
@@ -841,12 +846,12 @@ public:
return;
if (vd.originalType && vd.type == AST.Type.tsize_t)
- origType = &vd.originalType;
+ origType = vd.originalType;
scope(exit) origType = null;
- if (vd.alignment != STRUCTALIGN_DEFAULT)
+ if (!vd.alignment.isDefault())
{
- buf.printf("// Ignoring var %s alignment %u", vd.toChars(), vd.alignment);
+ buf.printf("// Ignoring var %s alignment %d", vd.toChars(), vd.alignment.get());
buf.writenl();
}
@@ -1008,12 +1013,12 @@ public:
if (ad.originalType && ad.type.ty == AST.Tpointer &&
(cast(AST.TypePointer)t).nextOf.ty == AST.Tfunction)
{
- origType = &ad.originalType;
+ origType = ad.originalType;
}
scope(exit) origType = null;
buf.writestring("typedef ");
- typeToBuffer(origType ? *origType : t, ad);
+ typeToBuffer(origType !is null ? origType : t, ad);
writeDeclEnd();
return;
}
@@ -1346,7 +1351,7 @@ public:
/// Starts a custom alignment section using `#pragma pack` if
/// `alignment` specifies a custom alignment
- private void pushAlignToBuffer(uint alignment)
+ private void pushAlignToBuffer(structalign_t alignment)
{
// DMD ensures alignment is a power of two
//assert(alignment > 0 && ((alignment & (alignment - 1)) == 0),
@@ -1354,20 +1359,20 @@ public:
// When no alignment is specified, `uint.max` is the default
// FIXME: alignment is 0 for structs templated members
- if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
+ if (alignment.isDefault() || (tdparent && alignment.isUnknown()))
{
return;
}
- buf.printf("#pragma pack(push, %d)", alignment);
+ buf.printf("#pragma pack(push, %d)", alignment.get());
buf.writenl();
}
/// Ends a custom alignment section using `#pragma pack` if
/// `alignment` specifies a custom alignment
- private void popAlignToBuffer(uint alignment)
+ private void popAlignToBuffer(structalign_t alignment)
{
- if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
+ if (alignment.isDefault() || (tdparent && alignment.isUnknown()))
return;
buf.writestringln("#pragma pack(pop)");
@@ -1645,7 +1650,7 @@ public:
}
this.ident = s.ident;
- auto type = origType ? *origType : t;
+ auto type = origType !is null ? origType : t;
AST.Dsymbol customLength;
// Check for quirks that are usually resolved during semantic
@@ -22,7 +22,7 @@ import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.globals;
import dmd.identifier;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.visitor;
/***********************************************************
@@ -59,7 +59,7 @@ import dmd.opover;
import dmd.optimize;
import dmd.root.ctfloat;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.root.string;
@@ -1201,20 +1201,14 @@ extern (C++) abstract class Expression : ASTNode
// DtorDeclaration without parents should fail at an earlier stage
auto ad = cast(AggregateDeclaration) f.toParent2();
assert(ad);
- assert(ad.dtors.length);
- // Search for the user-defined destructor (if any)
- foreach(dtor; ad.dtors)
+ if (ad.userDtors.dim)
{
- if (dtor.generated)
- continue;
-
- if (!check(dtor)) // doesn't match check (e.g. is impure as well)
+ if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
return;
// Sanity check
- assert(!check(cast(DtorDeclaration) ad.fieldDtor));
- break;
+ assert(!check(ad.fieldDtor));
}
dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
@@ -1781,13 +1775,13 @@ extern (C++) final class IntegerExp : Expression
this.value = cast(d_int32)value;
}
- static IntegerExp create(Loc loc, dinteger_t value, Type type)
+ static IntegerExp create(const ref Loc loc, dinteger_t value, Type type)
{
return new IntegerExp(loc, value, type);
}
// Same as create, but doesn't allocate memory.
- static void emplace(UnionExp* pue, Loc loc, dinteger_t value, Type type)
+ static void emplace(UnionExp* pue, const ref Loc loc, dinteger_t value, Type type)
{
emplaceExp!(IntegerExp)(pue, loc, value, type);
}
@@ -2052,13 +2046,13 @@ extern (C++) final class RealExp : Expression
this.type = type;
}
- static RealExp create(Loc loc, real_t value, Type type)
+ static RealExp create(const ref Loc loc, real_t value, Type type)
{
return new RealExp(loc, value, type);
}
// Same as create, but doesn't allocate memory.
- static void emplace(UnionExp* pue, Loc loc, real_t value, Type type)
+ static void emplace(UnionExp* pue, const ref Loc loc, real_t value, Type type)
{
emplaceExp!(RealExp)(pue, loc, value, type);
}
@@ -2127,13 +2121,13 @@ extern (C++) final class ComplexExp : Expression
//printf("ComplexExp::ComplexExp(%s)\n", toChars());
}
- static ComplexExp create(Loc loc, complex_t value, Type type)
+ static ComplexExp create(const ref Loc loc, complex_t value, Type type)
{
return new ComplexExp(loc, value, type);
}
// Same as create, but doesn't allocate memory.
- static void emplace(UnionExp* pue, Loc loc, complex_t value, Type type)
+ static void emplace(UnionExp* pue, const ref Loc loc, complex_t value, Type type)
{
emplaceExp!(ComplexExp)(pue, loc, value, type);
}
@@ -2203,7 +2197,7 @@ extern (C++) class IdentifierExp : Expression
this.ident = ident;
}
- static IdentifierExp create(Loc loc, Identifier ident)
+ static IdentifierExp create(const ref Loc loc, Identifier ident)
{
return new IdentifierExp(loc, ident);
}
@@ -2421,28 +2415,28 @@ extern (C++) final class StringExp : Expression
this.postfix = postfix;
}
- static StringExp create(Loc loc, char* s)
+ static StringExp create(const ref Loc loc, const(char)* s)
{
return new StringExp(loc, s.toDString());
}
- static StringExp create(Loc loc, void* string, size_t len)
+ static StringExp create(const ref Loc loc, const(void)* string, size_t len)
{
return new StringExp(loc, string[0 .. len]);
}
// Same as create, but doesn't allocate memory.
- static void emplace(UnionExp* pue, Loc loc, char* s)
+ static void emplace(UnionExp* pue, const ref Loc loc, const(char)* s)
{
emplaceExp!(StringExp)(pue, loc, s.toDString());
}
- extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string)
+ extern (D) static void emplace(UnionExp* pue, const ref Loc loc, const(void)[] string)
{
emplaceExp!(StringExp)(pue, loc, string);
}
- extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix)
+ extern (D) static void emplace(UnionExp* pue, const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix)
{
emplaceExp!(StringExp)(pue, loc, string, len, sz, postfix);
}
@@ -2863,7 +2857,7 @@ extern (C++) final class TupleExp : Expression
}
}
- static TupleExp create(Loc loc, Expressions* exps)
+ static TupleExp create(const ref Loc loc, Expressions* exps)
{
return new TupleExp(loc, exps);
}
@@ -2946,13 +2940,13 @@ extern (C++) final class ArrayLiteralExp : Expression
this.elements = elements;
}
- static ArrayLiteralExp create(Loc loc, Expressions* elements)
+ static ArrayLiteralExp create(const ref Loc loc, Expressions* elements)
{
return new ArrayLiteralExp(loc, null, elements);
}
// Same as create, but doesn't allocate memory.
- static void emplace(UnionExp* pue, Loc loc, Expressions* elements)
+ static void emplace(UnionExp* pue, const ref Loc loc, Expressions* elements)
{
emplaceExp!(ArrayLiteralExp)(pue, loc, null, elements);
}
@@ -3188,7 +3182,7 @@ extern (C++) final class StructLiteralExp : Expression
//printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
}
- static StructLiteralExp create(Loc loc, StructDeclaration sd, void* elements, Type stype = null)
+ static StructLiteralExp create(const ref Loc loc, StructDeclaration sd, void* elements, Type stype = null)
{
return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype);
}
@@ -3532,7 +3526,7 @@ extern (C++) final class NewExp : Expression
this.arguments = arguments;
}
- static NewExp create(Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
+ static NewExp create(const ref Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
{
return new NewExp(loc, thisexp, newargs, newtype, arguments);
}
@@ -3653,7 +3647,7 @@ extern (C++) final class VarExp : SymbolExp
this.type = var.type;
}
- static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
+ static VarExp create(const ref Loc loc, Declaration var, bool hasOverloads = true)
{
return new VarExp(loc, var, hasOverloads);
}
@@ -3965,6 +3959,7 @@ extern (C++) final class FuncExp : Expression
auto tfy = new TypeFunction(tfx.parameterList, tof.next,
tfx.linkage, STC.undefined_);
tfy.mod = tfx.mod;
+ tfy.trust = tfx.trust;
tfy.isnothrow = tfx.isnothrow;
tfy.isnogc = tfx.isnogc;
tfy.purity = tfx.purity;
@@ -4688,6 +4683,7 @@ extern (C++) final class DotIdExp : UnaExp
Identifier ident;
bool noderef; // true if the result of the expression will never be dereferenced
bool wantsym; // do not replace Symbol with its initializer during semantic()
+ bool arrow; // ImportC: if -> instead of .
extern (D) this(const ref Loc loc, Expression e, Identifier ident)
{
@@ -4695,7 +4691,7 @@ extern (C++) final class DotIdExp : UnaExp
this.ident = ident;
}
- static DotIdExp create(Loc loc, Expression e, Identifier ident)
+ static DotIdExp create(const ref Loc loc, Expression e, Identifier ident)
{
return new DotIdExp(loc, e, ident);
}
@@ -4889,6 +4885,31 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
return ti.updateTempDecl(sc, s);
}
+ override bool checkType()
+ {
+ // Same logic as ScopeExp.checkType()
+ if (ti.tempdecl &&
+ ti.semantictiargsdone &&
+ ti.semanticRun == PASS.init)
+ {
+ error("partial %s `%s` has no type", ti.kind(), toChars());
+ return true;
+ }
+ return false;
+ }
+
+ override bool checkValue()
+ {
+ if (ti.tempdecl &&
+ ti.semantictiargsdone &&
+ ti.semanticRun == PASS.init)
+
+ error("partial %s `%s` has no value", ti.kind(), toChars());
+ else
+ error("%s `%s` has no value", ti.kind(), ti.toChars());
+ return true;
+ }
+
override void accept(Visitor v)
{
v.visit(this);
@@ -4987,17 +5008,17 @@ extern (C++) final class CallExp : UnaExp
this.f = fd;
}
- static CallExp create(Loc loc, Expression e, Expressions* exps)
+ static CallExp create(const ref Loc loc, Expression e, Expressions* exps)
{
return new CallExp(loc, e, exps);
}
- static CallExp create(Loc loc, Expression e)
+ static CallExp create(const ref Loc loc, Expression e)
{
return new CallExp(loc, e);
}
- static CallExp create(Loc loc, Expression e, Expression earg1)
+ static CallExp create(const ref Loc loc, Expression e, Expression earg1)
{
return new CallExp(loc, e, earg1);
}
@@ -5009,7 +5030,7 @@ extern (C++) final class CallExp : UnaExp
* fd = the declaration of the function to call
* earg1 = the function argument
*/
- static CallExp create(Loc loc, FuncDeclaration fd, Expression earg1)
+ static CallExp create(const ref Loc loc, FuncDeclaration fd, Expression earg1)
{
return new CallExp(loc, fd, earg1);
}
@@ -5167,6 +5188,19 @@ extern (C++) final class PtrExp : UnaExp
override Expression modifiableLvalue(Scope* sc, Expression e)
{
//printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars());
+ Declaration var;
+ if (auto se = e1.isSymOffExp())
+ var = se.var;
+ else if (auto ve = e1.isVarExp())
+ var = ve.var;
+ if (var && var.type.isFunction_Delegate_PtrToFunction())
+ {
+ if (var.type.isTypeFunction())
+ error("function `%s` is not an lvalue and cannot be modified", var.toChars());
+ else
+ error("function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
+ return ErrorExp.get();
+ }
return Expression.modifiableLvalue(sc, e);
}
@@ -5331,13 +5365,13 @@ extern (C++) final class VectorExp : UnaExp
to = cast(TypeVector)t;
}
- static VectorExp create(Loc loc, Expression e, Type t)
+ static VectorExp create(const ref Loc loc, Expression e, Type t)
{
return new VectorExp(loc, e, t);
}
// Same as create, but doesn't allocate memory.
- static void emplace(UnionExp* pue, Loc loc, Expression e, Type type)
+ static void emplace(UnionExp* pue, const ref Loc loc, Expression e, Type type)
{
emplaceExp!(VectorExp)(pue, loc, e, type);
}
@@ -57,6 +57,9 @@ enum
OWNEDcache // constant value cached for CTFE
};
+#define WANTvalue 0 // default
+#define WANTexpand 1 // expand const/immutable variables if possible
+
/**
* Specifies how the checkModify deals with certain situations
*/
@@ -239,8 +242,8 @@ class IntegerExp : public Expression
public:
dinteger_t value;
- static IntegerExp *create(Loc loc, dinteger_t value, Type *type);
- static void emplace(UnionExp *pue, Loc loc, dinteger_t value, Type *type);
+ static IntegerExp *create(const Loc &loc, dinteger_t value, Type *type);
+ static void emplace(UnionExp *pue, const Loc &loc, dinteger_t value, Type *type);
bool equals(const RootObject *o) const;
dinteger_t toInteger();
real_t toReal();
@@ -269,8 +272,8 @@ class RealExp : public Expression
public:
real_t value;
- static RealExp *create(Loc loc, real_t value, Type *type);
- static void emplace(UnionExp *pue, Loc loc, real_t value, Type *type);
+ static RealExp *create(const Loc &loc, real_t value, Type *type);
+ static void emplace(UnionExp *pue, const Loc &loc, real_t value, Type *type);
bool equals(const RootObject *o) const;
dinteger_t toInteger();
uinteger_t toUInteger();
@@ -286,8 +289,8 @@ class ComplexExp : public Expression
public:
complex_t value;
- static ComplexExp *create(Loc loc, complex_t value, Type *type);
- static void emplace(UnionExp *pue, Loc loc, complex_t value, Type *type);
+ static ComplexExp *create(const Loc &loc, complex_t value, Type *type);
+ static void emplace(UnionExp *pue, const Loc &loc, complex_t value, Type *type);
bool equals(const RootObject *o) const;
dinteger_t toInteger();
uinteger_t toUInteger();
@@ -303,7 +306,7 @@ class IdentifierExp : public Expression
public:
Identifier *ident;
- static IdentifierExp *create(Loc loc, Identifier *ident);
+ static IdentifierExp *create(const Loc &loc, Identifier *ident);
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
void accept(Visitor *v) { v->visit(this); }
@@ -365,10 +368,9 @@ public:
utf8_t postfix; // 'c', 'w', 'd'
OwnedBy ownedByCtfe;
- static StringExp *create(Loc loc, char *s);
- static StringExp *create(Loc loc, void *s, size_t len);
- static void emplace(UnionExp *pue, Loc loc, char *s);
- static void emplace(UnionExp *pue, Loc loc, void *s, size_t len);
+ static StringExp *create(const Loc &loc, const char *s);
+ static StringExp *create(const Loc &loc, const void *s, size_t len);
+ static void emplace(UnionExp *pue, const Loc &loc, const char *s);
bool equals(const RootObject *o) const;
StringExp *toStringExp();
StringExp *toUTF8(Scope *sc);
@@ -397,7 +399,7 @@ public:
*/
Expressions *exps;
- static TupleExp *create(Loc loc, Expressions *exps);
+ static TupleExp *create(const Loc &loc, Expressions *exps);
TupleExp *toTupleExp();
TupleExp *syntaxCopy();
bool equals(const RootObject *o) const;
@@ -412,8 +414,8 @@ public:
Expressions *elements;
OwnedBy ownedByCtfe;
- static ArrayLiteralExp *create(Loc loc, Expressions *elements);
- static void emplace(UnionExp *pue, Loc loc, Expressions *elements);
+ static ArrayLiteralExp *create(const Loc &loc, Expressions *elements);
+ static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements);
ArrayLiteralExp *syntaxCopy();
bool equals(const RootObject *o) const;
Expression *getElement(d_size_t i); // use opIndex instead
@@ -468,7 +470,7 @@ public:
bool isOriginal; // used when moving instances to indicate `this is this.origin`
OwnedBy ownedByCtfe;
- static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
+ static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
bool equals(const RootObject *o) const;
StructLiteralExp *syntaxCopy();
Expression *getField(Type *type, unsigned offset);
@@ -528,7 +530,7 @@ public:
bool onstack; // allocate on stack
bool thrownew; // this NewExp is the expression of a ThrowStatement
- static NewExp *create(Loc loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments);
+ static NewExp *create(const Loc &loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments);
NewExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
@@ -576,7 +578,7 @@ class VarExp : public SymbolExp
{
public:
bool delegateWasExtracted;
- static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
+ static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true);
bool equals(const RootObject *o) const;
bool isLvalue();
Expression *toLvalue(Scope *sc, Expression *e);
@@ -746,8 +748,9 @@ public:
Identifier *ident;
bool noderef; // true if the result of the expression will never be dereferenced
bool wantsym; // do not replace Symbol with its initializer during semantic()
+ bool arrow; // ImportC: if -> instead of .
- static DotIdExp *create(Loc loc, Expression *e, Identifier *ident);
+ static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident);
void accept(Visitor *v) { v->visit(this); }
};
@@ -780,6 +783,8 @@ public:
DotTemplateInstanceExp *syntaxCopy();
bool findTempDecl(Scope *sc);
+ bool checkType();
+ bool checkValue();
void accept(Visitor *v) { v->visit(this); }
};
@@ -812,10 +817,10 @@ public:
bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code)
VarDeclaration *vthis2; // container for multi-context
- static CallExp *create(Loc loc, Expression *e, Expressions *exps);
- static CallExp *create(Loc loc, Expression *e);
- static CallExp *create(Loc loc, Expression *e, Expression *earg1);
- static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1);
+ static CallExp *create(const Loc &loc, Expression *e, Expressions *exps);
+ static CallExp *create(const Loc &loc, Expression *e);
+ static CallExp *create(const Loc &loc, Expression *e, Expression *earg1);
+ static CallExp *create(const Loc &loc, FuncDeclaration *fd, Expression *earg1);
CallExp *syntaxCopy();
bool isLvalue();
@@ -893,8 +898,8 @@ public:
unsigned dim; // number of elements in the vector
OwnedBy ownedByCtfe;
- static VectorExp *create(Loc loc, Expression *e, Type *t);
- static void emplace(UnionExp *pue, Loc loc, Expression *e, Type *t);
+ static VectorExp *create(const Loc &loc, Expression *e, Type *t);
+ static void emplace(UnionExp *pue, const Loc &loc, Expression *e, Type *t);
VectorExp *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@@ -1272,7 +1277,7 @@ public:
class GenericExp : Expression
{
- Expression cntlExp;
+ Expression *cntlExp;
Types *types;
Expressions *exps;
@@ -43,12 +43,14 @@ import dmd.dtemplate;
import dmd.errors;
import dmd.escape;
import dmd.expression;
+import dmd.file_manager;
import dmd.func;
import dmd.globals;
import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.imphint;
+import dmd.importc;
import dmd.init;
import dmd.initsem;
import dmd.inline;
@@ -62,7 +64,7 @@ import dmd.printast;
import dmd.root.ctfloat;
import dmd.root.file;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.semantic2;
@@ -1239,6 +1241,10 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
tthis = dve.e1.type;
goto Lfd;
}
+ else if (sc && sc.flags & SCOPE.Cfile && e1.op == TOK.variable && !e2)
+ {
+ // ImportC: do not implicitly call function if no ( ) are present
+ }
else if (e1.op == TOK.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration()))
{
s = (cast(VarExp)e1).var;
@@ -1388,7 +1394,6 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
Type t0 = null;
Expression e0 = null;
- size_t j0 = ~0;
bool foundType;
for (size_t i = 0; i < exps.dim; i++)
@@ -1441,17 +1446,16 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
{
// https://issues.dlang.org/show_bug.cgi?id=21285
// Functions and delegates don't convert correctly with castTo below
- exps[j0] = condexp.e1;
+ exps[i] = condexp.e1;
e = condexp.e2;
}
else
{
// Convert to common type
- exps[j0] = condexp.e1.castTo(sc, condexp.type);
+ exps[i] = condexp.e1.castTo(sc, condexp.type);
e = condexp.e2.castTo(sc, condexp.type);
}
}
- j0 = i;
e0 = e;
t0 = e.type;
if (e.op != TOK.error)
@@ -1599,6 +1603,7 @@ private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool repo
{
Expression arg = (*exps)[i];
arg = resolveProperties(sc, arg);
+ arg = arg.arrayFuncConv(sc);
if (arg.op == TOK.type)
{
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
@@ -4297,7 +4302,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* expr.foo!(tiargs)(funcargs)
*/
Ldotti:
- if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
+ if (exp.e1.op == TOK.dotTemplateInstance)
{
DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)exp.e1;
TemplateInstance ti = se.ti;
@@ -4352,7 +4357,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* We handle such earlier, so go back.
* Note that in the rewrite, we carefully did not run semantic() on e1
*/
- if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
+ if (exp.e1.op == TOK.dotTemplateInstance)
{
goto Ldotti;
}
@@ -4419,6 +4424,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else if (exp.e1.op == TOK.type && (sc && sc.flags & SCOPE.Cfile))
{
+ const numArgs = exp.arguments ? exp.arguments.length : 0;
+ if (e1org.parens && numArgs >= 1)
+ {
+ /* Ambiguous cases arise from CParser where there is not enough
+ * information to determine if we have a function call or a cast.
+ * ( type-name ) ( identifier ) ;
+ * ( identifier ) ( identifier ) ;
+ * If exp.e1 is a type-name, then this is a cast.
+ */
+ Expression arg;
+ foreach (a; (*exp.arguments)[])
+ {
+ arg = arg ? new CommaExp(a.loc, arg, a) : a;
+ }
+ auto t = exp.e1.isTypeExp().type;
+ auto e = new CastExp(exp.loc, arg, t);
+ result = e.expressionSemantic(sc);
+ return;
+ }
+
/* Ambiguous cases arise from CParser where there is not enough
* information to determine if we have a function call or declaration.
* type-name ( identifier ) ;
@@ -4426,7 +4451,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* If exp.e1 is a type-name, then this is a declaration. C11 does not
* have type construction syntax, so don't convert this to a cast().
*/
- if (exp.arguments && exp.arguments.dim == 1)
+ if (numArgs == 1)
{
Expression arg = (*exp.arguments)[0];
if (auto ie = (*exp.arguments)[0].isIdentifierExp())
@@ -4638,15 +4663,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
UnaExp ue = cast(UnaExp)exp.e1;
- Expression ue1 = ue.e1;
- Expression ue1old = ue1; // need for 'right this' check
- VarDeclaration v;
- if (ue1.op == TOK.variable && (v = (cast(VarExp)ue1).var.isVarDeclaration()) !is null && v.needThis())
- {
- ue.e1 = new TypeExp(ue1.loc, ue1.type);
- ue1 = null;
- }
-
+ Expression ue1old = ue.e1; // need for 'right this' check
DotVarExp dve;
DotTemplateExp dte;
Dsymbol s;
@@ -4665,7 +4682,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
// Do overload resolution
- exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue1 ? ue1.type : null, exp.arguments, FuncResolveFlag.standard);
+ exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue.e1.type, exp.arguments, FuncResolveFlag.standard);
if (!exp.f || exp.f.errors || exp.f.type.ty == Terror)
return setError();
@@ -4677,7 +4694,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
auto ad2 = b.sym;
ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod));
ue.e1 = ue.e1.expressionSemantic(sc);
- ue1 = ue.e1;
auto vi = exp.f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.dim);
assert(vi >= 0);
exp.f = ad2.vtbl[vi].isFuncDeclaration();
@@ -6036,17 +6052,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
{
- auto readResult = File.read(name);
- if (!readResult.success)
+ auto fileName = FileName(name.toDString);
+ if (auto fmResult = FileManager.fileManager.lookup(fileName))
{
- e.error("cannot read file `%s`", name);
- return setError();
+ se = new StringExp(e.loc, fmResult.data);
}
else
{
- // take ownership of buffer (probably leaking)
- auto data = readResult.extractSlice();
- se = new StringExp(e.loc, data);
+ auto readResult = File.read(name);
+ if (!readResult.success)
+ {
+ e.error("cannot read file `%s`", name);
+ return setError();
+ }
+ else
+ {
+ // take ownership of buffer (probably leaking)
+ auto data = readResult.extractSlice();
+ se = new StringExp(e.loc, data);
+
+ FileBuffer* fileBuffer = FileBuffer.create();
+ fileBuffer.data = data;
+ FileManager.fileManager.add(fileName, fileBuffer);
+ }
}
}
result = se.expressionSemantic(sc);
@@ -6357,7 +6385,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
result = e;
return;
}
- exp.type = Type.tnoreturn;
+
+ // Only override the type when it isn't already some flavour of noreturn,
+ // e.g. when this assert was generated by defaultInitLiteral
+ if (!exp.type || !exp.type.isTypeNoreturn())
+ exp.type = Type.tnoreturn;
}
else
exp.type = Type.tvoid;
@@ -6374,12 +6406,53 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
//printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op));
}
+ if (exp.arrow) // ImportC only
+ exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
+
+ if (sc.flags & SCOPE.Cfile)
+ {
+ if (exp.ident == Id.__xalignof && exp.e1.isTypeExp())
+ {
+ // C11 6.5.3 says _Alignof only applies to types
+ Expression e;
+ Type t;
+ Dsymbol s;
+ dmd.typesem.resolve(exp.e1.type, exp.e1.loc, sc, e, t, s, true);
+ if (e)
+ {
+ exp.e1.error("argument to `_Alignof` must be a type");
+ return setError();
+ }
+ else if (t)
+ {
+ result = new IntegerExp(exp.loc, t.alignsize, Type.tsize_t);
+ }
+ else if (s)
+ {
+ exp.e1.error("argument to `_Alignof` must be a type");
+ return setError();
+ }
+ else
+ assert(0);
+ return;
+ }
+ }
+
+ if (sc.flags & SCOPE.Cfile && exp.ident != Id.__sizeof)
+ {
+ result = fieldLookup(exp.e1, sc, exp.ident);
+ return;
+ }
+
Expression e = exp.semanticY(sc, 1);
+
if (e && isDotOpDispatch(e))
{
+ auto ode = e;
uint errors = global.startGagging();
e = resolvePropertiesX(sc, e);
- if (global.endGagging(errors))
+ // Any error or if 'e' is not resolved, go to UFCS
+ if (global.endGagging(errors) || e is ode)
e = null; /* fall down to UFCS */
else
{
@@ -6580,10 +6653,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars());
}
+ if (exp.type)
+ {
+ result = exp;
+ return;
+ }
// Indicate we need to resolve by UFCS.
Expression e = exp.semanticY(sc, 1);
if (!e)
e = resolveUFCSProperties(sc, exp);
+ if (e is exp)
+ e.type = Type.tvoid; // Unresolved type, because it needs inference
result = e;
}
@@ -7908,6 +7988,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
dinteger_t length = el.toInteger();
auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length));
exp.upperIsInBounds = bounds.contains(uprRange);
+ if (exp.lwr.op == TOK.int64 && exp.upr.op == TOK.int64 && exp.lwr.toInteger() > exp.upr.toInteger())
+ {
+ exp.error("in slice `%s[%llu .. %llu]`, lower bound is greater than upper bound", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger());
+ return setError();
+ }
+ if (exp.upr.op == TOK.int64 && exp.upr.toInteger() > length)
+ {
+ exp.error("in slice `%s[%llu .. %llu]`, upper bound is greater than array length `%llu`", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger(), length);
+ return setError();
+ }
}
else if (exp.upr.op == TOK.int64 && exp.upr.toInteger() == 0)
{
@@ -7965,6 +8055,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf("ArrayExp::semantic('%s')\n", exp.toChars());
}
assert(!exp.type);
+
+ result = exp.carraySemantic(sc); // C semantics
+ if (result)
+ return;
+
Expression e = exp.op_overload(sc);
if (e)
{
@@ -8019,6 +8114,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
override void visit(CommaExp e)
{
+ //printf("Semantic.CommaExp() %s\n", e.toChars());
if (e.type)
{
result = e;
@@ -8042,10 +8138,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (checkNonAssignmentArrayOp(e.e1))
return setError();
+ // Comma expressions trigger this conversion
+ e.e2 = e.e2.arrayFuncConv(sc);
+
e.type = e.e2.type;
if (e.type is Type.tvoid)
discardValue(e.e1);
- else if (!e.allowCommaExp && !e.isGenerated)
+ else if (!e.allowCommaExp && !e.isGenerated && !(sc.flags & SCOPE.Cfile))
e.error("Using the result of a comma expression is not allowed");
result = e;
}
@@ -8143,7 +8242,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
// operator overloading should be handled in ArrayExp already.
if (!exp.e1.type)
- exp.e1 = exp.e1.expressionSemantic(sc);
+ exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
assert(exp.e1.type); // semantic() should already be run on it
if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
{
@@ -8191,7 +8290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
if (t1b.ty == Ttuple)
sc = sc.startCTFE();
- exp.e2 = exp.e2.expressionSemantic(sc);
+ exp.e2 = exp.e2.expressionSemantic(sc).arrayFuncConv(sc);
exp.e2 = resolveProperties(sc, exp.e2);
if (t1b.ty == Ttuple)
sc = sc.endCTFE();
@@ -8515,7 +8614,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
if (auto e2comma = exp.e2.isCommaExp())
{
- if (!e2comma.isGenerated)
+ if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile))
exp.error("Using the result of a comma expression is not allowed");
/* Rewrite to get rid of the comma from rvalue
@@ -8659,6 +8758,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
e1x = e;
}
+ else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp())
+ {
+ auto die = e1x.isDotIdExp();
+ e1x = fieldLookup(die.e1, sc, die.ident);
+ }
else if (auto die = e1x.isDotIdExp())
{
Expression e = die.semanticY(sc, 1);
@@ -8672,10 +8776,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
* In order to make sure that UFCS is tried with correct parameters, e2
* needs to have semantic ran on it.
*/
+ auto ode = e;
exp.e2 = exp.e2.expressionSemantic(sc);
uint errors = global.startGagging();
e = resolvePropertiesX(sc, e, exp.e2);
- if (global.endGagging(errors))
+ // Any error or if 'e' is not resolved, go to UFCS
+ if (global.endGagging(errors) || e is ode)
e = null; /* fall down to UFCS */
else
return setResult(e);
@@ -8717,12 +8823,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{
Expression e2x = inferType(exp.e2, t1.baseElemOf());
e2x = e2x.expressionSemantic(sc);
+ if (!t1.isTypeSArray())
+ e2x = e2x.arrayFuncConv(sc);
e2x = resolveProperties(sc, e2x);
if (e2x.op == TOK.type)
e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
if (e2x.op == TOK.error)
return setResult(e2x);
- // We skip checking the value for structs/classes as these might have
+ // We delay checking the value for structs/classes as these might have
// an opAssign defined.
if ((t1.ty != Tstruct && t1.ty != Tclass && e2x.checkValue()) ||
e2x.checkSharedAccess(sc))
@@ -9208,6 +9316,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
else
assert(exp.op == TOK.blit);
+ if (e2x.checkValue())
+ return setError();
+
exp.e1 = e1x;
exp.e2 = e2x;
}
@@ -9223,6 +9334,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
}
+ if (exp.e2.checkValue())
+ return setError();
}
else if (t1.ty == Tsarray)
{
@@ -9672,7 +9785,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
exp.type = exp.e1.type;
assert(exp.type);
auto res = exp.op == TOK.assign ? exp.reorderSettingAAElem(sc) : exp;
- checkAssignEscape(sc, res, false);
+ Expression tmp;
+ /* https://issues.dlang.org/show_bug.cgi?id=22366
+ *
+ * `reorderSettingAAElem` creates a tree of comma expressions, however,
+ * `checkAssignExp` expects only AssignExps.
+ */
+ checkAssignEscape(sc, Expression.extractLast(res, tmp), false);
return setResult(res);
}
@@ -9944,6 +10063,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
+ /* ImportC: convert arrays to pointers, functions to pointers to functions
+ */
+ exp.e1 = exp.e1.arrayFuncConv(sc);
+ exp.e2 = exp.e2.arrayFuncConv(sc);
+
Type tb1 = exp.e1.type.toBasetype();
Type tb2 = exp.e2.type.toBasetype();
@@ -10045,6 +10169,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
+ /* ImportC: convert arrays to pointers, functions to pointers to functions
+ */
+ exp.e1 = exp.e1.arrayFuncConv(sc);
+ exp.e2 = exp.e2.arrayFuncConv(sc);
+
Type t1 = exp.e1.type.toBasetype();
Type t2 = exp.e2.type.toBasetype();
@@ -11539,12 +11668,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
ec = ec.toBoolean(sc);
CtorFlow ctorflow_root = sc.ctorflow.clone();
- Expression e1x = exp.e1.expressionSemantic(sc);
+ Expression e1x = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
e1x = resolveProperties(sc, e1x);
CtorFlow ctorflow1 = sc.ctorflow;
sc.ctorflow = ctorflow_root;
- Expression e2x = exp.e2.expressionSemantic(sc);
+ Expression e2x = exp.e2.expressionSemantic(sc).arrayFuncConv(sc);
e2x = resolveProperties(sc, e2x);
sc.merge(exp.loc, ctorflow1);
@@ -11894,95 +12023,84 @@ Expression semanticX(DotIdExp exp, Scope* sc)
if (exp.ident == Id._mangleof)
{
// symbol.mangleof
- Dsymbol ds;
- switch (exp.e1.op)
+
+ // return mangleof as an Expression
+ static Expression dotMangleof(const ref Loc loc, Scope* sc, Dsymbol ds)
{
- case TOK.scope_:
- ds = (cast(ScopeExp)exp.e1).sds;
- goto L1;
- case TOK.variable:
- ds = (cast(VarExp)exp.e1).var;
- goto L1;
- case TOK.dotVariable:
- ds = (cast(DotVarExp)exp.e1).var;
- goto L1;
- case TOK.overloadSet:
- ds = (cast(OverExp)exp.e1).vars;
- goto L1;
- case TOK.template_:
- {
- TemplateExp te = cast(TemplateExp)exp.e1;
- ds = te.fd ? cast(Dsymbol)te.fd : te.td;
- }
- L1:
+ assert(ds);
+ if (auto f = ds.isFuncDeclaration())
{
- assert(ds);
- if (auto f = ds.isFuncDeclaration())
+ if (f.checkForwardRef(loc))
+ return ErrorExp.get();
+
+ if (f.flags & (FUNCFLAG.purityInprocess | FUNCFLAG.safetyInprocess |
+ FUNCFLAG.nothrowInprocess | FUNCFLAG.nogcInprocess))
{
- if (f.checkForwardRef(exp.loc))
- {
- return ErrorExp.get();
- }
- if (f.flags & (FUNCFLAG.purityInprocess | FUNCFLAG.safetyInprocess |
- FUNCFLAG.nothrowInprocess | FUNCFLAG.nogcInprocess))
- {
- f.error(exp.loc, "cannot retrieve its `.mangleof` while inferring attributes");
- return ErrorExp.get();
- }
+ f.error(loc, "cannot retrieve its `.mangleof` while inferring attributes");
+ return ErrorExp.get();
}
- OutBuffer buf;
- mangleToBuffer(ds, &buf);
- Expression e = new StringExp(exp.loc, buf.extractSlice());
- e = e.expressionSemantic(sc);
- return e;
}
- default:
- break;
+ OutBuffer buf;
+ mangleToBuffer(ds, &buf);
+ Expression e = new StringExp(loc, buf.extractSlice());
+ return e.expressionSemantic(sc);
+ }
+
+ Dsymbol ds;
+ switch (exp.e1.op)
+ {
+ case TOK.scope_: return dotMangleof(exp.loc, sc, exp.e1.isScopeExp().sds);
+ case TOK.variable: return dotMangleof(exp.loc, sc, exp.e1.isVarExp().var);
+ case TOK.dotVariable: return dotMangleof(exp.loc, sc, exp.e1.isDotVarExp().var);
+ case TOK.overloadSet: return dotMangleof(exp.loc, sc, exp.e1.isOverExp().vars);
+ case TOK.template_:
+ {
+ TemplateExp te = exp.e1.isTemplateExp();
+ return dotMangleof(exp.loc, sc, ds = te.fd ? te.fd.isDsymbol() : te.td);
+ }
+
+ default:
+ break;
}
}
- if (exp.e1.op == TOK.variable && exp.e1.type.toBasetype().ty == Tsarray && exp.ident == Id.length)
+ if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length)
{
// bypass checkPurity
return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0);
}
- if (exp.e1.op == TOK.dot)
- {
- }
- else
+ if (!exp.e1.isDotExp())
{
exp.e1 = resolvePropertiesX(sc, exp.e1);
}
- if (exp.e1.op == TOK.tuple && exp.ident == Id.offsetof)
+
+ if (auto te = exp.e1.isTupleExp())
{
- /* 'distribute' the .offsetof to each of the tuple elements.
- */
- TupleExp te = cast(TupleExp)exp.e1;
- auto exps = new Expressions(te.exps.dim);
- for (size_t i = 0; i < exps.dim; i++)
+ if (exp.ident == Id.offsetof)
{
- Expression e = (*te.exps)[i];
+ /* 'distribute' the .offsetof to each of the tuple elements.
+ */
+ auto exps = new Expressions(te.exps.dim);
+ foreach (i, e; (*te.exps)[])
+ {
+ (*exps)[i] = new DotIdExp(e.loc, e, Id.offsetof);
+ }
+ // Don't evaluate te.e0 in runtime
+ Expression e = new TupleExp(exp.loc, null, exps);
e = e.expressionSemantic(sc);
- e = new DotIdExp(e.loc, e, Id.offsetof);
- (*exps)[i] = e;
+ return e;
+ }
+ if (exp.ident == Id.length)
+ {
+ // Don't evaluate te.e0 in runtime
+ return new IntegerExp(exp.loc, te.exps.dim, Type.tsize_t);
}
- // Don't evaluate te.e0 in runtime
- Expression e = new TupleExp(exp.loc, null, exps);
- e = e.expressionSemantic(sc);
- return e;
- }
- if (exp.e1.op == TOK.tuple && exp.ident == Id.length)
- {
- TupleExp te = cast(TupleExp)exp.e1;
- // Don't evaluate te.e0 in runtime
- Expression e = new IntegerExp(exp.loc, te.exps.dim, Type.tsize_t);
- return e;
}
// https://issues.dlang.org/show_bug.cgi?id=14416
// Template has no built-in properties except for 'stringof'.
- if ((exp.e1.op == TOK.dotTemplateDeclaration || exp.e1.op == TOK.template_) && exp.ident != Id.stringof)
+ if ((exp.e1.isDotTemplateExp() || exp.e1.isTemplateExp()) && exp.ident != Id.stringof)
{
exp.error("template `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars());
return ErrorExp.get();
@@ -11996,8 +12114,15 @@ Expression semanticX(DotIdExp exp, Scope* sc)
return exp;
}
-// Resolve e1.ident without seeing UFCS.
-// If flag == 1, stop "not a property" error and return NULL.
+/******************************
+ * Resolve properties, i.e. `e1.ident`, without seeing UFCS.
+ * Params:
+ * exp = expression to resolve
+ * sc = context
+ * flag = if 1 then do not emit error messages, just return null
+ * Returns:
+ * resolved expression, null if error
+ */
Expression semanticY(DotIdExp exp, Scope* sc, int flag)
{
//printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars());
@@ -12008,32 +12133,35 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
* to be classtype.id and baseclasstype.id
* if we have no this pointer.
*/
- if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && !hasThis(sc))
+ if ((exp.e1.isThisExp() || exp.e1.isSuperExp()) && !hasThis(sc))
{
if (AggregateDeclaration ad = sc.getStructClassScope())
{
- if (exp.e1.op == TOK.this_)
+ if (exp.e1.isThisExp())
{
exp.e1 = new TypeExp(exp.e1.loc, ad.type);
}
else
{
- ClassDeclaration cd = ad.isClassDeclaration();
- if (cd && cd.baseClass)
- exp.e1 = new TypeExp(exp.e1.loc, cd.baseClass.type);
+ if (auto cd = ad.isClassDeclaration())
+ {
+ if (cd.baseClass)
+ exp.e1 = new TypeExp(exp.e1.loc, cd.baseClass.type);
+ }
}
}
}
- Expression e = semanticX(exp, sc);
- if (e != exp)
- return e;
+ {
+ Expression e = semanticX(exp, sc);
+ if (e != exp)
+ return e;
+ }
Expression eleft;
Expression eright;
- if (exp.e1.op == TOK.dot)
+ if (auto de = exp.e1.isDotExp())
{
- DotExp de = cast(DotExp)exp.e1;
eleft = de.e1;
eright = de.e2;
}
@@ -12045,11 +12173,9 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
Type t1b = exp.e1.type.toBasetype();
- if (eright.op == TOK.scope_) // also used for template alias's
+ if (auto ie = eright.isScopeExp()) // also used for template alias's
{
- ScopeExp ie = cast(ScopeExp)eright;
-
- int flags = SearchLocalsOnly;
+ auto flags = SearchLocalsOnly;
/* Disable access to another module's private imports.
* The check for 'is sds our current module' is because
* the current module should have access to its own imports.
@@ -12082,13 +12208,11 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
exp.checkDeprecated(sc, s);
exp.checkDisabled(sc, s);
- EnumMember em = s.isEnumMember();
- if (em)
+ if (auto em = s.isEnumMember())
{
return em.getVarExp(exp.loc, sc);
}
- VarDeclaration v = s.isVarDeclaration();
- if (v)
+ if (auto v = s.isVarDeclaration())
{
//printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v.type.toChars());
if (!v.type ||
@@ -12100,7 +12224,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
exp.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
- if (v.type.ty == Terror)
+ if (v.type.isTypeError())
return ErrorExp.get();
if ((v.storage_class & STC.manifest) && v._init && !exp.wantsym)
@@ -12114,13 +12238,14 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
error(exp.loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars());
return ErrorExp.get();
}
- e = v.expandInitializer(exp.loc);
+ auto e = v.expandInitializer(exp.loc);
v.inuse++;
e = e.expressionSemantic(sc);
v.inuse--;
return e;
}
+ Expression e;
if (v.needThis())
{
if (!eleft)
@@ -12141,12 +12266,12 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
return e.expressionSemantic(sc);
}
- FuncDeclaration f = s.isFuncDeclaration();
- if (f)
+ if (auto f = s.isFuncDeclaration())
{
//printf("it's a function\n");
if (!f.functionSemantic())
return ErrorExp.get();
+ Expression e;
if (f.needThis())
{
if (!eleft)
@@ -12167,6 +12292,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
}
if (auto td = s.isTemplateDeclaration())
{
+ Expression e;
if (eleft)
e = new DotTemplateExp(exp.loc, eleft, td);
else
@@ -12176,7 +12302,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
}
if (OverDeclaration od = s.isOverDeclaration())
{
- e = new VarExp(exp.loc, od, true);
+ Expression e = new VarExp(exp.loc, od, true);
if (eleft)
{
e = new CommaExp(exp.loc, eleft, e);
@@ -12184,8 +12310,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
}
return e;
}
- OverloadSet o = s.isOverloadSet();
- if (o)
+ if (auto o = s.isOverloadSet())
{
//printf("'%s' is an overload set\n", o.toChars());
return new OverExp(exp.loc, o);
@@ -12196,36 +12321,33 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
return (new TypeExp(exp.loc, t)).expressionSemantic(sc);
}
- TupleDeclaration tup = s.isTupleDeclaration();
- if (tup)
+ if (auto tup = s.isTupleDeclaration())
{
if (eleft)
{
- e = new DotVarExp(exp.loc, eleft, tup);
+ Expression e = new DotVarExp(exp.loc, eleft, tup);
e = e.expressionSemantic(sc);
return e;
}
- e = new TupleExp(exp.loc, tup);
+ Expression e = new TupleExp(exp.loc, tup);
e = e.expressionSemantic(sc);
return e;
}
- ScopeDsymbol sds = s.isScopeDsymbol();
- if (sds)
+ if (auto sds = s.isScopeDsymbol())
{
//printf("it's a ScopeDsymbol %s\n", ident.toChars());
- e = new ScopeExp(exp.loc, sds);
+ Expression e = new ScopeExp(exp.loc, sds);
e = e.expressionSemantic(sc);
if (eleft)
e = new DotExp(exp.loc, eleft, e);
return e;
}
- Import imp = s.isImport();
- if (imp)
+ if (auto imp = s.isImport())
{
- ie = new ScopeExp(exp.loc, imp.pkg);
- return ie.expressionSemantic(sc);
+ Expression se = new ScopeExp(exp.loc, imp.pkg);
+ return se.expressionSemantic(sc);
}
// BUG: handle other cases like in IdentifierExp::semantic()
debug
@@ -12236,7 +12358,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
}
else if (exp.ident == Id.stringof)
{
- e = new StringExp(exp.loc, ie.toString());
+ Expression e = new StringExp(exp.loc, ie.toString());
e = e.expressionSemantic(sc);
return e;
}
@@ -12263,9 +12385,11 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
Type t1bn = t1b.nextOf();
if (flag)
{
- AggregateDeclaration ad = isAggregate(t1bn);
- if (ad && !ad.members) // https://issues.dlang.org/show_bug.cgi?id=11312
- return null;
+ if (AggregateDeclaration ad = isAggregate(t1bn))
+ {
+ if (!ad.members) // https://issues.dlang.org/show_bug.cgi?id=11312
+ return null;
+ }
}
/* Rewrite:
@@ -12275,27 +12399,27 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag)
*/
if (flag && t1bn.ty == Tvoid)
return null;
- e = new PtrExp(exp.loc, exp.e1);
+ Expression e = new PtrExp(exp.loc, exp.e1);
e = e.expressionSemantic(sc);
return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
}
else if (exp.ident == Id.__xalignof &&
exp.e1.isVarExp() &&
exp.e1.isVarExp().var.isVarDeclaration() &&
- exp.e1.isVarExp().var.isVarDeclaration().alignment)
+ !exp.e1.isVarExp().var.isVarDeclaration().alignment.isUnknown())
{
// For `x.alignof` get the alignment of the variable, not the alignment of its type
const explicitAlignment = exp.e1.isVarExp().var.isVarDeclaration().alignment;
const naturalAlignment = exp.e1.type.alignsize();
- const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment);
- e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
+ const actualAlignment = explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get();
+ Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t);
return e;
}
else
{
- if (exp.e1.op == TOK.type || exp.e1.op == TOK.template_)
+ if (exp.e1.isTypeExp() || exp.e1.isTemplateExp())
flag = 0;
- e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
+ Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0));
if (e)
e = e.expressionSemantic(sc);
return e;
@@ -12875,7 +12999,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
const hasPointers = tb.hasPointers();
if (hasPointers)
{
- if ((stype.alignment() < target.ptrsize ||
+ if ((!stype.alignment.isDefault() && stype.alignment.get() < target.ptrsize ||
(v.offset & (target.ptrsize - 1))) &&
(sc.func && sc.func.setUnsafe()))
{
new file mode 100644
@@ -0,0 +1,301 @@
+/**
+ * Read a file from disk and store it in memory.
+ *
+ * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d, _file_manager.d)
+ * Documentation: https://dlang.org/phobos/dmd_file_manager.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/file_manager.d
+ */
+
+module dmd.file_manager;
+
+import dmd.root.stringtable : StringTable;
+import dmd.root.file : File, FileBuffer;
+import dmd.root.filename : FileName;
+import dmd.root.string : toDString;
+import dmd.globals;
+import dmd.identifier;
+
+enum package_d = "package." ~ mars_ext;
+enum package_di = "package." ~ hdr_ext;
+
+extern(C++) struct FileManager
+{
+ private StringTable!(FileBuffer*) files;
+ private __gshared bool initialized = false;
+
+nothrow:
+ extern(D) private FileBuffer* readToFileBuffer(const(char)[] filename)
+ {
+ if (!initialized)
+ FileManager._init();
+
+ auto readResult = File.read(filename);
+ if (readResult.success)
+ {
+ FileBuffer* fb;
+ if (auto val = files.lookup(filename))
+ fb = val.value;
+
+ if (!fb)
+ fb = FileBuffer.create();
+
+ fb.data = readResult.extractSlice();
+
+ return files.insert(filename, fb) == null ? null : fb;
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ /********************************************
+ * Look for the source file if it's different from filename.
+ * Look for .di, .d, directory, and along global.path.
+ * Does not open the file.
+ * Params:
+ * filename = as supplied by the user
+ * path = path to look for filename
+ * Returns:
+ * the found file name or
+ * `null` if it is not different from filename.
+ */
+ extern(D) static const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
+ {
+ //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
+ /* Search along path[] for .di file, then .d file, then .i file, then .c file.
+ */
+ const sdi = FileName.forceExt(filename, hdr_ext);
+ if (FileName.exists(sdi) == 1)
+ return sdi;
+ scope(exit) FileName.free(sdi.ptr);
+
+ const sd = FileName.forceExt(filename, mars_ext);
+ if (FileName.exists(sd) == 1)
+ return sd;
+ scope(exit) FileName.free(sd.ptr);
+
+ const si = FileName.forceExt(filename, i_ext);
+ if (FileName.exists(si) == 1)
+ return si;
+ scope(exit) FileName.free(si.ptr);
+
+ const sc = FileName.forceExt(filename, c_ext);
+ if (FileName.exists(sc) == 1)
+ return sc;
+ scope(exit) FileName.free(sc.ptr);
+
+ if (FileName.exists(filename) == 2)
+ {
+ /* The filename exists and it's a directory.
+ * Therefore, the result should be: filename/package.d
+ * iff filename/package.d is a file
+ */
+ const ni = FileName.combine(filename, package_di);
+ if (FileName.exists(ni) == 1)
+ return ni;
+ FileName.free(ni.ptr);
+
+ const n = FileName.combine(filename, package_d);
+ if (FileName.exists(n) == 1)
+ return n;
+ FileName.free(n.ptr);
+ }
+ if (FileName.absolute(filename))
+ return null;
+ if (!path.length)
+ return null;
+ foreach (entry; path)
+ {
+ const p = entry.toDString();
+
+ const(char)[] n = FileName.combine(p, sdi);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ n = FileName.combine(p, sd);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ n = FileName.combine(p, si);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ n = FileName.combine(p, sc);
+ if (FileName.exists(n) == 1) {
+ return n;
+ }
+ FileName.free(n.ptr);
+
+ const b = FileName.removeExt(filename);
+ n = FileName.combine(p, b);
+ FileName.free(b.ptr);
+ if (FileName.exists(n) == 2)
+ {
+ const n2i = FileName.combine(n, package_di);
+ if (FileName.exists(n2i) == 1)
+ return n2i;
+ FileName.free(n2i.ptr);
+ const n2 = FileName.combine(n, package_d);
+ if (FileName.exists(n2) == 1) {
+ return n2;
+ }
+ FileName.free(n2.ptr);
+ }
+ FileName.free(n.ptr);
+ }
+ return null;
+ }
+
+ /**
+ * Looks up the given filename from the internal file buffer table.
+ * If the file does not already exist within the table, it will be read from the filesystem.
+ * If it has been read before,
+ *
+ * Returns: the loaded source file if it was found in memory,
+ * otherwise `null`
+ */
+ extern(D) FileBuffer* lookup(FileName filename)
+ {
+ if (!initialized)
+ FileManager._init();
+
+ if (auto val = files.lookup(filename.toString))
+ {
+ // There is a chance that the buffer could've been
+ // stolen by a reader with extractSlice, so we should
+ // try and do our reading logic if that happens.
+ if (val !is null && val.value.data !is null)
+ {
+ return val.value;
+ }
+ }
+
+ const name = filename.toString;
+ auto res = FileName.exists(name);
+ if (res == 1)
+ return readToFileBuffer(name);
+
+ const fullName = lookForSourceFile(name, global.path ? (*global.path)[] : null);
+ if (!fullName)
+ return null;
+
+ return readToFileBuffer(fullName);
+ }
+
+ extern(C++) FileBuffer* lookup(const(char)* filename)
+ {
+ return lookup(FileName(filename.toDString));
+ }
+
+ /**
+ * Looks up the given filename from the internal file buffer table, and returns the lines within the file.
+ * If the file does not already exist within the table, it will be read from the filesystem.
+ * If it has been read before,
+ *
+ * Returns: the loaded source file if it was found in memory,
+ * otherwise `null`
+ */
+ extern(D) const(char)[][] getLines(FileName file)
+ {
+ if (!initialized)
+ FileManager._init();
+
+ const(char)[][] lines;
+ if (FileBuffer* buffer = lookup(file))
+ {
+ ubyte[] slice = buffer.data[0 .. buffer.data.length];
+ size_t start, end;
+ ubyte c;
+ for (auto i = 0; i < slice.length; i++)
+ {
+ c = slice[i];
+ if (c == '\n' || c == '\r')
+ {
+ if (i != 0)
+ {
+ end = i;
+ lines ~= cast(const(char)[])slice[start .. end];
+ }
+ // Check for Windows-style CRLF newlines
+ if (c == '\r')
+ {
+ if (slice.length > i + 1 && slice[i + 1] == '\n')
+ {
+ // This is a CRLF sequence, skip over two characters
+ start = i + 2;
+ i++;
+ }
+ else
+ {
+ // Just a CR sequence
+ start = i + 1;
+ }
+ }
+ else
+ {
+ // The next line should start after the LF sequence
+ start = i + 1;
+ }
+ }
+ }
+
+ if (slice[$ - 1] != '\r' && slice[$ - 1] != '\n')
+ {
+ end = slice.length;
+ lines ~= cast(const(char)[])slice[start .. end];
+ }
+ }
+
+ return lines;
+ }
+
+ /**
+ * Adds a FileBuffer to the table.
+ *
+ * Returns: The FileBuffer added, or null
+ */
+ extern(D) FileBuffer* add(FileName filename, FileBuffer* filebuffer)
+ {
+ if (!initialized)
+ FileManager._init();
+
+ auto val = files.insert(filename.toString, filebuffer);
+ return val == null ? null : val.value;
+ }
+
+ extern(C++) FileBuffer* add(const(char)* filename, FileBuffer* filebuffer)
+ {
+ if (!initialized)
+ FileManager._init();
+
+ auto val = files.insert(filename.toDString, filebuffer);
+ return val == null ? null : val.value;
+ }
+
+ __gshared fileManager = FileManager();
+
+ // Initialize the global FileManager singleton
+ extern(C++) static __gshared void _init()
+ {
+ if (!initialized)
+ {
+ fileManager.initialize();
+ initialized = true;
+ }
+ }
+
+ void initialize()
+ {
+ files._init();
+ }
+}
similarity index 50%
rename from gcc/d/dmd/root/root.h
rename to gcc/d/dmd/file_manager.h
@@ -4,17 +4,16 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
- * https://github.com/dlang/dmd/blob/master/src/dmd/root/root.h
+ * https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.h
*/
#pragma once
-#include "object.h"
+#include "root/file.h"
-#include "filename.h"
-
-#include "file.h"
-
-#include "outbuffer.h"
-
-#include "array.h"
+struct FileManager
+{
+ static void _init();
+ FileBuffer* lookup(const char* filename);
+ FileBuffer* add(const char* filename, FileBuffer* filebuffer);
+};
@@ -45,7 +45,8 @@ import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.objc;
-import dmd.root.outbuffer;
+import dmd.root.aav;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.root.stringtable;
@@ -261,6 +262,8 @@ extern (C++) class FuncDeclaration : Declaration
VarDeclaration vresult; /// result variable for out contracts
LabelDsymbol returnLabel; /// where the return goes
+ bool[size_t] isTypeIsolatedCache; /// cache for the potentially very expensive isTypeIsolated check
+
// used to prevent symbols in different
// scopes from having the same name
DsymbolTable localsymtab;
@@ -740,7 +743,7 @@ extern (C++) class FuncDeclaration : Declaration
*/
final BaseClass* overrideInterface()
{
- if (ClassDeclaration cd = toParent2().isClassDeclaration())
+ for (ClassDeclaration cd = toParent2().isClassDeclaration(); cd; cd = cd.baseClass)
{
foreach (b; cd.interfaces)
{
@@ -1529,8 +1532,23 @@ extern (C++) class FuncDeclaration : Declaration
extern (D) final bool isTypeIsolated(Type t)
{
StringTable!Type parentTypes;
- parentTypes._init();
- return isTypeIsolated(t, parentTypes);
+ const uniqueTypeID = t.getUniqueID();
+ if (uniqueTypeID)
+ {
+ const cacheResultPtr = uniqueTypeID in isTypeIsolatedCache;
+ if (cacheResultPtr !is null)
+ return *cacheResultPtr;
+
+ parentTypes._init();
+ const isIsolated = isTypeIsolated(t, parentTypes);
+ isTypeIsolatedCache[uniqueTypeID] = isIsolated;
+ return isIsolated;
+ }
+ else
+ {
+ parentTypes._init();
+ return isTypeIsolated(t, parentTypes);
+ }
}
///ditto
@@ -2593,9 +2611,10 @@ extern (C++) class FuncDeclaration : Declaration
}
if (!tf.nextOf())
- error("must return `int` or `void`");
- else if (tf.nextOf().ty != Tint32 && tf.nextOf().ty != Tvoid)
- error("must return `int` or `void`, not `%s`", tf.nextOf().toChars());
+ // auto main(), check after semantic
+ assert(this.inferRetType);
+ else if (tf.nextOf().ty != Tint32 && tf.nextOf().ty != Tvoid && tf.nextOf().ty != Tnoreturn)
+ error("must return `int`, `void` or `noreturn`, not `%s`", tf.nextOf().toChars());
else if (tf.parameterList.varargs || nparams >= 2 || argerr)
error("parameters must be `main()` or `main(string[] args)`");
}
@@ -3054,7 +3073,11 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
return null;
bool hasOverloads = fd.overnext !is null;
- auto tf = fd.type.toTypeFunction();
+ auto tf = fd.type.isTypeFunction();
+ // if type is an error, the original type should be there for better diagnostics
+ if (!tf)
+ tf = fd.originalType.toTypeFunction();
+
if (tthis && !MODimplicitConv(tthis.mod, tf.mod)) // modifier mismatch
{
OutBuffer thisBuf, funcBuf;
@@ -3253,17 +3276,7 @@ private bool traverseIndirections(Type ta, Type tb)
{
//printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars());
- /* Threaded list of aggregate types already examined,
- * used to break cycles.
- * Cycles in type graphs can only occur with aggregates.
- */
- static struct Ctxt
- {
- Ctxt* prev;
- Type type; // an aggregate type
- }
-
- static bool traverse(Type ta, Type tb, Ctxt* ctxt, bool reversePass)
+ static bool traverse(Type ta, Type tb, ref scope AssocArray!(const(char)*, bool) table, bool reversePass)
{
//printf("traverse(%s, %s)\n", ta.toChars(), tb.toChars());
ta = ta.baseElemOf();
@@ -3293,28 +3306,27 @@ private bool traverseIndirections(Type ta, Type tb)
if (tb.ty == Tclass || tb.ty == Tstruct)
{
- for (Ctxt* c = ctxt; c; c = c.prev)
- if (tb == c.type)
- return true;
- Ctxt c;
- c.prev = ctxt;
- c.type = tb;
-
/* Traverse the type of each field of the aggregate
*/
+ bool* found = table.getLvalue(tb.deco);
+ if (*found == true)
+ return true; // We have already seen this symbol, break the cycle
+ else
+ *found = true;
+
AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration();
foreach (v; sym.fields)
{
Type tprmi = v.type.addMod(tb.mod);
//printf("\ttb = %s, tprmi = %s\n", tb.toChars(), tprmi.toChars());
- if (!traverse(ta, tprmi, &c, reversePass))
+ if (!traverse(ta, tprmi, table, reversePass))
return false;
}
}
else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer)
{
Type tind = tb.nextOf();
- if (!traverse(ta, tind, ctxt, reversePass))
+ if (!traverse(ta, tind, table, reversePass))
return false;
}
else if (tb.hasPointers())
@@ -3325,7 +3337,10 @@ private bool traverseIndirections(Type ta, Type tb)
// Still no match, so try breaking up ta if we have not done so yet.
if (!reversePass)
- return traverse(tb, ta, ctxt, true);
+ {
+ scope newTable = AssocArray!(const(char)*, bool)();
+ return traverse(tb, ta, newTable, true);
+ }
return true;
}
@@ -3333,7 +3348,8 @@ private bool traverseIndirections(Type ta, Type tb)
// To handle arbitrary levels of indirections in both parameters, we
// recursively descend into aggregate members/levels of indirection in both
// `ta` and `tb` while avoiding cycles. Start with the original types.
- const result = traverse(ta, tb, null, false);
+ scope table = AssocArray!(const(char)*, bool)();
+ const result = traverse(ta, tb, table, false);
//printf(" returns %d\n", result);
return result;
}
@@ -14,7 +14,7 @@ module dmd.globals;
import core.stdc.stdint;
import dmd.root.array;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.identifier;
/// Defines a setting for how compiler warnings and deprecations are handled
@@ -118,6 +118,7 @@ extern (C++) struct Param
bool vgc; // identify gc usage
bool vfield; // identify non-mutable field variables
bool vcomplex = true; // identify complex/imaginary type usage
+ bool vin; // identify 'in' parameters
ubyte symdebug; // insert debug symbolic information
bool symdebugref; // insert debug information for all referenced types, too
bool optimize; // run optimizer
@@ -261,11 +262,29 @@ extern (C++) struct Param
const(char)[] mapfile;
}
-alias structalign_t = uint;
+extern (C++) struct structalign_t
+{
+ private:
+ ushort value = 0; // unknown
+ enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does
+ bool pack; // use #pragma pack semantics
+
+ public:
+ pure @safe @nogc nothrow:
+ bool isDefault() const { return value == STRUCTALIGN_DEFAULT; }
+ void setDefault() { value = STRUCTALIGN_DEFAULT; }
+ bool isUnknown() const { return value == 0; } // value is not set
+ 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; }
+}
+//alias structalign_t = uint;
// magic value means "match whatever the underlying C compiler does"
// other values are all powers of 2
-enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
+//enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
enum mars_ext = "d"; // for D source files
enum doc_ext = "html"; // for Ddoc generated files
@@ -307,6 +326,8 @@ extern (C++) struct Global
Array!Identifier* versionids; /// command line versions and predefined versions
Array!Identifier* debugids; /// command line debug versions and predefined versions
+ bool hasMainFunction; /// Whether a main function has already been compiled in (for -main switch)
+
enum recursionLimit = 500; /// number of recursive template expansions before abort
nothrow:
@@ -12,7 +12,7 @@
#include "root/dcompat.h"
#include "root/ctfloat.h"
-#include "root/outbuffer.h"
+#include "common/outbuffer.h"
#include "root/filename.h"
#include "compiler.h"
@@ -107,6 +107,7 @@ struct Param
bool vgc; // identify gc usage
bool vfield; // identify non-mutable field variables
bool vcomplex; // identify complex/imaginary type usage
+ bool vin; // identify 'in' parameters
unsigned char symdebug; // insert debug symbolic information
bool symdebugref; // insert debug information for all referenced types, too
bool optimize; // run optimizer
@@ -238,10 +239,24 @@ struct Param
DString mapfile;
};
-typedef unsigned structalign_t;
+struct structalign_t
+{
+ unsigned short value;
+ bool pack;
+
+ bool isDefault() const;
+ void setDefault();
+ bool isUnknown() const;
+ void setUnknown();
+ void set(unsigned value);
+ unsigned get() const;
+ bool isPack() const;
+ void setPack(bool pack);
+};
+
// magic value means "match whatever the underlying C compiler does"
// other values are all powers of 2
-#define STRUCTALIGN_DEFAULT ((structalign_t) ~0)
+//#define STRUCTALIGN_DEFAULT ((structalign_t) ~0)
const DString mars_ext = "d";
const DString doc_ext = "html"; // for Ddoc generated files
@@ -274,6 +289,8 @@ struct Global
Array<class Identifier*>* versionids; // command line versions and predefined versions
Array<class Identifier*>* debugids; // command line debug versions and predefined versions
+ bool hasMainFunction;
+
/* Start gagging. Return the current number of gagged errors
*/
unsigned startGagging();
@@ -78,6 +78,7 @@ else version (IN_GCC)
extern (C++)
{
Statement asmSemantic(AsmStatement s, Scope* sc);
+ void toObjFile(Dsymbol ds, bool multiobj);
}
// stubs
@@ -44,7 +44,7 @@ import dmd.mtype;
import dmd.nspace;
import dmd.parse;
import dmd.root.ctfloat;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.statement;
@@ -909,6 +909,12 @@ public:
override void visit(AttribDeclaration d)
{
+ bool hasSTC;
+ if (auto stcd = d.isStorageClassDeclaration)
+ {
+ hasSTC = stcToBuffer(buf, stcd.stc);
+ }
+
if (!d.decl)
{
buf.writeByte(';');
@@ -918,10 +924,12 @@ public:
if (d.decl.dim == 0 || (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration()))
{
// hack for bugzilla 8081
+ if (hasSTC) buf.writeByte(' ');
buf.writestring("{}");
}
else if (d.decl.dim == 1)
{
+ if (hasSTC) buf.writeByte(' ');
(*d.decl)[0].accept(this);
return;
}
@@ -941,8 +949,6 @@ public:
override void visit(StorageClassDeclaration d)
{
- if (stcToBuffer(buf, d.stc))
- buf.writeByte(' ');
visit(cast(AttribDeclaration)d);
}
@@ -1324,11 +1330,10 @@ public:
if (d.ident)
{
buf.writestring(d.ident.toString());
- buf.writeByte(' ');
}
if (d.memtype)
{
- buf.writestring(": ");
+ buf.writestring(" : ");
typeToBuffer(d.memtype, null, buf, hgs);
}
if (!d.members)
@@ -2362,7 +2367,10 @@ public:
override void visit(DotIdExp e)
{
expToBuffer(e.e1, PREC.primary, buf, hgs);
- buf.writeByte('.');
+ if (e.arrow)
+ buf.writestring("->");
+ else
+ buf.writeByte('.');
buf.writestring(e.ident.toString());
}
@@ -300,7 +300,7 @@ Ldone:
* Returns:
* the completed gcc asm statement, or null if errors occurred
*/
-public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
+extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
{
//printf("GccAsmStatement.semantic()\n");
scope p = new Parser!ASTCodegen(sc._module, ";", false);
@@ -500,6 +500,17 @@ immutable Msgtable[] msgtable =
{ "vector_size" },
{ "__func__" },
{ "noreturn" },
+ { "__pragma", "pragma" },
+ { "builtin_va_list", "__builtin_va_list" },
+ { "builtin_va_start", "__builtin_va_start" },
+ { "builtin_va_arg", "__builtin_va_arg" },
+ { "builtin_va_copy", "__builtin_va_copy" },
+ { "builtin_va_end", "__builtin_va_end" },
+ { "va_list_tag", "__va_list_tag" },
+ { "pack" },
+ { "show" },
+ { "push" },
+ { "pop" },
];
@@ -16,7 +16,7 @@ import core.stdc.stdio;
import core.stdc.string;
import dmd.globals;
import dmd.id;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.root.stringtable;
new file mode 100644
@@ -0,0 +1,171 @@
+/**
+ * Contains semantic routines specific to ImportC
+ *
+ * Specification: C11
+ *
+ * Copyright: Copyright (C) 2021 by The D Language Foundation, All Rights Reserved
+ * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/importc.d, _importc.d)
+ * Documentation: https://dlang.org/phobos/dmd_importc.html
+ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/importc.d
+ */
+
+module dmd.importc;
+
+import core.stdc.stdio;
+
+import dmd.dcast;
+import dmd.dscope;
+import dmd.dsymbol;
+import dmd.expression;
+import dmd.expressionsem;
+import dmd.identifier;
+import dmd.mtype;
+
+/**************************************
+ * C11 does not allow array or function parameters.
+ * Hence, adjust those types per C11 6.7.6.3 rules.
+ * Params:
+ * t = parameter type to adjust
+ * sc = context
+ * Returns:
+ * adjusted type
+ */
+Type cAdjustParamType(Type t, Scope* sc)
+{
+ if (!(sc.flags & SCOPE.Cfile))
+ return t;
+
+ Type tb = t.toBasetype();
+
+ /* C11 6.7.6.3-7 array of T is converted to pointer to T
+ */
+ if (auto ta = tb.isTypeDArray())
+ {
+ t = ta.next.pointerTo();
+ }
+ else if (auto ts = tb.isTypeSArray())
+ {
+ t = ts.next.pointerTo();
+ }
+ /* C11 6.7.6.3-8 function is converted to pointer to function
+ */
+ else if (tb.isTypeFunction())
+ {
+ t = tb.pointerTo();
+ }
+ return t;
+}
+
+/***********************************************
+ * C11 6.3.2.1-3 Convert expression that is an array of type to a pointer to type.
+ * C11 6.3.2.1-4 Convert expression that is a function to a pointer to a function.
+ * Params:
+ * e = ImportC expression to possibly convert
+ * sc = context
+ * Returns:
+ * converted expression
+ */
+Expression arrayFuncConv(Expression e, Scope* sc)
+{
+ //printf("arrayFuncConv() %s\n", e.toChars());
+ if (!(sc.flags & SCOPE.Cfile))
+ return e;
+
+ auto t = e.type.toBasetype();
+ if (auto ta = t.isTypeDArray())
+ {
+ e = e.castTo(sc, ta.next.pointerTo());
+ }
+ else if (auto ts = t.isTypeSArray())
+ {
+ e = e.castTo(sc, ts.next.pointerTo());
+ }
+ else if (t.isTypeFunction())
+ {
+ e = e.addressOf();
+ }
+ else
+ return e;
+ return e.expressionSemantic(sc);
+}
+
+/****************************************
+ * Run semantic on `e`.
+ * Expression `e` evaluates to an instance of a struct.
+ * Look up `ident` as a field of that struct.
+ * Params:
+ * e = evaluates to an instance of a struct
+ * sc = context
+ * id = identifier of a field in that struct
+ * Returns:
+ * if successful `e.ident`
+ * if not then `ErrorExp` and message is printed
+ */
+Expression fieldLookup(Expression e, Scope* sc, Identifier id)
+{
+ e = e.expressionSemantic(sc);
+ if (e.isErrorExp())
+ return e;
+
+ Dsymbol s;
+ auto t = e.type;
+ if (t.isTypePointer())
+ {
+ t = t.isTypePointer().next;
+ e = new PtrExp(e.loc, e);
+ }
+ if (auto ts = t.isTypeStruct())
+ s = ts.sym.search(e.loc, id, 0);
+ if (!s)
+ {
+ e.error("`%s` is not a member of `%s`", id.toChars(), t.toChars());
+ return ErrorExp.get();
+ }
+ Expression ef = new DotVarExp(e.loc, e, s.isDeclaration());
+ return ef.expressionSemantic(sc);
+}
+
+/****************************************
+ * C11 6.5.2.1-2
+ * Apply C semantics to `E[I]` expression.
+ * E1[E2] is lowered to *(E1 + E2)
+ * Params:
+ * ae = ArrayExp to run semantics on
+ * sc = context
+ * Returns:
+ * Expression if this was a C expression with completed semantic, null if not
+ */
+Expression carraySemantic(ArrayExp ae, Scope* sc)
+{
+ if (!(sc.flags & SCOPE.Cfile))
+ return null;
+
+ auto e1 = ae.e1.expressionSemantic(sc);
+
+ assert(ae.arguments.length == 1);
+ Expression e2 = (*ae.arguments)[0];
+
+ /* CTFE cannot do pointer arithmetic, but it can index arrays.
+ * So, rewrite as an IndexExp if we can.
+ */
+ auto t1 = e1.type.toBasetype();
+ if (t1.isTypeDArray() || t1.isTypeSArray())
+ {
+ e2 = e2.expressionSemantic(sc).arrayFuncConv(sc);
+ return new IndexExp(ae.loc, e1, e2).expressionSemantic(sc);
+ }
+
+ e1 = e1.arrayFuncConv(sc); // e1 might still be a function call
+ e2 = e2.expressionSemantic(sc);
+ auto t2 = e2.type.toBasetype();
+ if (t2.isTypeDArray() || t2.isTypeSArray())
+ {
+ return new IndexExp(ae.loc, e2, e1).expressionSemantic(sc); // swap operands
+ }
+
+ e2 = e2.arrayFuncConv(sc);
+ auto ep = new PtrExp(ae.loc, new AddExp(ae.loc, e1, e2));
+ return ep.expressionSemantic(sc);
+}
@@ -23,7 +23,7 @@ import dmd.globals;
import dmd.hdrgen;
import dmd.identifier;
import dmd.mtype;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.tokens;
import dmd.visitor;
@@ -31,6 +31,7 @@ import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
+import dmd.importc;
import dmd.init;
import dmd.mtype;
import dmd.opover;
@@ -176,30 +177,35 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
break;
}
}
- else if (fieldi >= nfields)
+ if (j >= nfields)
{
- error(i.loc, "too many initializers for `%s`", sd.toChars());
+ error(i.value[j].loc, "too many initializers for `%s`", sd.toChars());
return err();
}
VarDeclaration vd = sd.fields[fieldi];
if (elems[fieldi])
{
- error(i.loc, "duplicate initializer for field `%s`", vd.toChars());
+ error(i.value[j].loc, "duplicate initializer for field `%s`", vd.toChars());
errors = true;
+ elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
+ ++fieldi;
continue;
}
// Check for @safe violations
if (vd.type.hasPointers)
{
- if ((t.alignment() < target.ptrsize ||
+ if ((!t.alignment.isDefault() && t.alignment.get() < target.ptrsize ||
(vd.offset & (target.ptrsize - 1))) &&
sc.func && sc.func.setUnsafe())
{
- error(i.loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
+ error(i.value[j].loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
sd.toChars(), vd.toChars());
errors = true;
+ elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
+ ++fieldi;
+ continue;
}
}
@@ -208,7 +214,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
{
if (vd.isOverlappedWith(v2) && elems[k])
{
- error(i.loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
+ error(elems[k].loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
errors = true;
continue;
}
@@ -222,6 +228,8 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
if (ex.op == TOK.error)
{
errors = true;
+ elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
+ ++fieldi;
continue;
}
@@ -363,10 +371,10 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
if (length > i.dim)
i.dim = length;
}
- if (t.ty == Tsarray)
+ if (auto tsa = t.isTypeSArray())
{
- uinteger_t edim = (cast(TypeSArray)t).dim.toInteger();
- if (i.dim > edim)
+ uinteger_t edim = tsa.dim.toInteger();
+ if (i.dim > edim && !(tsa.isIncomplete() && (sc.flags & SCOPE.Cfile)))
{
error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
return err();
@@ -398,6 +406,13 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
if (i.exp.op == TOK.error)
return err();
uint olderrors = global.errors;
+
+ /* ImportC: convert arrays to pointers, functions to pointers to functions
+ */
+ Type tb = t.toBasetype();
+ if (tb.isTypePointer())
+ i.exp = i.exp.arrayFuncConv(sc);
+
/* Save the expression before ctfe
* Otherwise the error message would contain for example "&[0][0]" instead of "new int"
* Regression: https://issues.dlang.org/show_bug.cgi?id=21687
@@ -408,7 +423,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
// If the result will be implicitly cast, move the cast into CTFE
// to avoid premature truncation of polysemous types.
// eg real [] x = [1.1, 2.2]; should use real precision.
- if (i.exp.implicitConvTo(t))
+ if (i.exp.implicitConvTo(t) && !(sc.flags & SCOPE.Cfile))
{
i.exp = i.exp.implicitCastTo(sc, t);
}
@@ -416,6 +431,11 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
{
return i;
}
+ if (sc.flags & SCOPE.Cfile)
+ /* the interpreter turns (char*)"string" into &"string"[0] which then
+ * it cannot interpret. Resolve that case by doing optimize() first
+ */
+ i.exp = i.exp.optimize(WANTvalue);
i.exp = i.exp.ctfeInterpret();
if (i.exp.op == TOK.voidExpression)
error(i.loc, "variables cannot be initialized with an expression of type `void`. Use `void` initialization instead.");
@@ -424,6 +444,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
{
i.exp = i.exp.optimize(WANTvalue);
}
+
if (!global.gag && olderrors != global.errors)
{
return i; // Failed, suppress duplicate error messages
@@ -445,7 +466,6 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
i.exp.error("cannot use non-constant CTFE pointer in an initializer `%s`", currExp.toChars());
return err();
}
- Type tb = t.toBasetype();
Type ti = i.exp.type.toBasetype();
if (i.exp.op == TOK.tuple && i.expandTuples && !i.exp.implicitConvTo(t))
{
@@ -470,13 +490,12 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
goto L1;
}
}
-
/* C11 6.7.9-14..15
* Initialize an array of unknown size with a string.
- * ImportC regards Tarray as an array of unknown size.
* Change to static array of known size
*/
- if (sc.flags & SCOPE.Cfile && i.exp.op == TOK.string_ && tb.ty == Tarray)
+ if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() &&
+ tb.isTypeSArray() && tb.isTypeSArray().isIncomplete())
{
StringExp se = i.exp.isStringExp();
auto ts = new TypeSArray(tb.nextOf(), new IntegerExp(Loc.initial, se.len + 1, Type.tsize_t));
@@ -683,8 +702,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
}
auto tsa = t.isTypeSArray();
- auto ta = t.isTypeDArray();
- if (!(tsa || ta))
+ if (!tsa)
{
/* Not an array. See if it is `{ exp }` which can be
* converted to an ExpInitializer
@@ -722,19 +740,32 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
{
//printf(" type %s i %d dim %d dil.length = %d\n", t.toChars(), cast(int)i, cast(int)dim, cast(int)dil.length);
auto tn = t.nextOf().toBasetype();
- if (auto tna = tn.isTypeDArray())
+ auto tnsa = tn.isTypeSArray();
+ if (tnsa && tnsa.isIncomplete())
{
// C11 6.2.5-20 "element type shall be complete whenever the array type is specified"
- error(ci.loc, "incomplete element type `%s` not allowed", tna.toChars());
+ error(ci.loc, "incomplete element type `%s` not allowed", tnsa.toChars());
errors = true;
return 1;
}
if (i == dil.length)
return 0;
size_t n;
- auto tnsa = tn.isTypeSArray();
const nelems = tnsa ? cast(size_t)tnsa.dim.toInteger() : 0;
+ /* Run initializerSemantic on a single element.
+ */
+ Initializer elem(Initializer ie)
+ {
+ ++i;
+ auto tnx = tn; // in case initializerSemantic tries to change it
+ ie = ie.initializerSemantic(sc, tnx, needInterpret);
+ if (ie.isErrorInitializer())
+ errors = true;
+ assert(tnx == tn); // sub-types should not be modified
+ return ie;
+ }
+
foreach (j; 0 .. dim)
{
auto di = dil[i];
@@ -751,16 +782,17 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
}
else if (auto tns = tn.isTypeStruct())
{
- dil[n].initializer = structs(tns);
+ if (di.initializer.isExpInitializer())
+ {
+ // no braces enclosing struct initializer
+ dil[n].initializer = structs(tns);
+ }
+ else
+ dil[n].initializer = elem(di.initializer);
}
else
{
- ++i;
- auto tnx = tn; // in case initializerSemantic tries to change it
- di.initializer = di.initializer.initializerSemantic(sc, tnx, needInterpret);
- if (di.initializer.isErrorInitializer())
- errors = true;
- assert(tnx == tn); // sub-types should not be modified
+ di.initializer = elem(di.initializer);
}
++n;
if (i == dil.length)
@@ -770,16 +802,16 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
return n;
}
- size_t dim = ta ? dil.length : cast(size_t)tsa.dim.toInteger();
- auto n = array(t, dim);
+ size_t dim = tsa.isIncomplete() ? dil.length : cast(size_t)tsa.dim.toInteger();
+ auto newdim = array(t, dim);
if (errors)
return err();
- if (ta) // array of unknown length
+ if (tsa.isIncomplete()) // array of unknown length
{
// Change to array of known length
- tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, n, Type.tsize_t));
+ tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, newdim, Type.tsize_t));
tx = tsa; // rewrite caller's type
ci.type = tsa; // remember for later passes
}
@@ -799,6 +831,39 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
return err();
}
+ /* If an array of simple elements, replace with an ArrayInitializer
+ */
+ auto tnb = tn.toBasetype();
+ if (!(tnb.isTypeSArray() || tnb.isTypeStruct()))
+ {
+ auto ai = new ArrayInitializer(ci.loc);
+ ai.dim = cast(uint) dil.length;
+ ai.index.setDim(dil.length);
+ ai.value.setDim(dil.length);
+ foreach (const j; 0 .. dil.length)
+ {
+ ai.index[j] = null;
+ ai.value[j] = dil[j].initializer;
+ }
+ auto ty = tx;
+ return ai.initializerSemantic(sc, ty, needInterpret);
+ }
+
+ if (newdim < ci.initializerList.length && tnb.isTypeStruct())
+ {
+ // https://issues.dlang.org/show_bug.cgi?id=22375
+ // initializerList can be bigger than the number of actual elements
+ // to initialize for array of structs because it is not required
+ // for values to have proper bracing.
+ // i.e: These are all valid initializers for `struct{int a,b;}[3]`:
+ // {1,2,3,4}, {{1,2},3,4}, {1,2,{3,4}}, {{1,2},{3,4}}
+ // In all examples above, the new length of the initializer list
+ // has been shortened from four elements to two. This is important,
+ // because `dil` is written back to directly, making the lowered
+ // initializer `{{1,2},{3,4}}` and not `{{1,2},{3,4},3,4}`.
+ ci.initializerList.length = newdim;
+ }
+
return ci;
}
@@ -1263,6 +1328,3 @@ private bool hasNonConstPointers(Expression e)
}
return false;
}
-
-
-
deleted file mode 100644
@@ -33,7 +33,7 @@ import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.mtype;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.target;
@@ -794,8 +794,8 @@ public:
property("init", d._init.toString());
if (d.isField())
property("offset", d.offset);
- if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
- property("align", d.alignment);
+ if (!d.alignment.isUnknown() && !d.alignment.isDefault())
+ property("align", d.alignment.get());
objectEnd();
}
@@ -27,7 +27,7 @@ import dmd.expression;
import dmd.func;
import dmd.dmangle;
import dmd.mtype;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.stringtable;
import dmd.dscope;
@@ -26,8 +26,9 @@ import dmd.errors;
import dmd.globals;
import dmd.id;
import dmd.identifier;
+import dmd.root.array;
import dmd.root.ctfloat;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.port;
import dmd.root.rmem;
import dmd.root.string;
@@ -229,6 +230,8 @@ class Lexer
ubyte long_doublesize; /// size of C long double, 8 or D real.sizeof
ubyte wchar_tsize; /// size of C wchar_t, 2 or 4
+ structalign_t packalign; /// current state of #pragma pack alignment (ImportC)
+
private
{
const(char)* base; // pointer to start of buffer
@@ -242,6 +245,10 @@ class Lexer
int lastDocLine; // last line of previous doc comment
Token* tokenFreelist;
+
+ // ImportC #pragma pack stack
+ Array!Identifier* records; // identifers (or null)
+ Array!structalign_t* packs; // parallel alignment values
}
nothrow:
@@ -273,6 +280,7 @@ class Lexer
this.commentToken = commentToken;
this.inTokenStringConstant = 0;
this.lastDocLine = 0;
+ this.packalign.setDefault();
//initKeywords();
/* If first line starts with '#!', ignore the line
*/
@@ -1146,6 +1154,11 @@ class Lexer
poundLine(n, false);
continue;
}
+ else if (n.ident == Id.__pragma && Ccompile)
+ {
+ pragmaDirective(scanloc);
+ continue;
+ }
else
{
const locx = loc();
@@ -2162,7 +2175,7 @@ class Lexer
case '.':
if (p[1] == '.')
goto Ldone; // if ".."
- if (base == 10 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
+ if (base <= 10 && n > 0 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
goto Ldone; // if ".identifier" or ".unicode"
if (base == 16 && (!ishex(p[1]) || p[1] == '_' || p[1] & 0x80))
goto Ldone; // if ".identifier" or ".unicode"
@@ -2911,6 +2924,220 @@ class Lexer
error(loc, "#line integer [\"filespec\"]\\n expected");
}
+ /*********************************************
+ * C11 6.10.6 Pragma directive
+ * # pragma pp-tokens(opt) new-line
+ * The C preprocessor sometimes leaves pragma directives in
+ * the preprocessed output. Ignore them.
+ * Upon return, p is at start of next line.
+ */
+ private void pragmaDirective(const ref Loc loc)
+ {
+ Token n;
+ scan(&n);
+ if (n.value == TOK.identifier && n.ident == Id.pack)
+ return pragmaPack(loc);
+ skipToNextLine();
+ }
+
+ /*********
+ * ImportC
+ * # pragma pack
+ * https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
+ * https://docs.microsoft.com/en-us/cpp/preprocessor/pack
+ * Scanner is on the `pack`
+ * Params:
+ * startloc = location to use for error messages
+ */
+ private void pragmaPack(const ref Loc startloc)
+ {
+ const loc = startloc;
+ Token n;
+ scan(&n);
+ if (n.value != TOK.leftParenthesis)
+ {
+ error(loc, "left parenthesis expected to follow `#pragma pack`");
+ skipToNextLine();
+ return;
+ }
+
+ void closingParen()
+ {
+ if (n.value != TOK.rightParenthesis)
+ {
+ error(loc, "right parenthesis expected to close `#pragma pack(`");
+ }
+ skipToNextLine();
+ }
+
+ void setPackAlign(ref const Token t)
+ {
+ const n = t.unsvalue;
+ 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);
+ }
+
+ scan(&n);
+
+ if (!records)
+ {
+ records = new Array!Identifier;
+ packs = new Array!structalign_t;
+ }
+
+ /* # pragma pack ( show )
+ */
+ if (n.value == TOK.identifier && n.ident == Id.show)
+ {
+ if (packalign.isDefault())
+ warning(startloc, "current pack attribute is default");
+ else
+ warning(startloc, "current pack attribute is %d", packalign.get());
+ scan(&n);
+ return closingParen();
+ }
+ /* # pragma pack ( push )
+ * # pragma pack ( push , identifier )
+ * # pragma pack ( push , integer )
+ * # pragma pack ( push , identifier , integer )
+ */
+ if (n.value == TOK.identifier && n.ident == Id.push)
+ {
+ scan(&n);
+ Identifier record = null;
+ if (n.value == TOK.comma)
+ {
+ scan(&n);
+ if (n.value == TOK.identifier)
+ {
+ record = n.ident;
+ scan(&n);
+ if (n.value == TOK.comma)
+ {
+ scan(&n);
+ if (n.value == TOK.int32Literal)
+ {
+ setPackAlign(n);
+ scan(&n);
+ }
+ else
+ error(loc, "alignment value expected, not `%s`", n.toChars());
+ }
+ }
+ else if (n.value == TOK.int32Literal)
+ {
+ setPackAlign(n);
+ scan(&n);
+ }
+ else
+ error(loc, "alignment value expected, not `%s`", n.toChars());
+ }
+ this.records.push(record);
+ this.packs.push(packalign);
+ return closingParen();
+ }
+ /* # pragma pack ( pop )
+ * # pragma pack ( pop PopList )
+ * PopList :
+ * , IdentifierOrInteger
+ * , IdentifierOrInteger PopList
+ * IdentifierOrInteger:
+ * identifier
+ * integer
+ */
+ if (n.value == TOK.identifier && n.ident == Id.pop)
+ {
+ scan(&n);
+ while (n.value == TOK.comma)
+ {
+ scan(&n);
+ if (n.value == TOK.identifier)
+ {
+ for (size_t len = this.records.length; len; --len)
+ {
+ if ((*this.records)[len - 1] == n.ident)
+ {
+ packalign = (*this.packs)[len - 1];
+ this.records.setDim(len - 1);
+ this.packs.setDim(len - 1);
+ break;
+ }
+ }
+ scan(&n);
+ }
+ else if (n.value == TOK.int32Literal)
+ {
+ setPackAlign(n);
+ this.records.push(null);
+ this.packs.push(packalign);
+ scan(&n);
+ }
+ }
+ return closingParen();
+ }
+ /* # pragma pack ( integer )
+ */
+ if (n.value == TOK.int32Literal)
+ {
+ setPackAlign(n);
+ scan(&n);
+ return closingParen();
+ }
+ /* # pragma pack ( )
+ */
+ if (n.value == TOK.rightParenthesis)
+ {
+ packalign.setDefault();
+ return closingParen();
+ }
+
+ error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
+ skipToNextLine();
+ }
+
+ /***************************************
+ * Scan forward to start of next line.
+ */
+ private void skipToNextLine()
+ {
+ while (1)
+ {
+ switch (*p)
+ {
+ case 0:
+ case 0x1A:
+ return; // do not advance p
+
+ case '\n':
+ ++p;
+ break;
+
+ case '\r':
+ ++p;
+ if (p[0] == '\n')
+ ++p;
+ break;
+
+ default:
+ if (*p & 0x80)
+ {
+ const u = decodeUTF();
+ if (u == PS || u == LS)
+ {
+ ++p;
+ break;
+ }
+ }
+ ++p;
+ continue;
+ }
+ break;
+ }
+ endOfLine();
+ }
+
/********************************************
* Decode UTF character.
* Issue error messages for invalid sequences.
@@ -3106,8 +3333,10 @@ class Lexer
return p;
}
-private:
- void endOfLine() pure @nogc @safe
+ /**************************
+ * `p` should be at start of next line
+ */
+ private void endOfLine() pure @nogc @safe
{
scanloc.linnum++;
line = p;
deleted file mode 100644
deleted file mode 100644
deleted file mode 100644
@@ -119,7 +119,7 @@ public:
static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen);
- static Module *load(Loc loc, Identifiers *packages, Identifier *ident);
+ static Module *load(const Loc &loc, Identifiers *packages, Identifier *ident);
const char *kind() const;
bool read(const Loc &loc); // read file, returns 'true' if succeed, 'false' otherwise.
@@ -43,7 +43,7 @@ import dmd.identifier;
import dmd.init;
import dmd.opover;
import dmd.root.ctfloat;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.root.stringtable;
@@ -237,6 +237,7 @@ enum DotExpFlag
{
gag = 1, // don't report "not a property" error and just return null
noDeref = 2, // the use of the expression will not attempt a dereference
+ noAliasThis = 4, // don't do 'alias this' resolution
}
/// Result of a check whether two types are covariant
@@ -426,6 +427,13 @@ extern (C++) abstract class Type : ASTNode
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
+ {
+ return cast(size_t) deco;
+ }
+
extern (D)
final Mcache* getMcache()
{
@@ -2298,7 +2306,9 @@ extern (C++) abstract class Type : ASTNode
*/
structalign_t alignment()
{
- return STRUCTALIGN_DEFAULT;
+ structalign_t s;
+ s.setDefault();
+ return s;
}
/***************************************
@@ -3532,6 +3542,13 @@ extern (C++) final class TypeSArray : TypeArray
this.dim = dim;
}
+ extern (D) this(Type t) // for incomplete type
+ {
+ super(Tsarray, t);
+ //printf("TypeSArray()\n");
+ this.dim = new IntegerExp(0);
+ }
+
override const(char)* kind() const
{
return "sarray";
@@ -3546,6 +3563,15 @@ extern (C++) final class TypeSArray : TypeArray
return result;
}
+ /***
+ * C11 6.7.6.2-4 incomplete array type
+ * Returns: true if incomplete type
+ */
+ bool isIncomplete()
+ {
+ return dim.isIntegerExp() && dim.isIntegerExp().getInteger() == 0;
+ }
+
override d_uns64 size(const ref Loc loc)
{
//printf("TypeSArray::size()\n");
@@ -3952,65 +3978,37 @@ extern (C++) final class TypePointer : TypeNext
if (equals(to))
return MATCH.exact;
- if (next.ty == Tfunction)
- {
- if (auto tp = to.isTypePointer())
- {
- if (tp.next.ty == Tfunction)
- {
- if (next.equals(tp.next))
- return MATCH.constant;
-
- if (next.covariant(tp.next) == Covariant.yes)
- {
- Type tret = this.next.nextOf();
- Type toret = tp.next.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;
- }
- }
- else if (tp.next.ty == Tvoid)
- {
- // Allow conversions to void*
- return MATCH.convert;
- }
- }
+ // Only convert between pointers
+ auto tp = to.isTypePointer();
+ if (!tp)
return MATCH.nomatch;
- }
- else if (auto tp = to.isTypePointer())
+
+ assert(this.next);
+ assert(tp.next);
+
+ // Conversion to void*
+ if (tp.next.ty == Tvoid)
{
- assert(tp.next);
+ // Function pointer conversion doesn't check constness?
+ if (this.next.ty == Tfunction)
+ return MATCH.convert;
if (!MODimplicitConv(next.mod, tp.next.mod))
return MATCH.nomatch; // not const-compatible
- /* Alloc conversion to void*
- */
- if (next.ty != Tvoid && tp.next.ty == Tvoid)
- {
- return MATCH.convert;
- }
-
- MATCH m = next.constConv(tp.next);
- if (m > MATCH.nomatch)
- {
- if (m == MATCH.exact && mod != to.mod)
- m = MATCH.constant;
- return m;
- }
+ return this.next.ty == Tvoid ? MATCH.constant : MATCH.convert;
}
- return MATCH.nomatch;
+
+ // Conversion between function pointers
+ if (auto thisTf = this.next.isTypeFunction())
+ return thisTf.implicitPointerConv(tp.next);
+
+ // Default, no implicit conversion between the pointer targets
+ MATCH m = next.constConv(tp.next);
+
+ if (m == MATCH.exact && mod != to.mod)
+ m = MATCH.constant;
+ return m;
}
override MATCH constConv(Type to)
@@ -4760,7 +4758,10 @@ extern (C++) final class TypeFunction : TypeNext
}
}
else
- m = arg.implicitConvTo(tprm);
+ {
+ import dmd.dcast : cimplicitConvTo;
+ m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm);
+ }
}
//printf("match %d\n", m);
}
@@ -4971,6 +4972,47 @@ extern (C++) final class TypeFunction : TypeNext
return MATCH.nomatch;
}
+ /+
+ + Checks whether this function type is convertible to ` to`
+ + when used in a function pointer / delegate.
+ +
+ + Params:
+ + to = target type
+ +
+ + Returns:
+ + MATCH.nomatch: `to` is not a covaraint function
+ + MATCH.convert: `to` is a covaraint function
+ + MATCH.exact: `to` is identical to this function
+ +/
+ private MATCH implicitPointerConv(Type to)
+ {
+ assert(to);
+
+ if (this == to)
+ return MATCH.constant;
+
+ if (this.covariant(to) == Covariant.yes)
+ {
+ Type tret = this.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;
+ }
+
+ return MATCH.nomatch;
+ }
+
/** Extends TypeNext.constConv by also checking for matching attributes **/
override MATCH constConv(Type to)
{
@@ -5262,27 +5304,16 @@ extern (C++) final class TypeDelegate : TypeNext
if (this == to)
return MATCH.exact;
- version (all)
+ if (auto toDg = to.isTypeDelegate())
{
- // not allowing covariant conversions because it interferes with overriding
- if (to.ty == Tdelegate && this.nextOf().covariant(to.nextOf()) == Covariant.yes)
- {
- Type tret = this.next.nextOf();
- Type toret = (cast(TypeDelegate)to).next.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 delegate() dg = delegate C() {} // should be error
- */
- int offset = 0;
- if (toret.isBaseOf(tret, &offset) && offset != 0)
- return MATCH.nomatch;
- }
- return MATCH.convert;
- }
+ MATCH m = this.next.isTypeFunction().implicitPointerConv(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;
+
+ return m;
}
return MATCH.nomatch;
@@ -5516,6 +5547,11 @@ extern (C++) final class TypeIdentifier : TypeQualified
this.ident = ident;
}
+ static TypeIdentifier create(const ref Loc loc, Identifier ident)
+ {
+ return new TypeIdentifier(loc, ident);
+ }
+
override const(char)* kind() const
{
return "identifier";
@@ -5737,7 +5773,7 @@ extern (C++) final class TypeStruct : Type
override structalign_t alignment()
{
- if (sym.alignment == 0)
+ if (sym.alignment.isUnknown())
sym.size(sym.loc);
return sym.alignment;
}
@@ -6519,6 +6555,29 @@ extern (C++) final class TypeTuple : Type
return false;
}
+ override MATCH implicitConvTo(Type to)
+ {
+ if (this == to)
+ return MATCH.exact;
+ if (auto tt = to.isTypeTuple())
+ {
+ if (arguments.dim == tt.arguments.dim)
+ {
+ MATCH m = MATCH.exact;
+ for (size_t i = 0; i < tt.arguments.dim; i++)
+ {
+ Parameter arg1 = (*arguments)[i];
+ Parameter arg2 = (*tt.arguments)[i];
+ MATCH mi = arg1.type.implicitConvTo(arg2.type);
+ if (mi < m)
+ m = mi;
+ }
+ return m;
+ }
+ }
+ return MATCH.nomatch;
+ }
+
override void accept(Visitor v)
{
v.visit(this);
@@ -224,6 +224,7 @@ public:
bool equivalent(Type *t);
// kludge for template.isType()
DYNCAST dyncast() const { return DYNCAST_TYPE; }
+ size_t getUniqueID() const;
Covariant covariant(Type *t, StorageClass *pstc = NULL);
const char *toChars() const;
char *toPrettyChars(bool QualifyTypes = false);
@@ -446,6 +447,7 @@ public:
const char *kind();
TypeSArray *syntaxCopy();
+ bool isIncomplete();
d_uns64 size(const Loc &loc);
unsigned alignsize();
bool isString();
@@ -582,6 +584,7 @@ struct ParameterList
Parameters* parameters;
StorageClass stc;
VarArg varargs;
+ bool hasIdentifierList; // true if C identifier-list style
size_t length();
Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); }
@@ -711,6 +714,7 @@ public:
Identifier *ident;
Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution
+ static TypeIdentifier *create(const Loc &loc, Identifier *ident);
const char *kind();
TypeIdentifier *syntaxCopy();
Dsymbol *toDsymbol(Scope *sc);
@@ -43,7 +43,7 @@ import dmd.tokens;
import dmd.visitor;
import dmd.root.bitarray;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
/**********************************
* Perform ownership/borrowing checks for funcdecl.
@@ -38,7 +38,7 @@ import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.root.array;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.stringtable;
import dmd.target;
import dmd.tokens;
@@ -213,9 +213,13 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE
if (isRecursiveAliasThis(e.att1, e.e1.type))
return null;
//printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
- Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
BinExp be = cast(BinExp)e.copy();
- be.e1 = e1;
+ // Resolve 'alias this' but in case of assigment don't resolve properties yet
+ // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2'
+ bool findOnly = (e.op == TOK.assign);
+ be.e1 = resolveAliasThis(sc, e.e1, true, findOnly);
+ if (!be.e1)
+ return null;
Expression result;
if (be.op == TOK.concatenateAssign)
@@ -237,9 +241,10 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE
if (isRecursiveAliasThis(e.att2, e.e2.type))
return null;
//printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
- Expression e2 = new DotIdExp(e.loc, e.e2, ad.aliasthis.ident);
BinExp be = cast(BinExp)e.copy();
- be.e2 = e2;
+ be.e2 = resolveAliasThis(sc, e.e2, true);
+ if (!be.e2)
+ return null;
Expression result;
if (be.op == TOK.concatenateAssign)
@@ -1744,11 +1749,31 @@ private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration f
else if (m == match && m > MATCH.nomatch)
{
assert(fd_best);
- /* Ignore covariant matches, as later on it can be redone
- * after the opApply delegate has its attributes inferred.
- */
- if (tf.covariant(fd_best.type) != Covariant.yes &&
- fd_best.type.covariant(tf) != Covariant.yes)
+ auto bestTf = fd_best.type.isTypeFunction();
+ assert(bestTf);
+
+ // Found another overload with different attributes?
+ // e.g. @system vs. @safe opApply
+ bool ambig = tf.attributesEqual(bestTf);
+
+ // opApplies with identical attributes could still accept
+ // different function bodies as delegate
+ // => different parameters or attributes
+ if (ambig)
+ {
+ // Fetch the delegates that receive the function body
+ auto tfBody = tf.parameterList[0].type.isTypeDelegate().next;
+ assert(tfBody);
+
+ auto bestBody = bestTf.parameterList[0].type.isTypeDelegate().next;
+ assert(bestBody);
+
+ // Ignore covariant matches, as later on it can be redone
+ // after the opApply delegate has its attributes inferred.
+ ambig = !(tfBody.covariant(bestBody) == Covariant.yes || bestBody.covariant(tfBody) == Covariant.yes);
+ }
+
+ if (ambig)
fd_ambig = f; // not covariant, so ambiguous
}
return 0; // continue
@@ -697,6 +697,8 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
// See if we can remove an unnecessary cast
ClassDeclaration cdfrom = e.e1.type.isClassHandle();
ClassDeclaration cdto = e.type.isClassHandle();
+ if (cdfrom.errors || cdto.errors)
+ return error();
if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
goto L1; // can always convert a class to Object
// Need to determine correct offset before optimizing away the cast.
@@ -22,7 +22,7 @@ import dmd.identifier;
import dmd.lexer;
import dmd.errors;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.root.string;
@@ -556,6 +556,9 @@ class Parser(AST) : Lexer
{
case TOK.leftParenthesis:
{
+ // MixinType
+ if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
+ goto Ldeclaration;
// mixin(string)
nextToken();
auto exps = parseArguments();
@@ -2954,6 +2957,8 @@ class Parser(AST) : Lexer
// Don't call nextToken again.
}
case TOK.in_:
+ if (global.params.vin)
+ message(scanloc, "Usage of 'in' on parameter");
stc = STC.in_;
goto L2;
@@ -5408,6 +5413,11 @@ class Parser(AST) : Lexer
stc = STC.scope_;
goto Lagain;
+ case TOK.out_:
+ error("cannot declare `out` loop variable, use `ref` instead");
+ stc = STC.out_;
+ goto Lagain;
+
case TOK.enum_:
stc = STC.manifest;
goto Lagain;
deleted file mode 100644
@@ -59,7 +59,7 @@ extern (C++) final class PrintASTVisitor : Visitor
printIndent(indent);
import dmd.hdrgen : floatToBuffer;
- import dmd.root.outbuffer : OutBuffer;
+ import dmd.common.outbuffer : OutBuffer;
OutBuffer buf;
floatToBuffer(e.type, e.value, &buf, false);
printf("Real %s %s\n", buf.peekChars(), e.type ? e.type.toChars() : "");
@@ -11,7 +11,6 @@
| [hash.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d) | Calculate a hash for a byte array |
| [longdouble.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/longdouble.d) | 80-bit floating point number implementation in case they are not natively supported |
| [man.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/man.d) | Opens an online manual page |
-| [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d) | An expandable buffer in which you can write text or binary data. |
| [port.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d) | Portable routines for functions that have different implementations on different platforms |
| [region.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d) | A region allocator |
| [response.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/response.d) | Parse command line arguments from response files |
deleted file mode 100644
deleted file mode 100644
@@ -23,410 +23,8 @@ import dmd.root.filename;
import dmd.root.rmem;
import dmd.root.string;
-/**
-Encapsulated management of a memory-mapped file.
-
-Params:
-Datum = the mapped data type: Use a POD of size 1 for read/write mapping
-and a `const` version thereof for read-only mapping. Other primitive types
-should work, but have not been yet tested.
-*/
-struct FileMapping(Datum)
-{
- static assert(__traits(isPOD, Datum) && Datum.sizeof == 1,
- "Not tested with other data types yet. Add new types with care.");
-
- version(Posix) enum invalidHandle = -1;
- else version(Windows) enum invalidHandle = INVALID_HANDLE_VALUE;
-
- // state {
- /// Handle of underlying file
- private auto handle = invalidHandle;
- /// File mapping object needed on Windows
- version(Windows) private HANDLE fileMappingObject = invalidHandle;
- /// Memory-mapped array
- private Datum[] data;
- /// Name of underlying file, zero-terminated
- private const(char)* name;
- // state }
-
- /**
- Open `filename` and map it in memory. If `Datum` is `const`, opens for
- read-only and maps the content in memory; no error is issued if the file
- does not exist. This makes it easy to treat a non-existing file as empty.
-
- If `Datum` is mutable, opens for read/write (creates file if it does not
- exist) and fails fatally on any error.
-
- Due to quirks in `mmap`, if the file is empty, `handle` is valid but `data`
- is `null`. This state is valid and accounted for.
-
- Params:
- filename = the name of the file to be mapped in memory
- */
- this(const char* filename)
- {
- version (Posix)
- {
- import core.sys.posix.sys.mman;
- import core.sys.posix.fcntl;
-
- handle = .open(filename, is(Datum == const) ? O_RDONLY : (O_CREAT | O_RDWR),
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
- if (handle == invalidHandle)
- {
- static if (is(Datum == const))
- {
- // No error, nonexisting file in read mode behaves like an empty file.
- return;
- }
- else
- {
- fprintf(stderr, "open(\"%s\") failed: %s\n", filename, strerror(errno));
- exit(1);
- }
- }
-
- const size = File.size(handle);
-
- if (size > 0 && size != ulong.max && size <= size_t.max)
- {
- auto p = mmap(null, cast(size_t) size, is(Datum == const) ? PROT_READ : PROT_WRITE, MAP_SHARED, handle, 0);
- if (p == MAP_FAILED)
- {
- fprintf(stderr, "mmap(null, %zu) for \"%s\" failed: %s\n", cast(size_t) size, filename, strerror(errno));
- exit(1);
- }
- // The cast below will always work because it's gated by the `size <= size_t.max` condition.
- data = cast(Datum[]) p[0 .. cast(size_t) size];
- }
- }
- else version(Windows)
- {
- static if (is(Datum == const))
- {
- enum createFileMode = GENERIC_READ;
- enum openFlags = OPEN_EXISTING;
- }
- else
- {
- enum createFileMode = GENERIC_READ | GENERIC_WRITE;
- enum openFlags = CREATE_ALWAYS;
- }
-
- handle = CreateFileA(filename, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null);
- if (handle == invalidHandle)
- {
- static if (is(Datum == const))
- {
- return;
- }
- else
- {
- fprintf(stderr, "CreateFileA() failed for \"%s\": %d\n", filename, GetLastError());
- exit(1);
- }
- }
- createMapping(filename, File.size(handle));
- }
- else static assert(0);
-
- // Save the name for later. Technically there's no need: on Linux one can use readlink on /proc/self/fd/NNN.
- // On BSD and OSX one can use fcntl with F_GETPATH. On Windows one can use GetFileInformationByHandleEx.
- // But just saving the name is simplest, fastest, and most portable...
- import core.stdc.string : strlen;
- name = filename[0 .. filename.strlen() + 1].idup.ptr;
- }
-
- /**
- Common code factored opportunistically. Windows only. Assumes `handle` is
- already pointing to an opened file. Initializes the `fileMappingObject`
- and `data` members.
-
- Params:
- filename = the file to be mapped
- size = the size of the file in bytes
- */
- version(Windows) private void createMapping(const char* filename, ulong size)
- {
- assert(size <= size_t.max || size == ulong.max);
- assert(handle != invalidHandle);
- assert(data is null);
- assert(fileMappingObject == invalidHandle);
-
- if (size == 0 || size == ulong.max)
- return;
-
- static if (is(Datum == const))
- {
- enum fileMappingFlags = PAGE_READONLY;
- enum mapViewFlags = FILE_MAP_READ;
- }
- else
- {
- enum fileMappingFlags = PAGE_READWRITE;
- enum mapViewFlags = FILE_MAP_WRITE;
- }
-
- fileMappingObject = CreateFileMappingA(handle, null, fileMappingFlags, 0, 0, null);
- if (!fileMappingObject)
- {
- fprintf(stderr, "CreateFileMappingA(%p) failed for %llu bytes of \"%s\": %d\n",
- handle, size, filename, GetLastError());
- fileMappingObject = invalidHandle; // by convention always use invalidHandle, not null
- exit(1);
- }
- auto p = MapViewOfFile(fileMappingObject, mapViewFlags, 0, 0, 0);
- if (!p)
- {
- fprintf(stderr, "MapViewOfFile() failed for \"%s\": %d\n", filename, GetLastError());
- exit(1);
- }
- data = cast(Datum[]) p[0 .. cast(size_t) size];
- }
-
- // Not copyable or assignable (for now).
- @disable this(const FileMapping!Datum rhs);
- @disable void opAssign(const ref FileMapping!Datum rhs);
-
- /**
- Frees resources associated with this mapping. However, it does not deallocate the name.
- */
- ~this() pure nothrow
- {
- if (!active)
- return;
- fakePure({
- version (Posix)
- {
- import core.sys.posix.sys.mman : munmap;
-
- // Cannot call fprintf from inside a destructor, so exiting silently.
-
- if (data.ptr && munmap(cast(void*) data.ptr, data.length) != 0)
- {
- exit(1);
- }
- data = null;
- if (handle != invalidHandle && .close(handle) != 0)
- {
- exit(1);
- }
- handle = invalidHandle;
- }
- else version(Windows)
- {
- if (data.ptr !is null && UnmapViewOfFile(cast(void*) data.ptr) == 0)
- {
- exit(1);
- }
- data = null;
- if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
- {
- exit(1);
- }
- fileMappingObject = invalidHandle;
- if (handle != invalidHandle && CloseHandle(handle) == 0)
- {
- exit(1);
- }
- handle = invalidHandle;
- }
- else static assert(0);
- });
- }
-
- /**
- Returns the zero-terminated file name associated with the mapping. Can
- be saved beyond the lifetime of `this`.
- */
- const(char)* filename() const pure @nogc @safe nothrow { return name; }
-
- /**
- Frees resources associated with this mapping. However, it does not deallocate the name.
- Reinitializes `this` as a fresh object that can be reused.
- */
- void close()
- {
- __dtor();
- handle = invalidHandle;
- version(Windows) fileMappingObject = invalidHandle;
- data = null;
- name = null;
- }
-
- /**
- Deletes the underlying file and frees all resources associated.
- Reinitializes `this` as a fresh object that can be reused.
-
- This function does not abort if the file cannot be deleted, but does print
- a message on `stderr` and returns `false` to the caller. The underlying
- rationale is to give the caller the option to continue execution if
- deleting the file is not important.
-
- Returns: `true` iff the file was successfully deleted. If the file was not
- deleted, prints a message to `stderr` and returns `false`.
- */
- static if (!is(Datum == const))
- bool discard()
- {
- // Truncate file to zero so unflushed buffers are not flushed unnecessarily.
- resize(0);
- auto deleteme = name;
- close();
- // In-memory resource freed, now get rid of the underlying temp file.
- version(Posix)
- {
- import core.sys.posix.unistd;
- if (unlink(deleteme) != 0)
- {
- fprintf(stderr, "unlink(\"%s\") failed: %s\n", filename, strerror(errno));
- return false;
- }
- }
- else version(Windows)
- {
- import core.sys.windows.winbase;
- if (DeleteFileA(deleteme) == 0)
- {
- fprintf(stderr, "DeleteFileA error %d\n", GetLastError());
- return false;
- }
- }
- else static assert(0);
- return true;
- }
-
- /**
- Queries whether `this` is currently associated with a file.
-
- Returns: `true` iff there is an active mapping.
- */
- bool active() const pure @nogc nothrow
- {
- return handle !is invalidHandle;
- }
-
- /**
- Queries the length of the file associated with this mapping. If not
- active, returns 0.
-
- Returns: the length of the file, or 0 if no file associated.
- */
- size_t length() const pure @nogc @safe nothrow { return data.length; }
-
- /**
- Get a slice to the contents of the entire file.
-
- Returns: the contents of the file. If not active, returns the `null` slice.
- */
- auto opSlice() pure @nogc @safe nothrow { return data; }
-
- /**
- Resizes the file and mapping to the specified `size`.
-
- Params:
- size = new length requested
- */
- static if (!is(Datum == const))
- void resize(size_t size) pure
- {
- assert(handle != invalidHandle);
- fakePure({
- version(Posix)
- {
- import core.sys.posix.unistd : ftruncate;
- import core.sys.posix.sys.mman;
-
- if (data.length)
- {
- assert(data.ptr, "Corrupt memory mapping");
- // assert(0) here because it would indicate an internal error
- munmap(cast(void*) data.ptr, data.length) == 0 || assert(0);
- data = null;
- }
- if (ftruncate(handle, size) != 0)
- {
- fprintf(stderr, "ftruncate() failed for \"%s\": %s\n", filename, strerror(errno));
- exit(1);
- }
- if (size > 0)
- {
- auto p = mmap(null, size, PROT_WRITE, MAP_SHARED, handle, 0);
- if (cast(ssize_t) p == -1)
- {
- fprintf(stderr, "mmap() failed for \"%s\": %s\n", filename, strerror(errno));
- exit(1);
- }
- data = cast(Datum[]) p[0 .. size];
- }
- }
- else version(Windows)
- {
- // Per documentation, must unmap first.
- if (data.length > 0 && UnmapViewOfFile(cast(void*) data.ptr) == 0)
- {
- fprintf(stderr, "UnmapViewOfFile(%p) failed for memory mapping of \"%s\": %d\n",
- data.ptr, filename, GetLastError());
- exit(1);
- }
- data = null;
- if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
- {
- fprintf(stderr, "CloseHandle() failed for memory mapping of \"%s\": %d\n", filename, GetLastError());
- exit(1);
- }
- fileMappingObject = invalidHandle;
- LARGE_INTEGER biggie;
- biggie.QuadPart = size;
- if (SetFilePointerEx(handle, biggie, null, FILE_BEGIN) == 0 || SetEndOfFile(handle) == 0)
- {
- fprintf(stderr, "SetFilePointer() failed for \"%s\": %d\n", filename, GetLastError());
- exit(1);
- }
- createMapping(name, size);
- }
- else static assert(0);
- });
- }
-
- /**
- Unconditionally and destructively moves the underlying file to `filename`.
- If the operation succeds, returns true. Upon failure, prints a message to
- `stderr` and returns `false`.
-
- Params: filename = zero-terminated name of the file to move to.
-
- Returns: `true` iff the operation was successful.
- */
- bool moveToFile(const char* filename)
- {
- auto oldname = name;
-
- close();
- // Rename the underlying file to the target, no copy necessary.
- version(Posix)
- {
- if (.rename(oldname, filename) != 0)
- {
- fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", oldname, filename, strerror(errno));
- return false;
- }
- }
- else version(Windows)
- {
- import core.sys.windows.winbase;
- if (MoveFileExA(oldname, filename, MOVEFILE_REPLACE_EXISTING) == 0)
- {
- fprintf(stderr, "MoveFileExA(\"%s\", \"%s\") failed: %d\n", oldname, filename, GetLastError());
- return false;
- }
- }
- else static assert(0);
- return true;
- }
-}
+import dmd.common.file;
+import dmd.common.string;
/// Owns a (rmem-managed) file buffer.
struct FileBuffer
@@ -585,58 +183,8 @@ nothrow:
/// Write a file, returning `true` on success.
extern (D) static bool write(const(char)* name, const void[] data)
{
- version (Posix)
- {
- ssize_t numwritten;
- int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
- if (fd == -1)
- goto err;
- numwritten = .write(fd, data.ptr, data.length);
- if (numwritten != data.length)
- goto err2;
- if (close(fd) == -1)
- goto err;
- return true;
- err2:
- close(fd);
- .remove(name);
- err:
- return false;
- }
- else version (Windows)
- {
- DWORD numwritten; // here because of the gotos
- const nameStr = name.toDString;
- // work around Windows file path length limitation
- // (see documentation for extendedPathThen).
- HANDLE h = nameStr.extendedPathThen!
- (p => CreateFileW(p.ptr,
- GENERIC_WRITE,
- 0,
- null,
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
- null));
- if (h == INVALID_HANDLE_VALUE)
- goto err;
-
- if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
- goto err2;
- if (numwritten != data.length)
- goto err2;
- if (!CloseHandle(h))
- goto err;
- return true;
- err2:
- CloseHandle(h);
- nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
- err:
- return false;
- }
- else
- {
- static assert(0);
- }
+ import dmd.common.file : writeFile;
+ return writeFile(name, data);
}
///ditto
@@ -717,42 +265,6 @@ nothrow:
return update(name, data[0 .. size]);
}
- /// Touch a file to current date
- static bool touch(const char* namez)
- {
- version (Windows)
- {
- FILETIME ft = void;
- SYSTEMTIME st = void;
- GetSystemTime(&st);
- SystemTimeToFileTime(&st, &ft);
-
- import core.stdc.string : strlen;
-
- // get handle to file
- HANDLE h = namez[0 .. namez.strlen()].extendedPathThen!(p => CreateFile(p.ptr,
- FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
- null, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, null));
- if (h == INVALID_HANDLE_VALUE)
- return false;
-
- const f = SetFileTime(h, null, null, &ft); // set last write time
-
- if (!CloseHandle(h))
- return false;
-
- return f != 0;
- }
- else version (Posix)
- {
- import core.sys.posix.utime;
- return utime(namez, null) == 0;
- }
- else
- static assert(0);
- }
-
/// Size of a file in bytes.
/// Params: namez = null-terminated filename
/// Returns: `ulong.max` on any error, the length otherwise.
@@ -777,38 +289,5 @@ nothrow:
// Error cases go here.
return ulong.max;
}
-
- /// Ditto
- version (Posix)
- static ulong size(int fd)
- {
- stat_t buf;
- if (fstat(fd, &buf) == 0)
- return buf.st_size;
- return ulong.max;
- }
-
- /// Ditto
- version (Windows)
- static ulong size(HANDLE fd)
- {
- ulong result;
- if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0)
- return result;
- return ulong.max;
- }
}
-/**
-Runs a non-pure function or delegate as pure code. Use with caution.
-
-Params:
-fun = the delegate to run, usually inlined: `fakePure({ ... });`
-
-Returns: whatever `fun` returns.
-*/
-private auto ref fakePure(F)(scope F fun) pure
-{
- mixin("alias PureFun = " ~ F.stringof ~ " pure;");
- return (cast(PureFun) fun)();
-}
@@ -16,7 +16,8 @@ import core.stdc.errno;
import core.stdc.string;
import dmd.root.array;
import dmd.root.file;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
+import dmd.common.file;
import dmd.root.port;
import dmd.root.rmem;
import dmd.root.rootobject;
@@ -1123,78 +1124,13 @@ version(Windows)
*/
private int _mkdir(const(char)[] path) nothrow
{
+ import dmd.common.string : extendedPathThen;
const createRet = path.extendedPathThen!(
p => CreateDirectoryW(&p[0], null /*securityAttributes*/));
// different conventions for CreateDirectory and mkdir
return createRet == 0 ? 1 : 0;
}
- /**************************************
- * Converts a path to one suitable to be passed to Win32 API
- * functions that can deal with paths longer than 248
- * characters then calls the supplied function on it.
- *
- * Params:
- * path = The Path to call F on.
- *
- * Returns:
- * The result of calling F on path.
- *
- * References:
- * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
- */
- package auto extendedPathThen(alias F)(const(char)[] path)
- {
- if (!path.length)
- return F((wchar[]).init);
- return path.toWStringzThen!((wpath)
- {
- // GetFullPathNameW expects a sized buffer to store the result in. Since we don't
- // know how large it has to be, we pass in null and get the needed buffer length
- // as the return code.
- const pathLength = GetFullPathNameW(&wpath[0],
- 0 /*length8*/,
- null /*output buffer*/,
- null /*filePartBuffer*/);
- if (pathLength == 0)
- {
- return F((wchar[]).init);
- }
-
- // wpath is the UTF16 version of path, but to be able to use
- // extended paths, we need to prefix with `\\?\` and the absolute
- // path.
- static immutable prefix = `\\?\`w;
-
- // prefix only needed for long names and non-UNC names
- const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\');
- const prefixLength = needsPrefix ? prefix.length : 0;
-
- // +1 for the null terminator
- const bufferLength = pathLength + prefixLength + 1;
-
- wchar[1024] absBuf = void;
- wchar[] absPath = bufferLength > absBuf.length
- ? new wchar[bufferLength] : absBuf[0 .. bufferLength];
-
- absPath[0 .. prefixLength] = prefix[0 .. prefixLength];
-
- const absPathRet = GetFullPathNameW(&wpath[0],
- cast(uint)(absPath.length - prefixLength - 1),
- &absPath[prefixLength],
- null /*filePartBuffer*/);
-
- if (absPathRet == 0 || absPathRet > absPath.length - prefixLength)
- {
- return F((wchar[]).init);
- }
-
- absPath[$ - 1] = '\0';
- // Strip null terminator from the slice
- return F(absPath[0 .. $ - 1]);
- });
- }
-
/**********************************
* Converts a UTF-16 string to a (null-terminated) narrow string.
* Returns:
@@ -1222,33 +1158,6 @@ version(Windows)
return newBuffer[0 .. length];
}
- /**********************************
- * Converts a narrow string to a (null-terminated) UTF-16 string.
- * Returns:
- * If `buffer` is specified and the result fits, a slice of that buffer,
- * otherwise a new buffer which can be released via `mem.xfree()`.
- * Nulls are propagated, i.e., if `narrow` is null, the returned slice is
- * null too.
- */
- wchar[] toWStringz(const(char)[] narrow, wchar[] buffer = null) nothrow
- {
- if (narrow is null)
- return null;
-
- const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
- if (requiredLength < buffer.length)
- {
- buffer[requiredLength] = 0;
- return buffer[0 .. requiredLength];
- }
-
- wchar* newBuffer = cast(wchar*) mem.xmalloc_noscan((requiredLength + 1) * wchar.sizeof);
- const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, newBuffer, requiredLength);
- assert(length == requiredLength);
- newBuffer[length] = 0;
- return newBuffer[0 .. length];
- }
-
/**********************************
* Converts a slice of UTF-8 characters to an array of wchar that's null
* terminated so it can be passed to Win32 APIs then calls the supplied
@@ -1262,9 +1171,12 @@ version(Windows)
*/
private auto toWStringzThen(alias F)(const(char)[] str) nothrow
{
+ import dmd.common.string : SmallBuffer, toWStringz;
+
if (!str.length) return F(""w.ptr);
- wchar[1024] buf = void;
+ wchar[1024] support = void;
+ auto buf = SmallBuffer!wchar(support.length, support);
wchar[] wide = toWStringz(str, buf);
scope(exit) wide.ptr != buf.ptr && mem.xfree(wide.ptr);
deleted file mode 100644
@@ -13,7 +13,7 @@ module dmd.root.rootobject;
import core.stdc.stdio;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
/***********************************************************
*/
deleted file mode 100644
deleted file mode 100644
@@ -89,7 +89,7 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
if (hasPointers && v.type.toBasetype().ty != Tstruct)
{
- if ((ad.type.alignment() < target.ptrsize ||
+ if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize ||
(v.offset & (target.ptrsize - 1))) &&
sc.func.setUnsafe())
{
@@ -53,7 +53,7 @@ import dmd.objc;
import dmd.opover;
import dmd.parse;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.sideeffect;
@@ -363,7 +363,7 @@ private extern(C++) final class Semantic2Visitor : Visitor
assert(fd.semanticRun <= PASS.semantic2);
fd.semanticRun = PASS.semantic2;
- //printf("FuncDeclaration::semantic2 [%s] fd0 = %s %s\n", loc.toChars(), toChars(), type.toChars());
+ //printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr);
// Only check valid functions which have a body to avoid errors
// for multiple declarations, e.g.
@@ -55,7 +55,7 @@ import dmd.objc;
import dmd.opover;
import dmd.parse;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.root.rootobject;
import dmd.sideeffect;
@@ -407,7 +407,8 @@ private extern(C++) final class Semantic3Visitor : Visitor
sc2.insert(_arguments);
_arguments.parent = funcdecl;
}
- if (f.linkage == LINK.d || f.parameterList.length)
+ if ((f.linkage == LINK.d || f.parameterList.length) &&
+ !(sc.flags & SCOPE.Cfile)) // don't want to require importing stdarg for C files
{
// Declare _argptr
Type t = target.va_listType(funcdecl.loc, sc);
@@ -598,7 +599,10 @@ private extern(C++) final class Semantic3Visitor : Visitor
f.next = Type.tvoid;
if (f.checkRetType(funcdecl.loc))
funcdecl.fbody = new ErrorStatement();
+ else if (funcdecl.isMain())
+ funcdecl.checkDmain(); // Check main() parameters and return type
}
+
if (global.params.vcomplex && f.next !is null)
f.next.checkComplexTransition(funcdecl.loc, sc);
@@ -777,8 +781,14 @@ private extern(C++) final class Semantic3Visitor : Visitor
}
assert(!funcdecl.returnLabel);
}
- else if (f.next.ty == Tnoreturn)
+ else if (f.next.toBasetype().ty == Tnoreturn)
{
+ // Fallthrough despite being declared as noreturn? return is already rejected when evaluating the ReturnStatement
+ if (blockexit & BE.fallthru)
+ {
+ funcdecl.error("is typed as `%s` but does return", f.next.toChars());
+ funcdecl.loc.errorSupplemental("`noreturn` functions must either throw, abort or loop indefinitely");
+ }
}
else
{
@@ -1571,7 +1581,7 @@ private struct FuncDeclSem3
}
}
-private void semanticTypeInfoMembers(StructDeclaration sd)
+extern (C++) void semanticTypeInfoMembers(StructDeclaration sd)
{
if (sd.xeq &&
sd.xeq._scope &&
@@ -39,7 +39,7 @@ import dmd.id;
import dmd.identifier;
import dmd.dinterpret;
import dmd.mtype;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.sapply;
import dmd.sideeffect;
@@ -463,7 +463,7 @@ extern (C++) class ExpStatement : Statement
this.exp = new DeclarationExp(loc, declaration);
}
- static ExpStatement create(Loc loc, Expression exp)
+ static ExpStatement create(const ref Loc loc, Expression exp)
{
return new ExpStatement(loc, exp);
}
@@ -577,7 +577,7 @@ extern (C++) class CompoundStatement : Statement
statements.push(s);
}
- static CompoundStatement create(Loc loc, Statement s1, Statement s2)
+ static CompoundStatement create(const ref Loc loc, Statement s1, Statement s2)
{
return new CompoundStatement(loc, s1, s2);
}
@@ -1635,7 +1635,7 @@ extern (C++) final class TryFinallyStatement : Statement
this.bodyFallsThru = true; // assume true until statementSemantic()
}
- static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody)
+ static TryFinallyStatement create(const ref Loc loc, Statement _body, Statement finalbody)
{
return new TryFinallyStatement(loc, _body, finalbody);
}
@@ -186,7 +186,7 @@ class ExpStatement : public Statement
public:
Expression *exp;
- static ExpStatement *create(Loc loc, Expression *exp);
+ static ExpStatement *create(const Loc &loc, Expression *exp);
ExpStatement *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
@@ -218,7 +218,7 @@ class CompoundStatement : public Statement
public:
Statements *statements;
- static CompoundStatement *create(Loc loc, Statement *s1, Statement *s2);
+ static CompoundStatement *create(const Loc &loc, Statement *s1, Statement *s2);
CompoundStatement *syntaxCopy();
ReturnStatement *endsWithReturnStatement();
Statement *last();
@@ -615,7 +615,7 @@ public:
Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
bool bodyFallsThru; // true if _body falls through to finally
- static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody);
+ static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody);
TryFinallyStatement *syntaxCopy();
bool hasBreak() const;
bool hasContinue() const;
deleted file mode 100644
@@ -54,7 +54,7 @@ import dmd.nogc;
import dmd.opover;
import dmd.parse;
import dmd.printast;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.string;
import dmd.semantic2;
import dmd.sideeffect;
@@ -659,20 +659,6 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
result = fs;
}
- /*******************
- * Determines the return type of makeTupleForeach.
- */
- private static template MakeTupleForeachRet(bool isDecl)
- {
- static if(isDecl)
- {
- alias MakeTupleForeachRet = Dsymbols*;
- }
- else
- {
- alias MakeTupleForeachRet = void;
- }
- }
/*******************
* Type check and unroll `foreach` over an expression tuple as well
@@ -696,29 +682,24 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
* expands the tuples into multiple `STC.local` `static foreach`
* variables.
*/
- MakeTupleForeachRet!isDecl makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
+ auto makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, Dsymbols* dbody, bool needExpansion)
{
- auto returnEarly()
- {
- static if (isDecl)
- {
- return null;
- }
- else
- {
- result = new ErrorStatement();
- return;
- }
- }
- static if(isDecl)
+ // Voldemort return type
+ union U
{
- static assert(isStatic);
- auto dbody = args[0];
+ Statement statement;
+ Dsymbols* decl;
}
- static if(isStatic)
+
+ U result;
+
+ auto returnEarly()
{
- auto needExpansion = args[$-1];
- assert(sc);
+ if (isDecl)
+ result.decl = null;
+ else
+ result.statement = new ErrorStatement();
+ return result;
}
auto loc = fs.loc;
@@ -827,7 +808,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
}
Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k));
auto var = new VarDeclaration(loc, p.type, p.ident, ie);
- var.storage_class |= STC.manifest;
+ var.storage_class |= STC.foreach_ | STC.manifest;
static if(isStatic) var.storage_class |= STC.local;
static if(!isDecl)
{
@@ -919,8 +900,9 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
e = resolveProperties(sc, e);
Initializer ie = new ExpInitializer(Loc.initial, e);
auto v = new VarDeclaration(loc, type, ident, ie, storageClass);
+ v.storage_class |= STC.foreach_;
if (storageClass & STC.ref_)
- v.storage_class |= STC.ref_ | STC.foreach_;
+ v.storage_class |= STC.ref_;
if (isStatic || storageClass&STC.manifest || e.isConst() ||
e.op == TOK.string_ ||
e.op == TOK.structLiteral ||
@@ -1057,23 +1039,17 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
ls.gotoTarget = res;
if (te && te.e0)
res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res);
+ result.statement = res;
}
else static if (!isDecl)
{
- Statement res = new CompoundStatement(loc, statements);
- }
- else
- {
- auto res = declarations;
- }
- static if (!isDecl)
- {
- result = res;
+ result.statement = new CompoundStatement(loc, statements);
}
else
{
- return res;
+ result.decl = declarations;
}
+ return result;
}
override void visit(ForeachStatement fs)
@@ -1202,10 +1178,10 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
if (tab.ty == Ttuple) // don't generate new scope for tuple loops
{
- makeTupleForeach!(false,false)(fs);
+ Statement s = makeTupleForeach!(false,false)(fs, null, false).statement;
if (vinit)
- result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result);
- result = result.statementSemantic(sc);
+ s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s);
+ result = s.statementSemantic(sc);
return;
}
@@ -2727,7 +2703,8 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
needswitcherror = true;
}
- if (!sc.sw.sdefault && (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on))
+ if (!sc.sw.sdefault && !(sc.flags & SCOPE.Cfile) &&
+ (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on))
{
ss.hasNoDefault = 1;
@@ -3061,7 +3038,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
if (lval - fval > 256)
{
- crs.error("had %llu cases which is more than 256 cases in case range", lval - fval);
+ crs.error("had %llu cases which is more than 257 cases in case range", 1 + lval - fval);
errors = true;
lval = fval + 256;
}
@@ -3295,12 +3272,14 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
if (e0)
e0 = e0.optimize(WANTvalue);
- /* Void-return function can have void typed expression
+ /* Void-return function can have void / noreturn typed expression
* on return statement.
*/
- if (tbret && tbret.ty == Tvoid || rs.exp.type.ty == Tvoid)
+ const convToVoid = rs.exp.type.ty == Tvoid || rs.exp.type.ty == Tnoreturn;
+
+ if (tbret && tbret.ty == Tvoid || convToVoid)
{
- if (rs.exp.type.ty != Tvoid)
+ if (!convToVoid)
{
rs.error("cannot return non-void from `void` function");
errors = true;
@@ -3345,7 +3324,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
}
else if (rs.exp.op != TOK.error)
{
- rs.error("Expected return type of `%s`, not `%s`:",
+ rs.error("expected return type of `%s`, not `%s`:",
tret.toChars(),
rs.exp.type.toChars());
errorSupplemental((fd.returns) ? (*fd.returns)[0].loc : fd.loc,
@@ -3409,10 +3388,20 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
}
else
{
+ // Type of the returned expression (if any), might've been moved to e0
+ auto resType = e0 ? e0.type : Type.tvoid;
+
// infer return type
if (fd.inferRetType)
{
- if (tf.next && tf.next.ty != Tvoid)
+ // 1. First `return <noreturn exp>?`
+ // 2. Potentially found a returning branch, update accordingly
+ if (!tf.next || tf.next.toBasetype().isTypeNoreturn())
+ {
+ tf.next = resType; // infer void or noreturn
+ }
+ // Found an actual return value before
+ else if (tf.next.ty != Tvoid && !resType.toBasetype().isTypeNoreturn())
{
if (tf.next.ty != Terror)
{
@@ -3421,20 +3410,23 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
errors = true;
tf.next = Type.terror;
}
- else
- tf.next = Type.tvoid;
- tret = tf.next;
+ tret = tf.next;
tbret = tret.toBasetype();
}
if (inferRef) // deduce 'auto ref'
tf.isref = false;
- if (tbret.ty != Tvoid) // if non-void return
+ if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return
{
if (tbret.ty != Terror)
- rs.error("`return` expression expected");
+ {
+ if (e0)
+ rs.error("expected return type of `%s`, not `%s`", tret.toChars(), resType.toChars());
+ else
+ rs.error("`return` expression expected");
+ }
errors = true;
}
else if (fd.isMain())
@@ -3522,7 +3514,12 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
}
else
{
- result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs);
+ auto es = new ExpStatement(rs.loc, e0);
+ if (e0.type.isTypeNoreturn())
+ result = es; // Omit unreachable return;
+ else
+ result = new CompoundStatement(rs.loc, es, rs);
+
return;
}
}
@@ -4014,7 +4011,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor
/* If catch exception type is derived from Exception
*/
if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) &&
- (!c.handler || !c.handler.comeFrom()))
+ (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_))
{
// Remove c from the array of catches
tcs.catches.remove(i);
@@ -4569,45 +4566,14 @@ Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out St
}
-/*******************
- * Determines additional argument types for makeTupleForeach.
- */
-static template TupleForeachArgs(bool isStatic, bool isDecl)
-{
- alias Seq(T...)=T;
- static if(isStatic) alias T = Seq!(bool);
- else alias T = Seq!();
- static if(!isDecl) alias TupleForeachArgs = T;
- else alias TupleForeachArgs = Seq!(Dsymbols*,T);
-}
-
-/*******************
- * Determines the return type of makeTupleForeach.
- */
-static template TupleForeachRet(bool isStatic, bool isDecl)
-{
- alias Seq(T...)=T;
- static if(!isDecl) alias TupleForeachRet = Statement;
- else alias TupleForeachRet = Dsymbols*;
-}
-
-
/*******************
* See StatementSemanticVisitor.makeTupleForeach. This is a simple
* wrapper that returns the generated statements/declarations.
*/
-TupleForeachRet!(isStatic, isDecl) makeTupleForeach(bool isStatic, bool isDecl)(Scope* sc, ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args)
+auto makeTupleForeach(bool isStatic, bool isDecl)(Scope* sc, ForeachStatement fs, Dsymbols* dbody, bool needExpansion)
{
scope v = new StatementSemanticVisitor(sc);
- static if(!isDecl)
- {
- v.makeTupleForeach!(isStatic, isDecl)(fs, args);
- return v.result;
- }
- else
- {
- return v.makeTupleForeach!(isStatic, isDecl)(fs, args);
- }
+ return v.makeTupleForeach!(isStatic, isDecl)(fs, dbody, needExpansion);
}
/*********************************
@@ -4731,7 +4697,7 @@ private Statements* flatten(Statement statement, Scope* sc)
sfs.sfe.prepare(sc);
if (sfs.sfe.ready())
{
- auto s = makeTupleForeach!(true, false)(sc, sfs.sfe.aggrfe, sfs.sfe.needExpansion);
+ Statement s = makeTupleForeach!(true, false)(sc, sfs.sfe.aggrfe, null, sfs.sfe.needExpansion).statement;
auto result = s.flatten(sc);
if (result)
{
@@ -22,7 +22,7 @@ import dmd.globals;
import dmd.identifier;
import dmd.mtype;
import dmd.root.array;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.tokens;
@@ -316,7 +316,8 @@ struct TargetC
enum BitFieldStyle : ubyte
{
Unspecified,
- Dm_Ms, /// Digital Mars and Microsoft C compilers
+ DM, /// Digital Mars 32 bit C compiler
+ MS, /// Microsoft 32 and 64 bit C compilers
/// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160
/// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160
Gcc_Clang, /// gcc and clang
@@ -63,7 +63,8 @@ struct TargetC
enum class BitFieldStyle : unsigned char
{
Unspecified,
- Dm_Ms, // Digital Mars and Microsoft C compilers
+ DM, // Digital Mars 32 bit C compiler
+ MS, // Microsoft 32 and 64 bit C compilers
// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160
// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160
Gcc_Clang, // gcc and clang
@@ -131,7 +131,7 @@ public:
virtual bool declareParameter(Scope *sc) = 0;
virtual void print(RootObject *oarg, RootObject *oded) = 0;
virtual RootObject *specialization() = 0;
- virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0;
+ virtual RootObject *defaultArg(const Loc &instLoc, Scope *sc) = 0;
virtual bool hasDefaultArg() = 0;
/* Create dummy argument based on parameter.
@@ -154,7 +154,7 @@ public:
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
- RootObject *defaultArg(Loc instLoc, Scope *sc);
+ RootObject *defaultArg(const Loc &instLoc, Scope *sc);
bool hasDefaultArg();
RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
@@ -186,7 +186,7 @@ public:
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
- RootObject *defaultArg(Loc instLoc, Scope *sc);
+ RootObject *defaultArg(const Loc &instLoc, Scope *sc);
bool hasDefaultArg();
RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
@@ -207,7 +207,7 @@ public:
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
- RootObject *defaultArg(Loc instLoc, Scope *sc);
+ RootObject *defaultArg(const Loc &instLoc, Scope *sc);
bool hasDefaultArg();
RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
@@ -224,7 +224,7 @@ public:
bool declareParameter(Scope *sc);
void print(RootObject *oarg, RootObject *oded);
RootObject *specialization();
- RootObject *defaultArg(Loc instLoc, Scope *sc);
+ RootObject *defaultArg(const Loc &instLoc, Scope *sc);
bool hasDefaultArg();
RootObject *dummyArg();
void accept(Visitor *v) { v->visit(this); }
@@ -19,7 +19,7 @@ import core.stdc.string;
import dmd.globals;
import dmd.identifier;
import dmd.root.ctfloat;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rmem;
import dmd.utf;
@@ -187,6 +187,7 @@ enum
TOKarrow,
TOKcolonColon,
TOKwchar_tLiteral,
+ TOKcompoundLiteral,
TOKinline,
TOKregister,
@@ -49,7 +49,7 @@ import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
import dmd.root.rootobject;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.string;
enum LOGSEMANTIC = false;
@@ -1410,19 +1410,30 @@ Expression semanticTraits(TraitsExp e, Scope* sc)
auto o = (*e.args)[0];
auto o1 = (*e.args)[1];
- FuncDeclaration fd;
- TypeFunction tf = toTypeFunction(o, fd);
-
ParameterList fparams;
- if (tf)
- fparams = tf.parameterList;
- else if (fd)
- fparams = fd.getParameterList();
+
+ CallExp ce;
+ if (auto exp = isExpression(o))
+ ce = exp.isCallExp();
+
+ if (ce)
+ {
+ fparams = ce.f.getParameterList();
+ }
else
{
- e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function",
- o.toChars(), o1.toChars());
- return ErrorExp.get();
+ FuncDeclaration fd;
+ auto tf = toTypeFunction(o, fd);
+ if (tf)
+ fparams = tf.parameterList;
+ else if (fd)
+ fparams = fd.getParameterList();
+ else
+ {
+ e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function or a function call",
+ o.toChars(), o1.toChars());
+ return ErrorExp.get();
+ }
}
// Avoid further analysis for invalid functions leading to misleading error messages
@@ -44,6 +44,7 @@ import dmd.hdrgen;
import dmd.id;
import dmd.identifier;
import dmd.imphint;
+import dmd.importc;
import dmd.init;
import dmd.initsem;
import dmd.visitor;
@@ -53,7 +54,7 @@ import dmd.opover;
import dmd.parse;
import dmd.root.ctfloat;
import dmd.root.rmem;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.rootobject;
import dmd.root.string;
import dmd.root.stringtable;
@@ -1334,6 +1335,8 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
continue;
}
+ fparam.type = fparam.type.cAdjustParamType(sc); // adjust C array and function parameter types
+
Type t = fparam.type.toBasetype();
/* If fparam after semantic() turns out to be a tuple, the number of parameters may
@@ -1378,6 +1381,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl);
}
fparam.type = new TypeTuple(newparams);
+ fparam.type = fparam.type.typeSemantic(loc, argsc);
}
fparam.storageClass = STC.parameter;
@@ -1627,7 +1631,8 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
errors = true;
}
- if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0)
+ if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 &&
+ !(sc.flags & SCOPE.Cfile))
{
.error(loc, "variadic functions with non-D linkage must have at least one parameter");
errors = true;
@@ -1750,10 +1755,10 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
{
// if there was an error evaluating the symbol, it might actually
// be a type. Avoid misleading error messages.
- .error(loc, "`%s` had previous errors", mtype.toChars());
+ .error(loc, "`%s` had previous errors", mtype.toChars());
}
else
- .error(loc, "`%s` is used as a type", mtype.toChars());
+ .error(loc, "`%s` is used as a type", mtype.toChars());
return error();
}
return t;
@@ -2133,6 +2138,15 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc)
* struct S { int a; } *s;
*/
sd.members = mtype.members;
+ if (sd.semanticRun == PASS.semanticdone)
+ {
+ /* The first semantic pass marked `sd` as an opaque struct.
+ * Re-run semantic so that all newly assigned members are
+ * picked up and added to the symtab.
+ */
+ sd.semanticRun = PASS.semantic;
+ sd.dsymbolSemantic(sc);
+ }
}
else
{
@@ -2264,7 +2278,7 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc)
* Returns:
* the type that was merged
*/
-Type merge(Type type)
+extern (C++) Type merge(Type type)
{
switch (type.ty)
{
@@ -2361,7 +2375,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden
{
const explicitAlignment = mt.alignment();
const naturalAlignment = mt.alignsize();
- const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment);
+ const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get());
e = new IntegerExp(loc, actualAlignment, Type.tsize_t);
}
else if (ident == Id._init)
@@ -3734,10 +3748,18 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
}
/***************************************
- * Figures out what to do with an undefined member reference
- * for classes and structs.
- *
- * If flag & 1, don't report "not a property" error and just return NULL.
+ * `ident` was not found as a member of `mt`.
+ * Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`.
+ * If that fails, forward to visitType().
+ * Params:
+ * mt = class or struct
+ * sc = context
+ * e = `this` for `ident`
+ * ident = name of member
+ * flag = flag & 1, don't report "not a property" error and just return NULL.
+ * flag & DotExpFlag.noAliasThis, don't do 'alias this' resolution.
+ * Returns:
+ * resolved expression if found, otherwise null
*/
Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
{
@@ -3828,7 +3850,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
/* See if we should forward to the alias this.
*/
- auto alias_e = resolveAliasThis(sc, e, gagError);
+ auto alias_e = flag & DotExpFlag.noAliasThis ? null
+ : resolveAliasThis(sc, e, gagError);
if (alias_e && alias_e != e)
{
/* Rewrite e.ident as:
@@ -4611,7 +4634,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag)
* Returns:
* The initialization expression for the type.
*/
-Expression defaultInit(Type mt, const ref Loc loc)
+extern (C++) Expression defaultInit(Type mt, const ref Loc loc)
{
Expression visitBasic(TypeBasic mt)
{
@@ -4744,6 +4767,7 @@ Expression defaultInit(Type mt, const ref Loc loc)
}
auto cond = IntegerExp.createBool(false);
auto msg = new StringExp(loc, "Accessed expression of type `noreturn`");
+ msg.type = Type.tstring;
auto ae = new AssertExp(loc, cond, msg);
ae.type = mt;
return ae;
@@ -1,5 +1,5 @@
/**
- * Generate `TypeInfo` objects, which are needed for run-time introspection of classes.
+ * Generate `TypeInfo` objects, which are needed for run-time introspection of types.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
@@ -11,9 +11,87 @@
module dmd.typinf;
+import dmd.astenums;
+import dmd.declaration;
+import dmd.dmodule;
import dmd.dscope;
+import dmd.dclass;
+import dmd.dstruct;
+import dmd.errors;
import dmd.globals;
+import dmd.gluelayer;
import dmd.mtype;
+import dmd.visitor;
+import core.stdc.stdio;
+
+/****************************************************
+ * Generates the `TypeInfo` object associated with `torig` if it
+ * hasn't already been generated
+ * Params:
+ * loc = the location for reporting line numbers in errors
+ * torig = the type to generate the `TypeInfo` object for
+ * sc = the scope
+ */
+extern (C++) void genTypeInfo(const ref Loc loc, Type torig, Scope* sc)
+{
+ // printf("genTypeInfo() %s\n", torig.toChars());
+
+ // Even when compiling without `useTypeInfo` (e.g. -betterC) we should
+ // still be able to evaluate `TypeInfo` at compile-time, just not at runtime.
+ // https://issues.dlang.org/show_bug.cgi?id=18472
+ if (!sc || !(sc.flags & SCOPE.ctfe))
+ {
+ if (!global.params.useTypeInfo)
+ {
+ .error(loc, "`TypeInfo` cannot be used with -betterC");
+ fatal();
+ }
+ }
+
+ if (!Type.dtypeinfo)
+ {
+ .error(loc, "`object.TypeInfo` could not be found, but is implicitly used");
+ fatal();
+ }
+
+ Type t = torig.merge2(); // do this since not all Type's are merge'd
+ if (!t.vtinfo)
+ {
+ if (t.isShared()) // does both 'shared' and 'shared const'
+ t.vtinfo = TypeInfoSharedDeclaration.create(t);
+ else if (t.isConst())
+ t.vtinfo = TypeInfoConstDeclaration.create(t);
+ else if (t.isImmutable())
+ t.vtinfo = TypeInfoInvariantDeclaration.create(t);
+ else if (t.isWild())
+ t.vtinfo = TypeInfoWildDeclaration.create(t);
+ else
+ t.vtinfo = getTypeInfoDeclaration(t);
+ assert(t.vtinfo);
+
+ // ClassInfos are generated as part of ClassDeclaration codegen
+ const isUnqualifiedClassInfo = (t.ty == Tclass && !t.mod);
+
+ // generate a COMDAT for other TypeInfos not available as builtins in
+ // druntime
+ if (!isUnqualifiedClassInfo && !builtinTypeInfo(t))
+ {
+ if (sc) // if in semantic() pass
+ {
+ // Find module that will go all the way to an object file
+ Module m = sc._module.importedFrom;
+ m.members.push(t.vtinfo);
+ }
+ else // if in obj generation pass
+ {
+ toObjFile(t.vtinfo, global.params.multiobj);
+ }
+ }
+ }
+ if (!torig.vtinfo)
+ torig.vtinfo = t.vtinfo; // Types aren't merged, but we can share the vtinfo's
+ assert(torig.vtinfo);
+}
/****************************************************
* Gets the type of the `TypeInfo` object associated with `t`
@@ -24,5 +102,161 @@ import dmd.mtype;
* Returns:
* The type of the `TypeInfo` object associated with `t`
*/
-extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc);
+extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc);
+
+private TypeInfoDeclaration getTypeInfoDeclaration(Type t)
+{
+ //printf("Type::getTypeInfoDeclaration() %s\n", t.toChars());
+ switch (t.ty)
+ {
+ case Tpointer:
+ return TypeInfoPointerDeclaration.create(t);
+ case Tarray:
+ return TypeInfoArrayDeclaration.create(t);
+ case Tsarray:
+ return TypeInfoStaticArrayDeclaration.create(t);
+ case Taarray:
+ return TypeInfoAssociativeArrayDeclaration.create(t);
+ case Tstruct:
+ return TypeInfoStructDeclaration.create(t);
+ case Tvector:
+ return TypeInfoVectorDeclaration.create(t);
+ case Tenum:
+ return TypeInfoEnumDeclaration.create(t);
+ case Tfunction:
+ return TypeInfoFunctionDeclaration.create(t);
+ case Tdelegate:
+ return TypeInfoDelegateDeclaration.create(t);
+ case Ttuple:
+ return TypeInfoTupleDeclaration.create(t);
+ case Tclass:
+ if ((cast(TypeClass)t).sym.isInterfaceDeclaration())
+ return TypeInfoInterfaceDeclaration.create(t);
+ else
+ return TypeInfoClassDeclaration.create(t);
+
+ default:
+ return TypeInfoDeclaration.create(t);
+ }
+}
+
+/**************************************************
+ * Returns:
+ * true if any part of type t is speculative.
+ * if t is null, returns false.
+ */
+bool isSpeculativeType(Type t)
+{
+ static bool visitVector(TypeVector t)
+ {
+ return isSpeculativeType(t.basetype);
+ }
+
+ static bool visitAArray(TypeAArray t)
+ {
+ return isSpeculativeType(t.index) ||
+ isSpeculativeType(t.next);
+ }
+
+ static bool visitStruct(TypeStruct t)
+ {
+ StructDeclaration sd = t.sym;
+ if (auto ti = sd.isInstantiated())
+ {
+ if (!ti.needsCodegen())
+ {
+ if (ti.minst || sd.requestTypeInfo)
+ return false;
+
+ /* https://issues.dlang.org/show_bug.cgi?id=14425
+ * TypeInfo_Struct would refer the members of
+ * struct (e.g. opEquals via xopEquals field), so if it's instantiated
+ * in speculative context, TypeInfo creation should also be
+ * stopped to avoid 'unresolved symbol' linker errors.
+ */
+ /* When -debug/-unittest is specified, all of non-root instances are
+ * automatically changed to speculative, and here is always reached
+ * from those instantiated non-root structs.
+ * Therefore, if the TypeInfo is not auctually requested,
+ * we have to elide its codegen.
+ */
+ return true;
+ }
+ }
+ else
+ {
+ //assert(!sd.inNonRoot() || sd.requestTypeInfo); // valid?
+ }
+ return false;
+ }
+
+ static bool visitClass(TypeClass t)
+ {
+ ClassDeclaration sd = t.sym;
+ if (auto ti = sd.isInstantiated())
+ {
+ if (!ti.needsCodegen() && !ti.minst)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool visitTuple(TypeTuple t)
+ {
+ if (t.arguments)
+ {
+ foreach (arg; *t.arguments)
+ {
+ if (isSpeculativeType(arg.type))
+ 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 Tstruct: return visitStruct(tb.isTypeStruct());
+ case Tclass: return visitClass(tb.isTypeClass());
+ case Ttuple: return visitTuple(tb.isTypeTuple());
+ case Tenum: return false;
+ default:
+ return isSpeculativeType(tb.nextOf());
+
+ /* For TypeFunction, TypeInfo_Function doesn't store parameter types,
+ * so only the .next (the return type) is checked here.
+ */
+ }
+}
+
+/* ========================================================================= */
+
+/* Indicates whether druntime already contains an appropriate TypeInfo instance
+ * for the specified type (in module rt.util.typeinfo).
+ */
+extern (C++) bool builtinTypeInfo(Type t)
+{
+ if (!t.mod) // unqualified types only
+ {
+ // unqualified basic types + typeof(null)
+ if (t.isTypeBasic() || t.ty == Tnull)
+ return true;
+ // some unqualified arrays
+ if (t.ty == Tarray)
+ {
+ Type next = t.nextOf();
+ return (next.isTypeBasic() && !next.mod) // of unqualified basic types
+ || (next.ty == Tchar && next.mod == MODFlags.immutable_) // string
+ || (next.ty == Tchar && next.mod == MODFlags.const_); // const(char)[]
+ }
+ }
+ return false;
+}
deleted file mode 100644
@@ -16,7 +16,7 @@ import dmd.errors;
import dmd.globals;
import dmd.root.file;
import dmd.root.filename;
-import dmd.root.outbuffer;
+import dmd.common.outbuffer;
import dmd.root.string;
@@ -1757,7 +1757,7 @@ public:
/* C++ constructors return void, even though front-end semantic
treats them as implicitly returning `this'. Set returnvalue
to override the result of this expression. */
- if (fd->isCtorDeclaration () && fd->linkage == LINK::cpp)
+ if (fd->isCtorDeclaration ())
{
thisexp = d_save_expr (thisexp);
returnvalue = thisexp;
@@ -1846,6 +1846,11 @@ public:
{
tree init = TARGET_EXPR_INITIAL (cleanup);
TARGET_EXPR_INITIAL (cleanup) = compound_expr (init, exp);
+
+ /* Keep the return value outside the TARGET_EXPR. */
+ if (returnvalue != NULL_TREE)
+ cleanup = compound_expr (cleanup, TREE_OPERAND (exp, 1));
+
exp = cleanup;
}
@@ -1856,7 +1861,7 @@ public:
void visit (DelegateExp *e)
{
- if (e->func->semanticRun == PASSsemantic3done)
+ if (e->func->semanticRun == PASS::semantic3done)
{
/* Add the function as nested function if it belongs to this module.
ie: it is a member of this module, or it is a template instance. */
@@ -283,6 +283,13 @@ Sets @code{__traits(getTargetInfo "cppStd")} to @code{202002}.
@cindex @option{-fno-invariants}
Turns off code generation for class @code{invariant} contracts.
+@item -fmain
+@cindex @option{-fmain}
+Generates a default @code{main()} function when compiling. This is useful when
+unittesting a library, as it enables running the unittests in a library without
+having to manually define an entry-point function. This option does nothing
+when @code{main} is already defined in user code.
+
@item -fno-moduleinfo
@cindex @option{-fmoduleinfo}
@cindex @option{-fno-moduleinfo}
@@ -742,6 +749,8 @@ List information on all D language transitions.
List all usages of complex or imaginary types.
@item field
List all non-mutable fields which occupy an object instance.
+@item in
+List all usages of @code{in} on parameter.
@item nogc
List all hidden GC allocations.
@item templates
@@ -436,6 +436,10 @@ ftransition=field
D RejectNegative
List all non-mutable fields which occupy an object instance.
+ftransition=in
+D RejectNegative
+List all usages of 'in' on parameter.
+
ftransition=nogc
D RejectNegative
List all hidden GC allocations.
@@ -145,7 +145,7 @@ get_internal_fn (tree ident, const Visibility &visibility)
fd->loc = Loc (mod->srcfile.toChars (), 1, 0);
fd->parent = mod;
fd->visibility = visibility;
- fd->semanticRun = PASSsemantic3done;
+ fd->semanticRun = PASS::semantic3done;
return fd;
}
@@ -610,7 +610,8 @@ public:
void visit (TypeNoreturn *t)
{
- t->ctype = void_type_node;
+ t->ctype = noreturn_type_node;
+ TYPE_NAME (t->ctype) = get_identifier (t->toChars ());
}
/* Basic Data Types. */
@@ -770,11 +771,17 @@ public:
fnparams = chainon (fnparams, build_tree_list (0, type));
}
- size_t n_args = t->parameterList.length ();
+ const size_t n_args = t->parameterList.length ();
for (size_t i = 0; i < n_args; i++)
{
tree type = parameter_type (t->parameterList[i]);
+
+ /* Type `noreturn` is a terminator, as no other arguments can possibly
+ be evaluated after it. */
+ if (type == noreturn_type_node)
+ break;
+
fnparams = chainon (fnparams, build_tree_list (0, type));
}
@@ -797,6 +804,10 @@ public:
TYPE_LANG_SPECIFIC (t->ctype) = build_lang_type (t);
d_keep (t->ctype);
+ /* Qualify function types that have the type `noreturn` as volatile. */
+ if (fntype == noreturn_type_node)
+ t->ctype = build_qualified_type (t->ctype, TYPE_QUAL_VOLATILE);
+
/* Handle any special support for calling conventions. */
switch (t->linkage)
{
@@ -995,8 +1006,8 @@ public:
the context or laying out fields as those types may make
recursive references to this type. */
unsigned structsize = t->sym->structsize;
- unsigned alignsize = (t->sym->alignment != STRUCTALIGN_DEFAULT)
- ? t->sym->alignment : t->sym->alignsize;
+ unsigned alignsize = t->sym->alignment.isDefault ()
+ ? t->sym->alignsize : t->sym->alignment.get ();
TYPE_SIZE (t->ctype) = bitsize_int (structsize * BITS_PER_UNIT);
TYPE_SIZE_UNIT (t->ctype) = size_int (structsize);