d: Merge upstream dmd 568496d5b, druntime 178c44ff, phobos 574bf883b.

D front-end changes:

    - Import dmd v2.098.0
    - New ImportC module for compiling preprocessed C11 code into D.
    - New -ftransition=in switch.
    - Improved handling of new 'noreturn' type.

Druntime changes:

    - Import druntime v2.098.0
    - Fix broken import in core.sys.linux.perf_event module (PR103558).

Phobos changes:

    - Import phobos v2.098.0
    - All sources are now compiled with -fpreview=fieldwise.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd 568496d5b.
	* Make-lang.in (D_FRONTEND_OBJS): Add d/common-file.o,
	d/common-outbuffer.o, d/common-string.o, d/file_manager.o,
	d/importc.o.  Remove d/root-outbuffer.o.
	(d/common-%.o): New recipe.
	* d-builtins.cc (build_frontend_type): Update for new front-end
	interface.
	(d_build_d_type_nodes): Set noreturn_type_node.
	* d-codegen.cc (d_build_call): Don't call function if one of the
	arguments is type 'noreturn'.
	(build_vthis_function): Propagate TYPE_QUAL_VOLATILE from original
	function type.
	* d-frontend.cc (eval_builtin): Update signature.
	(getTypeInfoType): Likewise.
	(toObjFile): New function.
	* d-gimplify.cc (d_gimplify_call_expr): Always evaluate arguments from
	left to right.
	* d-lang.cc (d_handle_option): Handle OPT_ftransition_in.
	(d_parse_file): Don't generate D main if it is declared in user code.
	* d-tree.h (CALL_EXPR_ARGS_ORDERED): Remove.
	(enum d_tree_index): Add DTI_BOTTOM_TYPE.
	(noreturn_type_node): New.
	* decl.cc (apply_pragma_crt): Remove.
	(DeclVisitor::visit): Update for new front-end interface.
	(DeclVisitor::visit (PragmaDeclaration *)): Don't handle
	crt_constructor and crt_destructor pragmas.
	(DeclVisitor::visit (VarDeclaration *)): Don't generate declarations
	of type 'noreturn'.
	(DeclVisitor::visit (FuncDeclaration *)): Stop adding parameters when
	'noreturn' type has been encountered.
	(get_symbol_decl): Set DECL_STATIC_CONSTRUCTOR and
	DECL_STATIC_DESTRUCTOR on decl node if requested.
	(aggregate_initializer_decl): Update for new front-end interface.
	* expr.cc (ExprVisitor::visit (CallExp *)): Always use the 'this'
	object as the result of calling any constructor function.
	(ExprVisitor::visit): Update for new front-end interface.
	* gdc.texi (Runtime Options): Document -fmain and -ftransition=in.
	* lang.opt (ftransition=in): New option.
	* modules.cc (get_internal_fn): Update for new front-end interface.
	* types.cc (TypeVisitor::visit): Likewise.
	(TypeVisitor::visit (TypeNoreturn *)): Return noreturn_type_node.
	(TypeVisitor::visit (TypeFunction *)): Stop adding parameters when
	'notreturn' type has been encountered.  Qualify function types that
	return 'noreturn' as TYPE_QUAL_VOLATILE.

libphobos/ChangeLog:

	PR d/103558
	* libdruntime/MERGE: Merge upstream druntime 178c44ff.
	* libdruntime/Makefile.am (DRUNTIME_DSOURCES_LINUX): Add
	core/sys/linux/syscalls.d.
	(DRUNTIME_DSOURCES_OPENBSD): Add core/sys/openbsd/pthread_np.d.
	* libdruntime/Makefile.in: Regenerate.
	* src/MERGE: Merge upstream phobos 574bf883b.
	* src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=fieldwise.
	* src/Makefile.in: Regenerate.
	* testsuite/libphobos.exceptions/assert_fail.d: Update test.
	* testsuite/libphobos.betterc/test22336.d: New test.
This commit is contained in:
Iain Buclaw 2021-12-05 17:11:12 +01:00
parent c15aa46cca
commit 0fb5703477
443 changed files with 11463 additions and 5094 deletions

View file

@ -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)

View file

@ -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++)

View file

@ -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

View file

@ -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);
}

View file

@ -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);
/* 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++)
{
/* 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;
/* 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 argument has a side-effect, gimplify_arg will handle it. */
if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR)
return GS_ERROR;
/* 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);
CALL_EXPR_ARG (*expr_p, i) = new_arg;
}
return GS_OK;
}
return GS_UNHANDLED;
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 argument has a side-effect, gimplify_arg will handle it. */
if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR)
return GS_ERROR;
/* 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);
CALL_EXPR_ARG (*expr_p, i) = new_arg;
}
return GS_OK;
}
/* Gimplify an UNSIGNED_RSHIFT_EXPR node. */

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -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.

View file

@ -20,6 +20,7 @@ this license for that file.
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [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 @@ Note that these groups have no strict meaning, the category assignments are a bi
| [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 that these groups have no strict meaning, the category assignments are a bi
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 |
|---------------------------------------------------------------------------------|---------------------------------------------------------------|

View file

@ -1 +1 @@
v2.097.2
v2.098.0

View file

@ -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:
// 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 = 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
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)

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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;
// 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;
if (xdtor && xdtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable)
xdtor = buildWindowsCppDtor(ad, xdtor, sc);
// 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;

View file

@ -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 |

576
gcc/d/dmd/common/file.d Normal file
View file

@ -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)();
}

View file

@ -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,18 @@ 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 automatically.
Frees resources associated.
*/
extern (C++) ~this() pure nothrow
extern (C++) void dtor() nothrow @trusted
{
if (fileMapping)
{
@ -74,10 +93,41 @@ struct OutBuffer
else
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
mem.xfree(data.ptr);
free(data.ptr);
}
}
/**
Frees resources associated automatically.
*/
extern (C++) ~this() pure nothrow @trusted
{
if (fileMapping)
{
if (fileMapping.active)
fileMapping.close();
fileMapping = null;
}
else
{
debug (stomp) memset(data.ptr, 0xFF, data.length);
pureFree(data.ptr);
}
}
/// 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);

View file

@ -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;

209
gcc/d/dmd/common/string.d Normal file
View file

@ -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]);
}

View file

@ -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)

View file

@ -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);
}
//}
}

View file

@ -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);

View file

@ -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 ||

View file

@ -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))

View file

@ -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;

View file

@ -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)
{
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 (!fieldState.inFlight)
{
startNewField();
}
if (0 && target.os & Target.OS.Posix)
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)
{
while (bitOffset > memsize * 8)
{
bitOffset -= 8;
offset += 1;
}
}
//fieldState.fieldSize = memsize;
const pastField = bitOffset + fieldWidth;
if (style == TargetC.BitFieldStyle.Gcc_Clang)
{
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);
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)

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);

View file

@ -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;
}
}
/**********************************

View file

@ -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;

View file

@ -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,9 +795,17 @@ 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))
{
@ -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();
}

View file

@ -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;
};

View file

@ -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();
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();
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)
{

View file

@ -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());
}
}

View file

@ -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

View file

@ -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;
/***********************************************************

View file

@ -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);
}

View file

@ -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;

View file

@ -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
// return mangleof as an Expression
static Expression dotMangleof(const ref Loc loc, Scope* sc, Dsymbol ds)
{
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))
{
f.error(loc, "cannot retrieve its `.mangleof` while inferring attributes");
return ErrorExp.get();
}
}
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_:
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_:
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 = cast(TemplateExp)exp.e1;
ds = te.fd ? cast(Dsymbol)te.fd : te.td;
TemplateExp te = exp.e1.isTemplateExp();
return dotMangleof(exp.loc, sc, ds = te.fd ? te.fd.isDsymbol() : te.td);
}
L1:
{
assert(ds);
if (auto f = ds.isFuncDeclaration())
{
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();
}
}
OutBuffer buf;
mangleToBuffer(ds, &buf);
Expression e = new StringExp(exp.loc, buf.extractSlice());
e = e.expressionSemantic(sc);
return e;
}
default:
break;
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()))
{

301
gcc/d/dmd/file_manager.d Normal file
View file

@ -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();
}
}

View file

@ -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);
};

View file

@ -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;
}

View file

@ -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:

View file

@ -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();

View file

@ -78,6 +78,7 @@ else version (IN_GCC)
extern (C++)
{
Statement asmSemantic(AsmStatement s, Scope* sc);
void toObjFile(Dsymbol ds, bool multiobj);
}
// stubs

View file

@ -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());
}

View file

@ -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);

View file

@ -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" },
];

View file

@ -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;

171
gcc/d/dmd/importc.d Normal file
View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -1,177 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by KennyTM
* 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/intrange.h
*/
#pragma once
#include "globals.h" // for uinteger_t
class Type;
class Expression;
/**
This class represents a "sign-extended number", i.e. a 65-bit number, which can
represent all built-in integer types in D. This class is mainly used for
performing value-range propagation only, therefore all arithmetic are done with
saturation, not wrapping as usual.
*/
struct SignExtendedNumber
{
/// The lower 64-bit of the number.
uinteger_t value;
/// The sign (i.e. the most significant bit) of the number.
bool negative;
/// Create an uninitialized sign-extended number.
SignExtendedNumber() {}
/// Create a sign-extended number from an unsigned 64-bit number.
SignExtendedNumber(uinteger_t value_)
: value(value_), negative(false) {}
/// Create a sign-extended number from the lower 64-bit and the sign bit.
SignExtendedNumber(uinteger_t value_, bool negative_)
: value(value_), negative(negative_) {}
/// Create a sign-extended number from a signed 64-bit number.
static SignExtendedNumber fromInteger(uinteger_t value_);
/// Get the minimum or maximum value of a sign-extended number.
static SignExtendedNumber extreme(bool minimum);
// These names probably shouldn't be used anyway, as they are common macros
#undef max
#undef min
static SignExtendedNumber max();
static SignExtendedNumber min() { return SignExtendedNumber(0, true); }
/// Check if the sign-extended number is minimum or zero.
bool isMinimum() const { return negative && value == 0; }
/// Compare two sign-extended number.
bool operator==(const SignExtendedNumber&) const;
bool operator!=(const SignExtendedNumber& a) const { return !(*this == a); }
bool operator<(const SignExtendedNumber&) const;
bool operator>(const SignExtendedNumber& a) const { return a < *this; }
bool operator<=(const SignExtendedNumber& a) const { return !(a < *this); }
bool operator>=(const SignExtendedNumber& a) const { return !(*this < a); }
/// Increase the sign-extended number by 1 (saturated).
SignExtendedNumber& operator++();
/// Compute the saturated complement of a sign-extended number.
SignExtendedNumber operator~() const;
/// Compute the saturated negation of a sign-extended number.
SignExtendedNumber operator-() const;
/// Compute the saturated binary and of two sign-extended number.
SignExtendedNumber operator&(const SignExtendedNumber&) const;
/// Compute the saturated binary or of two sign-extended number.
SignExtendedNumber operator|(const SignExtendedNumber&) const;
/// Compute the saturated binary xor of two sign-extended number.
SignExtendedNumber operator^(const SignExtendedNumber&) const;
/// Compute the saturated sum of two sign-extended number.
SignExtendedNumber operator+(const SignExtendedNumber&) const;
/// Compute the saturated difference of two sign-extended number.
SignExtendedNumber operator-(const SignExtendedNumber&) const;
/// Compute the saturated product of two sign-extended number.
SignExtendedNumber operator*(const SignExtendedNumber&) const;
/// Compute the saturated quotient of two sign-extended number.
SignExtendedNumber operator/(const SignExtendedNumber&) const;
/// Compute the saturated modulus of two sign-extended number.
SignExtendedNumber operator%(const SignExtendedNumber&) const;
/// Compute the saturated shifts of two sign-extended number.
SignExtendedNumber operator<<(const SignExtendedNumber&) const;
SignExtendedNumber operator>>(const SignExtendedNumber&) const;
};
/**
This class represents a range of integers, denoted by its lower and upper bounds
(inclusive).
*/
struct IntRange
{
SignExtendedNumber imin, imax;
/// Create an uninitialized range.
IntRange() {}
/// Create a range consisting of a single number.
IntRange(const SignExtendedNumber& a)
: imin(a), imax(a) {}
/// Create a range with the lower and upper bounds.
IntRange(const SignExtendedNumber& lower, const SignExtendedNumber& upper)
: imin(lower), imax(upper) {}
/// Create the tightest range containing all valid integers in the specified
/// type.
static IntRange fromType(Type *type);
/// Create the tightest range containing all valid integers in the type with
/// a forced signedness.
static IntRange fromType(Type *type, bool isUnsigned);
/// Create the tightest range containing all specified numbers.
static IntRange fromNumbers2(const SignExtendedNumber numbers[2]);
static IntRange fromNumbers4(const SignExtendedNumber numbers[4]);
/// Create the widest range possible.
static IntRange widest();
/// Cast the integer range to a signed type with the given size mask.
IntRange& castSigned(uinteger_t mask);
/// Cast the integer range to an unsigned type with the given size mask.
IntRange& castUnsigned(uinteger_t mask);
/// Cast the integer range to the dchar type.
IntRange& castDchar();
/// Cast the integer range to a specific type.
IntRange& cast(Type *type);
/// Cast the integer range to a specific type, forcing it to be unsigned.
IntRange& castUnsigned(Type *type);
/// Check if this range contains another range.
bool contains(const IntRange& a) const;
/// Check if this range contains 0.
bool containsZero() const;
/// Compute the range of the negated absolute values of the original range.
IntRange absNeg() const;
/// Compute the union of two ranges.
IntRange unionWith(const IntRange& other) const;
void unionOrAssign(const IntRange& other, bool& union_);
/// Dump the content of the integer range to the console.
const IntRange& dump(const char* funcName, Expression *e) const;
/// Split the range into two nonnegative- and negative-only subintervals.
void splitBySign(IntRange& negRange, bool& hasNegRange,
IntRange& nonNegRange, bool& hasNonNegRange) const;
/// Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
/// https://github.com/tgehr/d-compiler/blob/master/vrange.d
static SignExtendedNumber maxOr(const IntRange&, const IntRange&);
static SignExtendedNumber minOr(const IntRange&, const IntRange&);
static SignExtendedNumber maxAnd(const IntRange&, const IntRange&);
static SignExtendedNumber minAnd(const IntRange&, const IntRange&);
static void swap(IntRange&, IntRange&);
IntRange operator~() const;
IntRange operator-() const;
IntRange operator&(const IntRange&) const;
IntRange operator|(const IntRange&) const;
IntRange operator^(const IntRange&) const;
IntRange operator+(const IntRange&) const;
IntRange operator-(const IntRange&) const;
IntRange operator*(const IntRange&) const;
IntRange operator/(const IntRange&) const;
IntRange operator%(const IntRange&) const;
IntRange operator<<(const IntRange&) const;
IntRange operator>>(const IntRange&) const;
};

View file

@ -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();
}

View file

@ -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;

View file

@ -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;

View file

@ -1,75 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/lexer.h
*/
#pragma once
#include "root/root.h"
#include "globals.h"
#include "tokens.h"
struct StringTable;
class Identifier;
class Lexer
{
public:
static OutBuffer stringbuffer;
Loc scanloc; // for error messages
const utf8_t *base; // pointer to start of buffer
const utf8_t *end; // past end of buffer
const utf8_t *p; // current character
const utf8_t *line; // start of current line
Token token;
bool doDocComment; // collect doc comment information
bool anyToken; // !=0 means seen at least one token
bool commentToken; // !=0 means comments are TOKcomment's
bool errors; // errors occurred during lexing or parsing
Lexer(const char *filename,
const utf8_t *base, size_t begoffset, size_t endoffset,
bool doDocComment, bool commentToken);
TOK nextToken();
TOK peekNext();
TOK peekNext2();
void scan(Token *t);
Token *peek(Token *t);
Token *peekPastParen(Token *t);
unsigned escapeSequence();
TOK wysiwygStringConstant(Token *t, int tc);
TOK hexStringConstant(Token *t);
TOK delimitedStringConstant(Token *t);
TOK tokenStringConstant(Token *t);
TOK escapeStringConstant(Token *t);
TOK charConstant(Token *t);
void stringPostfix(Token *t);
TOK number(Token *t);
TOK inreal(Token *t);
Loc loc()
{
scanloc.charnum = (unsigned)(1 + p-line);
return scanloc;
}
void error(const char *format, ...);
void error(Loc loc, const char *format, ...);
void deprecation(const char *format, ...);
void poundLine();
unsigned decodeUTF();
void getDocComment(Token *t, unsigned lineComment);
static const utf8_t *combineComments(const utf8_t *c1, const utf8_t *c2);
private:
void endOfLine();
};

View file

@ -1,38 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/macro.h
*/
#pragma once
#include "root/dsystem.h"
#include "root/port.h"
struct Macro
{
private:
Macro *next; // next in list
const utf8_t *name; // macro name
size_t namelen; // length of macro name
const utf8_t *text; // macro replacement text
size_t textlen; // length of replacement text
int inuse; // macro is in use (don't expand)
Macro(const utf8_t *name, size_t namelen, const utf8_t *text, size_t textlen);
Macro *search(const utf8_t *name, size_t namelen);
public:
static Macro *define(Macro **ptable, const utf8_t *name, size_t namelen, const utf8_t *text, size_t textlen);
void expand(OutBuffer *buf, size_t start, size_t *pend,
const utf8_t *arg, size_t arglen);
};

View file

@ -1,93 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* 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/mars.h
*/
#pragma once
/*
It is very important to use version control macros correctly - the
idea is that host and target are independent. If these are done
correctly, cross compilers can be built.
The host compiler and host operating system are also different,
and are predefined by the host compiler. The ones used in
dmd are:
Macros defined by the compiler, not the code:
Compiler:
__DMC__ Digital Mars compiler
_MSC_VER Microsoft compiler
__GNUC__ Gnu compiler
__clang__ Clang compiler
Host operating system:
_WIN32 Microsoft NT, Windows 95, Windows 98, Win32s,
Windows 2000, Win XP, Vista
_WIN64 Windows for AMD64
__linux__ Linux
__APPLE__ Mac OSX
__FreeBSD__ FreeBSD
__OpenBSD__ OpenBSD
__sun Solaris, OpenSolaris, SunOS, OpenIndiana, etc
For the target systems, there are the target operating system and
the target object file format:
Target operating system:
TARGET_WINDOS Covers 32 bit windows and 64 bit windows
TARGET_LINUX Covers 32 and 64 bit linux
TARGET_OSX Covers 32 and 64 bit Mac OSX
TARGET_FREEBSD Covers 32 and 64 bit FreeBSD
TARGET_OPENBSD Covers 32 and 64 bit OpenBSD
TARGET_SOLARIS Covers 32 and 64 bit Solaris
It is expected that the compiler for each platform will be able
to generate 32 and 64 bit code from the same compiler binary.
There are currently no macros for byte endianness order.
*/
#include "root/dsystem.h"
#ifdef __DMC__
#ifdef DEBUG
#undef assert
#define assert(e) (static_cast<void>((e) || (printf("assert %s(%d) %s\n", __FILE__, __LINE__, #e), halt())))
#endif
#endif
void unittests();
struct OutBuffer;
#include "globals.h"
#include "root/ctfloat.h"
#include "complex_t.h"
#include "errors.h"
class Dsymbol;
class Library;
struct File;
void obj_start(char *srcfile);
void obj_end(Library *library, File *objfile);
void obj_append(Dsymbol *s);
void obj_write_deferred(Library *library);
/// Utility functions used by both main and frontend.
void readFile(Loc loc, File *f);
void writeFile(Loc loc, File *f);
void ensurePathToNameExists(Loc loc, const char *name);
const char *importHint(const char *s);
/// Little helper function for writing out deps.
void escapePath(OutBuffer *buf, const char *fname);

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -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.

View file

@ -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;

View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -1,192 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/parse.h
*/
#pragma once
#include "arraytypes.h"
#include "lexer.h"
#include "enum.h"
class Type;
class TypeQualified;
class Expression;
class Declaration;
class Statement;
class Import;
class Initializer;
class FuncDeclaration;
class CtorDeclaration;
class PostBlitDeclaration;
class DtorDeclaration;
class StaticCtorDeclaration;
class StaticDtorDeclaration;
class SharedStaticCtorDeclaration;
class SharedStaticDtorDeclaration;
class ConditionalDeclaration;
class InvariantDeclaration;
class UnitTestDeclaration;
class NewDeclaration;
class DeleteDeclaration;
class Condition;
class Module;
struct ModuleDeclaration;
class TemplateDeclaration;
class TemplateInstance;
class StaticAssert;
struct PrefixAttributes;
/************************************
* These control how parseStatement() works.
*/
enum ParseStatementFlags
{
PSsemi = 1, // empty ';' statements are allowed, but deprecated
PSscope = 2, // start a new scope
PScurly = 4, // { } statement is required
PScurlyscope = 8, // { } starts a new scope
PSsemi_ok = 0x10 // empty ';' are really ok
};
class Parser : public Lexer
{
public:
Module *mod;
ModuleDeclaration *md;
LINK linkage;
CPPMANGLE cppmangle;
Loc endloc; // set to location of last right curly
int inBrackets; // inside [] of array index or slice
Loc lookingForElse; // location of lonely if looking for an else
Parser(Loc loc, Module *module, const utf8_t *base, size_t length, bool doDocComment);
Parser(Module *module, const utf8_t *base, size_t length, bool doDocComment);
Dsymbols *parseModule();
Dsymbols *parseDeclDefs(int once, Dsymbol **pLastDecl = NULL, PrefixAttributes *pAttrs = NULL);
Dsymbols *parseAutoDeclarations(StorageClass storageClass, const utf8_t *comment);
Dsymbols *parseBlock(Dsymbol **pLastDecl, PrefixAttributes *pAttrs = NULL);
StorageClass appendStorageClass(StorageClass storageClass, StorageClass stc, bool deprec = false);
StorageClass parseAttribute(Expressions **pexps);
StorageClass parsePostfix(StorageClass storageClass, Expressions **pudas);
StorageClass parseTypeCtor();
Expression *parseConstraint();
TemplateDeclaration *parseTemplateDeclaration(bool ismixin = false);
TemplateParameters *parseTemplateParameterList(int flag = 0);
Dsymbol *parseMixin();
Objects *parseTemplateArguments();
RootObject *parseTypeOrAssignExp(TOK endtoken = TOKreserved);
Objects *parseTemplateArgumentList();
Objects *parseTemplateSingleArgument();
StaticAssert *parseStaticAssert();
TypeQualified *parseTypeof();
Type *parseVector();
LINK parseLinkage(Identifiers **, CPPMANGLE *, bool *);
Identifiers *parseQualifiedIdentifier(const char *entity);
Condition *parseDebugCondition();
Condition *parseVersionCondition();
Condition *parseStaticIfCondition();
Dsymbol *parseCtor(PrefixAttributes *pAttrs);
Dsymbol *parseDtor(PrefixAttributes *pAttrs);
Dsymbol *parseStaticCtor(PrefixAttributes *pAttrs);
Dsymbol *parseStaticDtor(PrefixAttributes *pAttrs);
Dsymbol *parseSharedStaticCtor(PrefixAttributes *pAttrs);
Dsymbol *parseSharedStaticDtor(PrefixAttributes *pAttrs);
Dsymbol *parseInvariant(PrefixAttributes *pAttrs);
Dsymbol *parseUnitTest(PrefixAttributes *pAttrs);
Dsymbol *parseNew(PrefixAttributes *pAttrs);
Dsymbol *parseDelete(PrefixAttributes *pAttrs);
Parameters *parseParameters(VarArg *pvarargs, TemplateParameters **tpl = NULL);
EnumDeclaration *parseEnum();
Dsymbol *parseAggregate();
BaseClasses *parseBaseClasses();
Dsymbols *parseImport();
Type *parseType(Identifier **pident = NULL, TemplateParameters **ptpl = NULL);
Type *parseBasicType(bool dontLookDotIdents = false);
Type *parseBasicTypeStartingAt(TypeQualified *tid, bool dontLookDotIdents);
Type *parseBasicType2(Type *t);
Type *parseDeclarator(Type *t, int *alt, Identifier **pident,
TemplateParameters **tpl = NULL, StorageClass storage_class = 0, int *pdisable = NULL, Expressions **pudas = NULL);
void parseStorageClasses(StorageClass &storage_class, LINK &link, bool &setAlignment, Expression *&ealign, Expressions *&udas);
Dsymbols *parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, const utf8_t *comment);
Dsymbol *parseFunctionLiteral();
FuncDeclaration *parseContracts(FuncDeclaration *f);
void checkDanglingElse(Loc elseloc);
void checkCstyleTypeSyntax(Loc loc, Type *t, int alt, Identifier *ident);
Statement *parseForeach(Loc loc, bool *isRange, bool isDecl);
Dsymbol *parseForeachStaticDecl(Loc loc, Dsymbol **pLastDecl);
Statement *parseForeachStatic(Loc loc);
/** endPtr used for documented unittests */
Statement *parseStatement(int flags, const utf8_t** endPtr = NULL, Loc *pEndloc = NULL);
Initializer *parseInitializer();
Expression *parseDefaultInitExp();
void check(Loc loc, TOK value);
void check(TOK value);
void check(TOK value, const char *string);
void checkParens(TOK value, Expression *e);
bool isDeclaration(Token *t, int needId, TOK endtok, Token **pt);
bool isBasicType(Token **pt);
bool isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok, bool allowAltSyntax = true);
bool isParameters(Token **pt);
bool isExpression(Token **pt);
bool skipParens(Token *t, Token **pt);
bool skipParensIf(Token *t, Token **pt);
bool skipAttributes(Token *t, Token **pt);
Expression *parseExpression();
Expression *parsePrimaryExp();
Expression *parseUnaryExp();
Expression *parsePostExp(Expression *e);
Expression *parseMulExp();
Expression *parseAddExp();
Expression *parseShiftExp();
Expression *parseCmpExp();
Expression *parseAndExp();
Expression *parseXorExp();
Expression *parseOrExp();
Expression *parseAndAndExp();
Expression *parseOrOrExp();
Expression *parseCondExp();
Expression *parseAssignExp();
Expressions *parseArguments();
Expression *parseNewExp(Expression *thisexp);
void addComment(Dsymbol *s, const utf8_t *blockComment);
};
// Operator precedence - greater values are higher precedence
enum PREC
{
PREC_zero,
PREC_expr,
PREC_assign,
PREC_cond,
PREC_oror,
PREC_andand,
PREC_or,
PREC_xor,
PREC_and,
PREC_equal,
PREC_rel,
PREC_shift,
PREC_add,
PREC_mul,
PREC_pow,
PREC_unary,
PREC_primary
};
extern PREC precedence[TOKMAX];
void initPrecedence();

View file

@ -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() : "");

View file

@ -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 |

View file

@ -1,22 +0,0 @@
/* Copyright (C) 2010-2021 by The D Language Foundation, All Rights Reserved
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
* https://github.com/D-Programming-Language/dmd/blob/master/src/root/aav.h
*/
#pragma once
#include "dsystem.h"
typedef void* Value;
typedef void* Key;
struct AA;
size_t dmd_aaLen(AA* aa);
Value* dmd_aaGet(AA** aa, Key key);
Value dmd_aaGetRvalue(AA* aa, Key key);
void dmd_aaRehash(AA** paa);

View file

@ -1,30 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 2003-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/root/checkedint.h
*/
#include "dsystem.h"
int adds(int x, int y, bool& overflow);
int64_t adds(int64_t x, int64_t y, bool& overflow);
unsigned addu(unsigned x, unsigned y, bool& overflow);
uint64_t addu(uint64_t x, uint64_t y, bool& overflow);
int subs(int x, int y, bool& overflow);
int64_t subs(int64_t x, int64_t y, bool& overflow);
unsigned subu(unsigned x, unsigned y, bool& overflow);
uint64_t subu(uint64_t x, uint64_t y, bool& overflow);
int negs(int x, bool& overflow);
int64_t negs(int64_t x, bool& overflow);
int muls(int x, int y, bool& overflow);
int64_t muls(int64_t x, int64_t y, bool& overflow);
unsigned mulu(unsigned x, unsigned y, bool& overflow);
uint64_t mulu(uint64_t x, uint64_t y, bool& overflow);

View file

@ -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)();
}

View file

@ -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);

View file

@ -1,76 +0,0 @@
/**
* Compiler implementation of the D programming language
* http://dlang.org
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: Martin Nowak, Walter Bright, http://www.digitalmars.com
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(DMDSRC root/_hash.h)
*/
#pragma once
#include "dsystem.h" // uint{8|16|32}_t
// MurmurHash2 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// https://sites.google.com/site/murmurhash/
static inline uint32_t calcHash(const uint8_t *data, size_t len)
{
// 'm' and 'r' are mixing constants generated offline.
// They're not really 'magic', they just happen to work well.
const uint32_t m = 0x5bd1e995;
const int r = 24;
// Initialize the hash to a 'random' value
uint32_t h = (uint32_t)len;
// Mix 4 bytes at a time into the hash
while(len >= 4)
{
uint32_t k = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
data += 4;
len -= 4;
}
// Handle the last few bytes of the input array
switch(len & 3)
{
case 3: h ^= data[2] << 16; /* fall through */
case 2: h ^= data[1] << 8; /* fall through */
case 1: h ^= data[0];
h *= m;
}
// Do a few final mixes of the hash to ensure the last few
// bytes are well-incorporated.
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
static inline uint32_t calcHash(const char *data, size_t len)
{
return calcHash((const uint8_t *)data, len);
}
// combine and mix two words (boost::hash_combine)
static inline size_t mixHash(size_t h, size_t k)
{
return h ^ (k + 0x9e3779b9 + (h << 6) + (h >> 2));
}

View file

@ -13,7 +13,7 @@ module dmd.root.rootobject;
import core.stdc.stdio;
import dmd.root.outbuffer;
import dmd.common.outbuffer;
/***********************************************************
*/

View file

@ -1,16 +0,0 @@
/* Copyright (C) 2010-2021 by The D Language Foundation, All Rights Reserved
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
* https://github.com/D-Programming-Language/dmd/blob/master/src/root/speller.h
*/
#pragma once
typedef void *(fp_speller_t)(void *, const char *, int*);
extern const char idchars[];
void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset);

View file

@ -1,56 +0,0 @@
/* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* 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/stringtable.h
*/
#pragma once
#include "root.h"
struct StringEntry;
// StringValue is a variable-length structure. It has neither proper c'tors nor a
// factory method because the only thing which should be creating these is StringTable.
struct StringValue
{
void *ptrvalue;
size_t length;
char *lstring() { return (char *)(this + 1); }
size_t len() const { return length; }
const char *toDchars() const { return (const char *)(this + 1); }
StringValue(); // not constructible
};
struct StringTable
{
private:
StringEntry *table;
size_t tabledim;
uint8_t **pools;
size_t npools;
size_t nfill;
size_t count;
public:
void _init(size_t size = 0);
void reset(size_t size = 0);
~StringTable();
StringValue *lookup(const char *s, size_t len);
StringValue *insert(const char *s, size_t len, void *ptrvalue);
StringValue *update(const char *s, size_t len);
int apply(int (*fp)(StringValue *));
private:
uint32_t allocValue(const char *p, size_t length, void *ptrvalue);
StringValue *getValue(uint32_t validx);
size_t findSlot(hash_t hash, const char *s, size_t len);
void grow();
};

View file

@ -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())
{

View file

@ -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.

View file

@ -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 &&

View file

@ -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);
}

View file

@ -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;

View file

@ -1,172 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
*/
#include "statement.h"
#include "visitor.h"
/* A visitor to walk entire statements and provides ability to replace any sub-statements.
*/
class StatementRewriteWalker : public Visitor
{
/* Point the currently visited statement.
* By using replaceCurrent() method, you can replace AST during walking.
*/
Statement **ps;
public:
void visitStmt(Statement *&s) { ps = &s; s->accept(this); }
void replaceCurrent(Statement *s) { *ps = s; }
void visit(ErrorStatement *) { }
void visit(PeelStatement *s)
{
if (s->s)
visitStmt(s->s);
}
void visit(ExpStatement *) { }
void visit(DtorExpStatement *) { }
void visit(CompileStatement *) { }
void visit(CompoundStatement *s)
{
if (s->statements && s->statements->length)
{
for (size_t i = 0; i < s->statements->length; i++)
{
if ((*s->statements)[i])
visitStmt((*s->statements)[i]);
}
}
}
void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); }
void visit(UnrolledLoopStatement *s)
{
if (s->statements && s->statements->length)
{
for (size_t i = 0; i < s->statements->length; i++)
{
if ((*s->statements)[i])
visitStmt((*s->statements)[i]);
}
}
}
void visit(ScopeStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(WhileStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(DoStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(ForStatement *s)
{
if (s->_init)
visitStmt(s->_init);
if (s->_body)
visitStmt(s->_body);
}
void visit(ForeachStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(ForeachRangeStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(IfStatement *s)
{
if (s->ifbody)
visitStmt(s->ifbody);
if (s->elsebody)
visitStmt(s->elsebody);
}
void visit(ConditionalStatement *) { }
void visit(PragmaStatement *) { }
void visit(StaticAssertStatement *) { }
void visit(SwitchStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(CaseStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(CaseRangeStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(DefaultStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(GotoDefaultStatement *) { }
void visit(GotoCaseStatement *) { }
void visit(SwitchErrorStatement *) { }
void visit(ReturnStatement *) { }
void visit(BreakStatement *) { }
void visit(ContinueStatement *) { }
void visit(SynchronizedStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(WithStatement *s)
{
if (s->_body)
visitStmt(s->_body);
}
void visit(TryCatchStatement *s)
{
if (s->_body)
visitStmt(s->_body);
if (s->catches && s->catches->length)
{
for (size_t i = 0; i < s->catches->length; i++)
{
Catch *c = (*s->catches)[i];
if (c && c->handler)
visitStmt(c->handler);
}
}
}
void visit(TryFinallyStatement *s)
{
if (s->_body)
visitStmt(s->_body);
if (s->finalbody)
visitStmt(s->finalbody);
}
void visit(ScopeGuardStatement *) { }
void visit(ThrowStatement *) { }
void visit(DebugStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(GotoStatement *) { }
void visit(LabelStatement *s)
{
if (s->statement)
visitStmt(s->statement);
}
void visit(AsmStatement *) { }
void visit(ImportStatement *) { }
};

View file

@ -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)
{
// Voldemort return type
union U
{
Statement statement;
Dsymbols* decl;
}
U result;
auto returnEarly()
{
static if (isDecl)
{
return null;
}
if (isDecl)
result.decl = null;
else
{
result = new ErrorStatement();
return;
}
}
static if(isDecl)
{
static assert(isStatic);
auto dbody = args[0];
}
static if(isStatic)
{
auto needExpansion = args[$-1];
assert(sc);
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);
result.statement = new CompoundStatement(loc, statements);
}
else
{
auto res = declarations;
}
static if (!isDecl)
{
result = res;
}
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)
{

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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); }

View file

@ -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;

Some files were not shown because too many files have changed in this diff Show more