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:
parent
c15aa46cca
commit
0fb5703477
443 changed files with 11463 additions and 5094 deletions
|
@ -89,6 +89,9 @@ D_FRONTEND_OBJS = \
|
|||
d/canthrow.o \
|
||||
d/chkformat.o \
|
||||
d/clone.o \
|
||||
d/common-file.o \
|
||||
d/common-outbuffer.o \
|
||||
d/common-string.o \
|
||||
d/compiler.o \
|
||||
d/complex.o \
|
||||
d/cond.o \
|
||||
|
@ -120,6 +123,7 @@ D_FRONTEND_OBJS = \
|
|||
d/escape.o \
|
||||
d/expression.o \
|
||||
d/expressionsem.o \
|
||||
d/file_manager.o \
|
||||
d/foreachvar.o \
|
||||
d/func.o \
|
||||
d/globals.o \
|
||||
|
@ -131,6 +135,7 @@ D_FRONTEND_OBJS = \
|
|||
d/identifier.o \
|
||||
d/impcnvtab.o \
|
||||
d/imphint.o \
|
||||
d/importc.o \
|
||||
d/init.o \
|
||||
d/initsem.o \
|
||||
d/inline.o \
|
||||
|
@ -157,7 +162,6 @@ D_FRONTEND_OBJS = \
|
|||
d/root-filename.o \
|
||||
d/root-hash.o \
|
||||
d/root-longdouble.o \
|
||||
d/root-outbuffer.o \
|
||||
d/root-port.o \
|
||||
d/root-region.o \
|
||||
d/root-rmem.o \
|
||||
|
@ -393,6 +397,10 @@ d/%.o: d/dmd/%.d
|
|||
$(DCOMPILE) $(D_INCLUDES) $<
|
||||
$(DPOSTCOMPILE)
|
||||
|
||||
d/common-%.o: d/dmd/common/%.d
|
||||
$(DCOMPILE) $(D_INCLUDES) $<
|
||||
$(DPOSTCOMPILE)
|
||||
|
||||
d/root-%.o: d/dmd/root/%.d
|
||||
$(DCOMPILE) $(D_INCLUDES) $<
|
||||
$(DPOSTCOMPILE)
|
||||
|
|
|
@ -236,7 +236,7 @@ build_frontend_type (tree type)
|
|||
sdecl->parent = stubmod;
|
||||
sdecl->structsize = int_size_in_bytes (type);
|
||||
sdecl->alignsize = TYPE_ALIGN_UNIT (type);
|
||||
sdecl->alignment = STRUCTALIGN_DEFAULT;
|
||||
sdecl->alignment.setDefault ();
|
||||
sdecl->sizeok = Sizeok::done;
|
||||
sdecl->type = (TypeStruct::create (sdecl))->addMod (mod);
|
||||
sdecl->type->ctype = type;
|
||||
|
@ -275,7 +275,7 @@ build_frontend_type (tree type)
|
|||
NULL);
|
||||
vd->parent = sdecl;
|
||||
vd->offset = tree_to_uhwi (byte_position (field));
|
||||
vd->semanticRun = PASSsemanticdone;
|
||||
vd->semanticRun = PASS::semanticdone;
|
||||
vd->csym = field;
|
||||
sdecl->members->push (vd);
|
||||
sdecl->fields.push (vd);
|
||||
|
@ -856,6 +856,9 @@ d_build_d_type_nodes (void)
|
|||
ireal_type_node = build_distinct_type_copy (long_double_type_node);
|
||||
TYPE_IMAGINARY_FLOAT (ireal_type_node) = 1;
|
||||
|
||||
/* Noreturn type. */
|
||||
noreturn_type_node = build_distinct_type_copy (void_type_node);
|
||||
|
||||
/* Calling build_ctype() links the front-end Type to the GCC node,
|
||||
and sets the TYPE_NAME to the D language type. */
|
||||
for (unsigned ty = 0; ty < (unsigned) TY::TMAX; ty++)
|
||||
|
|
|
@ -2140,6 +2140,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
|
|||
/* Build the argument list for the call. */
|
||||
vec <tree, va_gc> *args = NULL;
|
||||
tree saved_args = NULL_TREE;
|
||||
bool noreturn_call = false;
|
||||
|
||||
/* If this is a delegate call or a nested function being called as
|
||||
a delegate, the object should not be NULL. */
|
||||
|
@ -2165,9 +2166,9 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
|
|||
}
|
||||
}
|
||||
|
||||
size_t nparams = tf->parameterList.length ();
|
||||
const size_t nparams = tf->parameterList.length ();
|
||||
/* if _arguments[] is the first argument. */
|
||||
size_t varargs = tf->isDstyleVariadic ();
|
||||
const size_t varargs = tf->isDstyleVariadic ();
|
||||
|
||||
/* Assumes arguments->length <= formal_args->length if (!tf->varargs). */
|
||||
for (size_t i = 0; i < arguments->length; ++i)
|
||||
|
@ -2206,6 +2207,11 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
|
|||
build_address (targ));
|
||||
}
|
||||
|
||||
/* Type `noreturn` is a terminator, as no other arguments can possibly
|
||||
be evaluated after it. */
|
||||
if (TREE_TYPE (targ) == noreturn_type_node)
|
||||
noreturn_call = true;
|
||||
|
||||
vec_safe_push (args, targ);
|
||||
}
|
||||
}
|
||||
|
@ -2217,13 +2223,27 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
|
|||
saved_args = compound_expr (callee, saved_args);
|
||||
}
|
||||
|
||||
/* If we saw a `noreturn` parameter, any unreachable argument evaluations
|
||||
after it are discarded, as well as the function call itself. */
|
||||
if (noreturn_call)
|
||||
{
|
||||
if (TREE_SIDE_EFFECTS (callee))
|
||||
saved_args = compound_expr (callee, saved_args);
|
||||
|
||||
tree arg;
|
||||
unsigned int ix;
|
||||
|
||||
FOR_EACH_VEC_SAFE_ELT (args, ix, arg)
|
||||
saved_args = compound_expr (saved_args, arg);
|
||||
|
||||
/* Add a stub result type for the expression. */
|
||||
tree result = build_zero_cst (TREE_TYPE (ctype));
|
||||
return compound_expr (saved_args, result);
|
||||
}
|
||||
|
||||
tree result = build_call_vec (TREE_TYPE (ctype), callee, args);
|
||||
SET_EXPR_LOCATION (result, input_location);
|
||||
|
||||
/* Enforce left to right evaluation. */
|
||||
if (tf->linkage == LINK::d)
|
||||
CALL_EXPR_ARGS_ORDERED (result) = 1;
|
||||
|
||||
result = maybe_expand_intrinsic (result);
|
||||
|
||||
/* Return the value in a temporary slot so that it can be evaluated
|
||||
|
@ -2296,6 +2316,10 @@ build_vthis_function (tree basetype, tree type)
|
|||
TYPE_ARG_TYPES (type));
|
||||
tree fntype = build_function_type (TREE_TYPE (type), argtypes);
|
||||
|
||||
/* Copy volatile qualifiers from the original function type. */
|
||||
if (TYPE_QUALS (type) & TYPE_QUAL_VOLATILE)
|
||||
fntype = build_qualified_type (fntype, TYPE_QUAL_VOLATILE);
|
||||
|
||||
if (RECORD_OR_UNION_TYPE_P (basetype))
|
||||
TYPE_METHOD_BASETYPE (fntype) = TYPE_MAIN_VARIANT (basetype);
|
||||
else
|
||||
|
|
|
@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "diagnostic.h"
|
||||
|
||||
#include "d-tree.h"
|
||||
#include "d-frontend.h"
|
||||
|
||||
/* Implements back-end specific interfaces used by the frontend. */
|
||||
|
||||
|
@ -51,7 +52,7 @@ isBuiltin (FuncDeclaration *fd)
|
|||
Return result; NULL if cannot evaluate it. */
|
||||
|
||||
Expression *
|
||||
eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
|
||||
eval_builtin (const Loc &loc, FuncDeclaration *fd, Expressions *arguments)
|
||||
{
|
||||
if (fd->builtin == BUILTIN::unimp)
|
||||
return NULL;
|
||||
|
@ -78,10 +79,16 @@ eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
|
|||
/* Build and return typeinfo type for TYPE. */
|
||||
|
||||
Type *
|
||||
getTypeInfoType (Loc loc, Type *type, Scope *sc)
|
||||
getTypeInfoType (const Loc &loc, Type *type, Scope *sc)
|
||||
{
|
||||
gcc_assert (type->ty != TY::Terror);
|
||||
check_typeinfo_type (loc, sc);
|
||||
create_typeinfo (type, sc ? sc->_module->importedFrom : NULL);
|
||||
return type->vtinfo->type;
|
||||
}
|
||||
|
||||
void
|
||||
toObjFile (Dsymbol *ds, bool)
|
||||
{
|
||||
build_decl_tree (ds);
|
||||
}
|
||||
|
|
|
@ -120,52 +120,47 @@ d_gimplify_addr_expr (tree *expr_p)
|
|||
static gimplify_status
|
||||
d_gimplify_call_expr (tree *expr_p, gimple_seq *pre_p)
|
||||
{
|
||||
if (CALL_EXPR_ARGS_ORDERED (*expr_p))
|
||||
/* Strictly evaluate all arguments from left to right. */
|
||||
int nargs = call_expr_nargs (*expr_p);
|
||||
location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location);
|
||||
|
||||
/* 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. */
|
||||
|
|
|
@ -674,6 +674,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
|
|||
case OPT_ftransition_all:
|
||||
global.params.vfield = value;
|
||||
global.params.vgc = value;
|
||||
global.params.vin = value;
|
||||
global.params.vmarkdown= value;
|
||||
global.params.vtls = value;
|
||||
break;
|
||||
|
@ -682,6 +683,10 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
|
|||
global.params.vfield = value;
|
||||
break;
|
||||
|
||||
case OPT_ftransition_in:
|
||||
global.params.vin = value;
|
||||
break;
|
||||
|
||||
case OPT_ftransition_nogc:
|
||||
global.params.vgc = value;
|
||||
break;
|
||||
|
@ -1176,6 +1181,14 @@ d_parse_file (void)
|
|||
{
|
||||
Module *m = modules[i];
|
||||
|
||||
/* If this is the `__main` module, check that `D main` hasn't already
|
||||
been declared in user code before running semantic on it. */
|
||||
if (m == main_module && global.hasMainFunction)
|
||||
{
|
||||
modules.remove (i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (global.params.verbose)
|
||||
message ("semantic %s", m->toChars ());
|
||||
|
||||
|
@ -1357,6 +1370,9 @@ d_parse_file (void)
|
|||
for (size_t i = 0; i < modules.length; i++)
|
||||
{
|
||||
Module *m = modules[i];
|
||||
|
||||
/* Skip generating code for header files, or when the module wasn't
|
||||
specified by `-fonly=`. */
|
||||
if ((m->isHdrFile && m != main_module)
|
||||
|| (d_option.fonly && m != Module::rootModule))
|
||||
continue;
|
||||
|
|
|
@ -47,7 +47,6 @@ typedef Array <Expression *> Expressions;
|
|||
|
||||
/* Usage of TREE_LANG_FLAG_?:
|
||||
0: METHOD_CALL_EXPR
|
||||
1: CALL_EXPR_ARGS_ORDERED (in CALL_EXPR).
|
||||
|
||||
Usage of TYPE_LANG_FLAG_?:
|
||||
0: TYPE_SHARED
|
||||
|
@ -351,11 +350,6 @@ lang_tree_node
|
|||
#define METHOD_CALL_EXPR(NODE) \
|
||||
(TREE_LANG_FLAG_0 (NODE))
|
||||
|
||||
/* True if all arguments in a call expression should be evaluated in the
|
||||
order they are given (left to right). */
|
||||
#define CALL_EXPR_ARGS_ORDERED(NODE) \
|
||||
(TREE_LANG_FLAG_1 (CALL_EXPR_CHECK (NODE)))
|
||||
|
||||
/* True if the type was declared 'shared'. */
|
||||
#define TYPE_SHARED(NODE) \
|
||||
(TYPE_LANG_FLAG_0 (NODE))
|
||||
|
@ -430,6 +424,7 @@ enum d_tree_index
|
|||
|
||||
DTI_ARRAY_TYPE,
|
||||
DTI_NULL_ARRAY,
|
||||
DTI_BOTTOM_TYPE,
|
||||
|
||||
DTI_MAX
|
||||
};
|
||||
|
@ -465,6 +460,8 @@ extern GTY(()) tree d_global_trees[DTI_MAX];
|
|||
#define array_type_node d_global_trees[DTI_ARRAY_TYPE]
|
||||
/* Null initializer for dynamic arrays. */
|
||||
#define null_array_node d_global_trees[DTI_NULL_ARRAY]
|
||||
/* The bottom type, referred to as `noreturn` in code. */
|
||||
#define noreturn_type_node d_global_trees[DTI_BOTTOM_TYPE]
|
||||
|
||||
/* A prefix for internal variables, which are not user-visible. */
|
||||
#if !defined (NO_DOT_IN_LABEL)
|
||||
|
|
155
gcc/d/decl.cc
155
gcc/d/decl.cc
|
@ -116,59 +116,6 @@ gcc_attribute_p (Dsymbol *decl)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Subroutine of pragma declaration visitor for marking the function in the
|
||||
defined in SYM as a global constructor or destructor. If ISCTOR is true,
|
||||
then we're applying pragma(crt_constructor). */
|
||||
|
||||
static int
|
||||
apply_pragma_crt (Dsymbol *sym, bool isctor)
|
||||
{
|
||||
AttribDeclaration *ad = sym->isAttribDeclaration ();
|
||||
if (ad != NULL)
|
||||
{
|
||||
int nested = 0;
|
||||
|
||||
/* Walk all declarations of the attribute scope. */
|
||||
Dsymbols *ds = ad->include (NULL);
|
||||
if (ds)
|
||||
{
|
||||
for (size_t i = 0; i < ds->length; i++)
|
||||
nested += apply_pragma_crt ((*ds)[i], isctor);
|
||||
}
|
||||
|
||||
return nested;
|
||||
}
|
||||
|
||||
FuncDeclaration *fd = sym->isFuncDeclaration ();
|
||||
if (fd != NULL)
|
||||
{
|
||||
tree decl = get_decl_tree (fd);
|
||||
|
||||
/* Apply flags to the function. */
|
||||
if (isctor)
|
||||
{
|
||||
DECL_STATIC_CONSTRUCTOR (decl) = 1;
|
||||
decl_init_priority_insert (decl, DEFAULT_INIT_PRIORITY);
|
||||
}
|
||||
else
|
||||
{
|
||||
DECL_STATIC_DESTRUCTOR (decl) = 1;
|
||||
decl_fini_priority_insert (decl, DEFAULT_INIT_PRIORITY);
|
||||
}
|
||||
|
||||
if (fd->linkage != LINK::c)
|
||||
{
|
||||
error_at (make_location_t (fd->loc),
|
||||
"must be %<extern(C)%> for %<pragma(%s)%>",
|
||||
isctor ? "crt_constructor" : "crt_destructor");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implements the visitor interface to lower all Declaration AST classes
|
||||
emitted from the D Front-end to GCC trees.
|
||||
All visit methods accept one parameter D, which holds the frontend AST
|
||||
|
@ -210,18 +157,18 @@ public:
|
|||
|
||||
void visit (Module *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
build_module_tree (d);
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Write the imported symbol to debug. */
|
||||
|
||||
void visit (Import *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
/* Implements import declarations by telling the debug back-end we are
|
||||
|
@ -266,7 +213,7 @@ public:
|
|||
false, false);
|
||||
}
|
||||
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Expand any local variables found in tuples. */
|
||||
|
@ -312,18 +259,6 @@ public:
|
|||
"pragma(%s) not implemented", d->ident->toChars ());
|
||||
}
|
||||
}
|
||||
else if (d->ident == Identifier::idPool ("crt_constructor")
|
||||
|| d->ident == Identifier::idPool ("crt_destructor"))
|
||||
{
|
||||
/* Handle pragma(crt_constructor) and pragma(crt_destructor). Apply
|
||||
flag to indicate that the functions enclosed should run automatically
|
||||
at the beginning or end of execution. */
|
||||
bool isctor = (d->ident == Identifier::idPool ("crt_constructor"));
|
||||
|
||||
if (apply_pragma_crt (d, isctor) > 1)
|
||||
error_at (make_location_t (d->loc),
|
||||
"can only apply to a single declaration");
|
||||
}
|
||||
|
||||
visit ((AttribDeclaration *) d);
|
||||
}
|
||||
|
@ -422,7 +357,7 @@ public:
|
|||
|
||||
void visit (StructDeclaration *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
if (d->type->ty == TY::Terror)
|
||||
|
@ -470,7 +405,7 @@ public:
|
|||
if (d->xhash)
|
||||
this->build_dsymbol (d->xhash);
|
||||
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Finish semantic analysis of functions in vtbl for class CD. */
|
||||
|
@ -537,7 +472,7 @@ public:
|
|||
|
||||
void visit (ClassDeclaration *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
if (d->type->ty == TY::Terror)
|
||||
|
@ -603,7 +538,7 @@ public:
|
|||
if (TYPE_NAME (ctype))
|
||||
d_pushdecl (TYPE_NAME (ctype));
|
||||
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Write out compiler generated TypeInfo and vtables for the given interface
|
||||
|
@ -611,7 +546,7 @@ public:
|
|||
|
||||
void visit (InterfaceDeclaration *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
if (d->type->ty == TY::Terror)
|
||||
|
@ -646,7 +581,7 @@ public:
|
|||
if (TYPE_NAME (ctype))
|
||||
d_pushdecl (TYPE_NAME (ctype));
|
||||
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Write out compiler generated TypeInfo and initializer for the given
|
||||
|
@ -654,7 +589,7 @@ public:
|
|||
|
||||
void visit (EnumDeclaration *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
if (d->errors || d->type->ty == TY::Terror)
|
||||
|
@ -685,7 +620,7 @@ public:
|
|||
if (TYPE_NAME (ctype))
|
||||
d_pushdecl (TYPE_NAME (ctype));
|
||||
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Finish up a variable declaration and push it into the current scope.
|
||||
|
@ -693,7 +628,7 @@ public:
|
|||
|
||||
void visit (VarDeclaration *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
if (d->type->ty == TY::Terror)
|
||||
|
@ -703,6 +638,21 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
/* Variables of type `noreturn` are just placeholders, and evaluate to
|
||||
`assert(0)` if ever read. */
|
||||
if (d->type->isTypeNoreturn ())
|
||||
{
|
||||
if (!d->isDataseg () && !d->isMember ()
|
||||
&& d->_init && !d->_init->isVoidInitializer ())
|
||||
{
|
||||
Expression *e = d->type->defaultInitLiteral (d->loc);
|
||||
tree exp = build_expr (e);
|
||||
add_stmt (exp);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->aliassym)
|
||||
{
|
||||
this->build_dsymbol (d->toAlias ());
|
||||
|
@ -762,7 +712,7 @@ public:
|
|||
|
||||
/* Frontend should have already caught this. */
|
||||
gcc_assert (!integer_zerop (size)
|
||||
|| d->type->toBasetype ()->ty == TY::Tsarray);
|
||||
|| d->type->toBasetype ()->isTypeSArray ());
|
||||
|
||||
d_finish_decl (decl);
|
||||
|
||||
|
@ -797,7 +747,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Generate and compile a static TypeInfo declaration, but only if it is
|
||||
|
@ -805,7 +755,7 @@ public:
|
|||
|
||||
void visit (TypeInfoDeclaration *d)
|
||||
{
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
if (speculative_type_p (d->tinfo))
|
||||
|
@ -814,7 +764,7 @@ public:
|
|||
tree t = get_typeinfo_decl (d);
|
||||
DECL_INITIAL (t) = layout_typeinfo (d);
|
||||
d_finish_decl (t);
|
||||
d->semanticRun = PASSobj;
|
||||
d->semanticRun = PASS::obj;
|
||||
}
|
||||
|
||||
/* Finish up a function declaration and compile it all the way
|
||||
|
@ -823,7 +773,7 @@ public:
|
|||
void visit (FuncDeclaration *d)
|
||||
{
|
||||
/* Already generated the function. */
|
||||
if (d->semanticRun >= PASSobj)
|
||||
if (d->semanticRun >= PASS::obj)
|
||||
return;
|
||||
|
||||
/* Don't emit any symbols from gcc.attribute module. */
|
||||
|
@ -861,7 +811,7 @@ public:
|
|||
}
|
||||
|
||||
/* Ensure all semantic passes have run. */
|
||||
if (d->semanticRun < PASSsemantic3)
|
||||
if (d->semanticRun < PASS::semantic3)
|
||||
{
|
||||
d->functionSemantic3 ();
|
||||
Module::runDeferredSemantic3 ();
|
||||
|
@ -887,8 +837,8 @@ public:
|
|||
message ("function %s", d->toPrettyChars ());
|
||||
|
||||
/* Start generating code for this function. */
|
||||
gcc_assert (d->semanticRun == PASSsemantic3done);
|
||||
d->semanticRun = PASSobj;
|
||||
gcc_assert (d->semanticRun == PASS::semantic3done);
|
||||
d->semanticRun = PASS::obj;
|
||||
|
||||
tree old_context = start_function (d);
|
||||
|
||||
|
@ -927,12 +877,19 @@ public:
|
|||
}
|
||||
|
||||
/* formal function parameters. */
|
||||
size_t n_parameters = d->parameters ? d->parameters->length : 0;
|
||||
const size_t n_parameters = d->parameters ? d->parameters->length : 0;
|
||||
|
||||
for (size_t i = 0; i < n_parameters; i++)
|
||||
{
|
||||
VarDeclaration *param = (*d->parameters)[i];
|
||||
|
||||
parm_decl = get_symbol_decl (param);
|
||||
|
||||
/* Type `noreturn` is a terminator, as no other arguments can possibly
|
||||
be evaluated after it. */
|
||||
if (TREE_TYPE (parm_decl) == noreturn_type_node)
|
||||
break;
|
||||
|
||||
/* Chain them in the correct order. */
|
||||
param_list = chainon (param_list, parm_decl);
|
||||
}
|
||||
|
@ -1136,9 +1093,9 @@ get_symbol_decl (Declaration *decl)
|
|||
declaration_type (vd));
|
||||
|
||||
/* If any alignment was set on the declaration. */
|
||||
if (vd->alignment != STRUCTALIGN_DEFAULT)
|
||||
if (!vd->alignment.isDefault ())
|
||||
{
|
||||
SET_DECL_ALIGN (decl->csym, vd->alignment * BITS_PER_UNIT);
|
||||
SET_DECL_ALIGN (decl->csym, vd->alignment.get () * BITS_PER_UNIT);
|
||||
DECL_USER_ALIGN (decl->csym) = 1;
|
||||
}
|
||||
|
||||
|
@ -1321,6 +1278,20 @@ get_symbol_decl (Declaration *decl)
|
|||
else if (fd->inlining == PINLINE::never)
|
||||
DECL_UNINLINABLE (decl->csym) = 1;
|
||||
|
||||
/* In [pragma/crtctor], Annotates a function so it is run after the C
|
||||
runtime library is initialized and before the D runtime library is
|
||||
initialized. */
|
||||
if (fd->isCrtCtorDtor == 1)
|
||||
{
|
||||
DECL_STATIC_CONSTRUCTOR (decl->csym) = 1;
|
||||
decl_init_priority_insert (decl->csym, DEFAULT_INIT_PRIORITY);
|
||||
}
|
||||
else if (fd->isCrtCtorDtor == 2)
|
||||
{
|
||||
DECL_STATIC_DESTRUCTOR (decl->csym) = 1;
|
||||
decl_fini_priority_insert (decl->csym, DEFAULT_INIT_PRIORITY);
|
||||
}
|
||||
|
||||
/* Function was declared `naked'. */
|
||||
if (fd->naked)
|
||||
{
|
||||
|
@ -1342,7 +1313,7 @@ get_symbol_decl (Declaration *decl)
|
|||
DECL_FINAL_P (decl->csym) = 1;
|
||||
|
||||
/* Function is of type `noreturn' or `typeof(*null)'. */
|
||||
if (fd->type->nextOf ()->ty == TY::Tnoreturn)
|
||||
if (fd->type->nextOf ()->isTypeNoreturn ())
|
||||
TREE_THIS_VOLATILE (decl->csym) = 1;
|
||||
|
||||
/* Check whether this function is expanded by the frontend. */
|
||||
|
@ -2246,9 +2217,9 @@ aggregate_initializer_decl (AggregateDeclaration *decl)
|
|||
TREE_READONLY (sinit) = 1;
|
||||
|
||||
/* Honor struct alignment set by user. */
|
||||
if (sd && sd->alignment != STRUCTALIGN_DEFAULT)
|
||||
if (sd && !sd->alignment.isDefault ())
|
||||
{
|
||||
SET_DECL_ALIGN (sinit, sd->alignment * BITS_PER_UNIT);
|
||||
SET_DECL_ALIGN (sinit, sd->alignment.get () * BITS_PER_UNIT);
|
||||
DECL_USER_ALIGN (sinit) = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
b8384668f28741ad5884fc055a2bdb9c05fd95ec
|
||||
568496d5b6ed02d577dfa86f73c7bb4edee05813
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the dlang/dmd repository.
|
||||
|
|
|
@ -20,6 +20,7 @@ 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 |
|
||||
|---------------------------------------------------------------------------------|---------------------------------------------------------------|
|
||||
|
|
|
@ -1 +1 @@
|
|||
v2.097.2
|
||||
v2.098.0
|
||||
|
|
|
@ -21,6 +21,7 @@ import dmd.aliasthis;
|
|||
import dmd.apply;
|
||||
import dmd.arraytypes;
|
||||
import dmd.astenums;
|
||||
import dmd.attrib;
|
||||
import dmd.declaration;
|
||||
import dmd.dscope;
|
||||
import dmd.dstruct;
|
||||
|
@ -115,11 +116,12 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
|
|||
|
||||
AliasThis aliasthis; /// forward unresolved lookups to aliasthis
|
||||
|
||||
DtorDeclarations dtors; /// Array of destructors
|
||||
DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors
|
||||
DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D
|
||||
DtorDeclarations userDtors; /// user-defined destructors (`~this()`) - mixins can yield multiple ones
|
||||
DtorDeclaration aggrDtor; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
|
||||
DtorDeclaration dtor; /// the aggregate destructor exposed as `__xdtor` alias
|
||||
/// (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
|
||||
DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
|
||||
FuncDeclaration fieldDtor; /// aggregate destructor for just the fields
|
||||
DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields
|
||||
|
||||
Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
|
||||
|
||||
|
@ -177,7 +179,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
|
|||
* Returns:
|
||||
* false if failed to determine the size.
|
||||
*/
|
||||
final bool determineSize(Loc loc)
|
||||
final bool determineSize(const ref Loc loc)
|
||||
{
|
||||
//printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
|
||||
|
||||
|
@ -331,7 +333,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
|
|||
* false if any errors occur.
|
||||
* Otherwise, returns true and the missing arguments will be pushed in elements[].
|
||||
*/
|
||||
final bool fill(Loc loc, Expressions* elements, bool ctorinit)
|
||||
final bool fill(const ref Loc loc, Expressions* elements, bool ctorinit)
|
||||
{
|
||||
//printf("AggregateDeclaration::fill() %s\n", toChars());
|
||||
assert(sizeok == Sizeok.done);
|
||||
|
@ -482,45 +484,52 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
|
|||
* Align sizes of 0, as we may not know array sizes yet.
|
||||
* Params:
|
||||
* alignment = struct alignment that is in effect
|
||||
* size = alignment requirement of field
|
||||
* memalignsize = natural alignment of field
|
||||
* poffset = pointer to offset to be aligned
|
||||
*/
|
||||
extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe
|
||||
extern (D) static void alignmember(structalign_t alignment, uint memalignsize, uint* poffset) pure nothrow @safe
|
||||
{
|
||||
//printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
|
||||
switch (alignment)
|
||||
{
|
||||
case cast(structalign_t)1:
|
||||
// No alignment
|
||||
break;
|
||||
//debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, *poffset);
|
||||
uint alignvalue;
|
||||
|
||||
case cast(structalign_t)STRUCTALIGN_DEFAULT:
|
||||
if (alignment.isDefault())
|
||||
{
|
||||
// Alignment in Target::fieldalignsize must match what the
|
||||
// corresponding C compiler's default alignment behavior is.
|
||||
assert(size > 0 && !(size & (size - 1)));
|
||||
*poffset = (*poffset + size - 1) & ~(size - 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
// 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)
|
||||
|
|
|
@ -105,11 +105,12 @@ public:
|
|||
|
||||
AliasThis *aliasthis; // forward unresolved lookups to aliasthis
|
||||
|
||||
DtorDeclarations dtors; // Array of destructors
|
||||
DtorDeclaration *dtor; // aggregate destructor
|
||||
DtorDeclaration *primaryDtor; // non-deleting C++ destructor, same as dtor for D
|
||||
DtorDeclarations userDtors; // user-defined destructors (`~this()`) - mixins can yield multiple ones
|
||||
DtorDeclaration *aggrDtor; // aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes)
|
||||
DtorDeclaration *dtor; // the aggregate destructor exposed as `__xdtor` alias
|
||||
// (same as aggrDtor, except for C++ classes with virtual dtor on Windows)
|
||||
DtorDeclaration *tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI)
|
||||
FuncDeclaration *fieldDtor; // aggregate destructor for just the fields
|
||||
DtorDeclaration *fieldDtor; // function destructing (non-inherited) fields
|
||||
|
||||
Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this)
|
||||
|
||||
|
@ -121,10 +122,10 @@ public:
|
|||
virtual Scope *newScope(Scope *sc);
|
||||
void setScope(Scope *sc);
|
||||
size_t nonHiddenFields();
|
||||
bool determineSize(Loc loc);
|
||||
bool determineSize(const Loc &loc);
|
||||
virtual void finalizeSize() = 0;
|
||||
d_uns64 size(const Loc &loc);
|
||||
bool fill(Loc loc, Expressions *elements, bool ctorinit);
|
||||
bool fill(const Loc &loc, Expressions *elements, bool ctorinit);
|
||||
Type *getType();
|
||||
bool isDeprecated() const; // is aggregate deprecated?
|
||||
void setDeprecated();
|
||||
|
@ -184,7 +185,7 @@ public:
|
|||
// ABI-specific type(s) if the struct can be passed in registers
|
||||
TypeTuple *argTypes;
|
||||
|
||||
static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
|
||||
static StructDeclaration *create(const Loc &loc, Identifier *id, bool inObject);
|
||||
StructDeclaration *syntaxCopy(Dsymbol *s);
|
||||
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
|
||||
const char *kind() const;
|
||||
|
@ -277,7 +278,7 @@ public:
|
|||
ObjcClassDeclaration objc; // Data for a class declaration that is needed for the Objective-C integration
|
||||
Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr
|
||||
|
||||
static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
|
||||
static ClassDeclaration *create(const Loc &loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
|
||||
const char *toPrettyChars(bool QualifyTypes = false);
|
||||
ClassDeclaration *syntaxCopy(Dsymbol *s);
|
||||
Scope *newScope(Scope *sc);
|
||||
|
|
|
@ -72,17 +72,32 @@ extern (C++) final class AliasThis : Dsymbol
|
|||
}
|
||||
}
|
||||
|
||||
Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false)
|
||||
/*************************************
|
||||
* Find the `alias this` symbol of e's type.
|
||||
* Params:
|
||||
* sc = context
|
||||
* e = expression forming the `this`
|
||||
* gag = if true do not print errors, return null instead
|
||||
* findOnly = don't do further processing like resolving properties,
|
||||
* i.e. just return plain dotExp() result.
|
||||
* Returns:
|
||||
* Expression that is `e.aliasthis`
|
||||
*/
|
||||
Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
|
||||
{
|
||||
import dmd.typesem : dotExp;
|
||||
for (AggregateDeclaration ad = isAggregate(e.type); ad;)
|
||||
{
|
||||
if (ad.aliasthis)
|
||||
{
|
||||
uint olderrors = gag ? global.startGagging() : 0;
|
||||
Loc loc = e.loc;
|
||||
Type tthis = (e.op == TOK.type ? e.type : null);
|
||||
e = new DotIdExp(loc, e, ad.aliasthis.ident);
|
||||
e = e.expressionSemantic(sc);
|
||||
const flags = DotExpFlag.noAliasThis | (gag ? DotExpFlag.gag : 0);
|
||||
uint olderrors = gag ? global.startGagging() : 0;
|
||||
e = dotExp(e.type, sc, e, ad.aliasthis.ident, flags);
|
||||
if (!e || findOnly)
|
||||
return gag && global.endGagging(olderrors) ? null : e;
|
||||
|
||||
if (tthis && ad.aliasthis.sym.needThis())
|
||||
{
|
||||
if (e.op == TOK.variable)
|
||||
|
|
|
@ -26,7 +26,7 @@ import dmd.globals;
|
|||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.statement;
|
||||
import dmd.tokens;
|
||||
import dmd.visitor;
|
||||
|
|
|
@ -42,7 +42,7 @@ import dmd.id;
|
|||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.objc; // for objc.addSymbols
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.target; // for target.systemLinkage
|
||||
import dmd.tokens;
|
||||
import dmd.visitor;
|
||||
|
@ -696,12 +696,9 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
|
|||
{
|
||||
Expressions* exps; /// Expression(s) yielding the desired alignment,
|
||||
/// the largest value wins
|
||||
enum structalign_t UNKNOWN = 0; /// alignment not yet computed
|
||||
static assert(STRUCTALIGN_DEFAULT != UNKNOWN);
|
||||
|
||||
/// the actual alignment, `UNKNOWN` until it's either set to the value of `ealign`
|
||||
/// or `STRUCTALIGN_DEFAULT` if `ealign` is null ( / an error ocurred)
|
||||
structalign_t salign = UNKNOWN;
|
||||
/// the actual alignment is Unknown until it's either set to the value of `ealign`
|
||||
/// or the default if `ealign` is null ( / an error ocurred)
|
||||
structalign_t salign;
|
||||
|
||||
|
||||
extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl)
|
||||
|
@ -709,8 +706,7 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
|
|||
super(loc, null, decl);
|
||||
if (exp)
|
||||
{
|
||||
if (!exps)
|
||||
exps = new Expressions();
|
||||
exps = new Expressions();
|
||||
exps.push(exp);
|
||||
}
|
||||
}
|
||||
|
@ -721,6 +717,12 @@ extern (C++) final class AlignDeclaration : AttribDeclaration
|
|||
this.exps = exps;
|
||||
}
|
||||
|
||||
extern (D) this(const ref Loc loc, structalign_t salign, Dsymbols* decl)
|
||||
{
|
||||
super(loc, null, decl);
|
||||
this.salign = salign;
|
||||
}
|
||||
|
||||
override AlignDeclaration syntaxCopy(Dsymbol s)
|
||||
{
|
||||
assert(!s);
|
||||
|
@ -1196,7 +1198,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration
|
|||
|
||||
// expand static foreach
|
||||
import dmd.statementsem: makeTupleForeach;
|
||||
Dsymbols* d = makeTupleForeach!(true,true)(_scope, sfe.aggrfe, decl, sfe.needExpansion);
|
||||
Dsymbols* d = makeTupleForeach!(true,true)(_scope, sfe.aggrfe, decl, sfe.needExpansion).decl;
|
||||
if (d) // process generated declarations
|
||||
{
|
||||
// Add members lazily.
|
||||
|
|
|
@ -108,7 +108,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (s.exp.type.toBasetype().isTypeNoreturn())
|
||||
if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn())
|
||||
result = BE.halt;
|
||||
if (canThrow(s.exp, func, mustNotThrow))
|
||||
result |= BE.throw_;
|
||||
|
@ -146,7 +146,7 @@ int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
|
|||
else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement()))
|
||||
{
|
||||
}
|
||||
else
|
||||
else if (!func.getModule().isCFile)
|
||||
{
|
||||
const(char)* gototype = s.isCaseStatement() ? "case" : "default";
|
||||
s.deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
|
||||
|
|
|
@ -30,4 +30,4 @@ public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd);
|
|||
* Evaluate builtin function.
|
||||
* Return result; NULL if cannot evaluate it.
|
||||
*/
|
||||
public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments);
|
||||
public extern (C++) Expression eval_builtin(const ref Loc loc, FuncDeclaration fd, Expressions* arguments);
|
||||
|
|
|
@ -690,8 +690,8 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx,
|
|||
return error();
|
||||
while ('0' <= format[i] && format[i] <= '9')
|
||||
{
|
||||
++i;
|
||||
if (i == length)
|
||||
++i;
|
||||
if (i == length)
|
||||
return error();
|
||||
}
|
||||
}
|
||||
|
@ -720,8 +720,8 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx,
|
|||
return error();
|
||||
while ('0' <= format[i] && format[i] <= '9')
|
||||
{
|
||||
++i;
|
||||
if (i == length)
|
||||
++i;
|
||||
if (i == length)
|
||||
return error();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -794,6 +794,19 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
|
|||
if (!needToHash(sd))
|
||||
return null;
|
||||
|
||||
/* The trouble is that the following code relies on .tupleof, but .tupleof
|
||||
* is not allowed for C files. If we allow it for C files, then that turns on
|
||||
* the other D properties, too, such as .dup which will then conflict with allowed
|
||||
* field names.
|
||||
* One way to fix it is to replace the following foreach and .tupleof with C
|
||||
* statements and expressions.
|
||||
* But, it's debatable whether C structs should even need toHash().
|
||||
* Note that it would only be necessary if it has floating point fields.
|
||||
* For now, we'll just not generate a toHash() for C files.
|
||||
*/
|
||||
if (sc.flags & SCOPE.Cfile)
|
||||
return null;
|
||||
|
||||
//printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars());
|
||||
Loc declLoc; // loc is unnecessary so __xtoHash is never called directly
|
||||
Loc loc; // internal code should have no loc to prevent coverage
|
||||
|
@ -831,31 +844,31 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc)
|
|||
}
|
||||
|
||||
/*****************************************
|
||||
* Create inclusive destructor for struct/class by aggregating
|
||||
* all the destructors in dtors[] with the destructors for
|
||||
* Create aggregate destructor for struct/class by aggregating
|
||||
* all the destructors in userDtors[] with the destructors for
|
||||
* all the members.
|
||||
* Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields.
|
||||
* Params:
|
||||
* ad = struct or class to build destructor for
|
||||
* sc = context
|
||||
* Returns:
|
||||
* generated function, null if none needed
|
||||
* Note:
|
||||
* Close similarity with StructDeclaration::buildPostBlit(),
|
||||
* and the ordering changes (runs backward instead of forwards).
|
||||
*/
|
||||
DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
|
||||
void buildDtors(AggregateDeclaration ad, Scope* sc)
|
||||
{
|
||||
//printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars());
|
||||
if (ad.isUnionDeclaration())
|
||||
return null; // unions don't have destructors
|
||||
return; // unions don't have destructors
|
||||
|
||||
StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
|
||||
Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc;
|
||||
Loc declLoc = ad.userDtors.dim ? ad.userDtors[0].loc : ad.loc;
|
||||
Loc loc; // internal code should have no loc to prevent coverage
|
||||
FuncDeclaration xdtor_fwd = null;
|
||||
|
||||
// if the dtor is an extern(C++) prototype, then we expect it performs a full-destruction; we don't need to build a full-dtor
|
||||
const bool dtorIsCppPrototype = ad.dtors.dim == 1 && ad.dtors[0].linkage == LINK.cpp && !ad.dtors[0].fbody;
|
||||
// Build the field destructor (`ad.fieldDtor`), if needed.
|
||||
// If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building.
|
||||
const bool dtorIsCppPrototype = ad.userDtors.dim && ad.userDtors[0].linkage == LINK.cpp && !ad.userDtors[0].fbody;
|
||||
if (!dtorIsCppPrototype)
|
||||
{
|
||||
Expression e = null;
|
||||
|
@ -936,36 +949,6 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
|
|||
e = Expression.combine(ex, e); // combine in reverse order
|
||||
}
|
||||
|
||||
/* extern(C++) destructors call into super to destruct the full hierarchy
|
||||
*/
|
||||
ClassDeclaration cldec = ad.isClassDeclaration();
|
||||
if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.primaryDtor)
|
||||
{
|
||||
// WAIT BUT: do I need to run `cldec.baseClass.dtor` semantic? would it have been run before?
|
||||
cldec.baseClass.dtor.functionSemantic();
|
||||
|
||||
stc = mergeFuncAttrs(stc, cldec.baseClass.primaryDtor);
|
||||
if (!(stc & STC.disable))
|
||||
{
|
||||
// super.__xdtor()
|
||||
|
||||
Expression ex = new SuperExp(loc);
|
||||
|
||||
// This is a hack so we can call destructors on const/immutable objects.
|
||||
// Do it as a type 'paint'.
|
||||
ex = new CastExp(loc, ex, cldec.baseClass.type.mutableOf());
|
||||
if (stc & STC.safe)
|
||||
stc = (stc & ~STC.safe) | STC.trusted;
|
||||
|
||||
ex = new DotVarExp(loc, ex, cldec.baseClass.primaryDtor, false);
|
||||
ex = new CallExp(loc, ex);
|
||||
|
||||
e = Expression.combine(e, ex); // super dtor last
|
||||
}
|
||||
}
|
||||
|
||||
/* Build our own "destructor" which executes e
|
||||
*/
|
||||
if (e || (stc & STC.disable))
|
||||
{
|
||||
//printf("Building __fieldDtor(), %s\n", e.toChars());
|
||||
|
@ -973,29 +956,45 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
|
|||
dd.generated = true;
|
||||
dd.storage_class |= STC.inference;
|
||||
dd.fbody = new ExpStatement(loc, e);
|
||||
ad.dtors.shift(dd);
|
||||
ad.members.push(dd);
|
||||
dd.dsymbolSemantic(sc);
|
||||
ad.fieldDtor = dd;
|
||||
}
|
||||
}
|
||||
|
||||
DtorDeclaration xdtor = null;
|
||||
switch (ad.dtors.dim)
|
||||
// Generate list of dtors to call in that order
|
||||
DtorDeclarations dtors;
|
||||
foreach_reverse (userDtor; ad.userDtors[])
|
||||
dtors.push(userDtor);
|
||||
if (ad.fieldDtor)
|
||||
dtors.push(ad.fieldDtor);
|
||||
if (!dtorIsCppPrototype)
|
||||
{
|
||||
// extern(C++) destructors call into super to destruct the full hierarchy
|
||||
ClassDeclaration cldec = ad.isClassDeclaration();
|
||||
if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor)
|
||||
dtors.push(cldec.baseClass.aggrDtor);
|
||||
}
|
||||
|
||||
// Set/build `ad.aggrDtor`
|
||||
switch (dtors.dim)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
xdtor = ad.dtors[0];
|
||||
// Use the single existing dtor directly as aggregate dtor.
|
||||
// Note that this might be `cldec.baseClass.aggrDtor`.
|
||||
ad.aggrDtor = dtors[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
// Build the aggregate destructor, calling all dtors in order.
|
||||
assert(!dtorIsCppPrototype);
|
||||
Expression e = null;
|
||||
e = null;
|
||||
stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc;
|
||||
foreach (FuncDeclaration fd; ad.dtors)
|
||||
foreach (FuncDeclaration fd; dtors)
|
||||
{
|
||||
stc = mergeFuncAttrs(stc, fd);
|
||||
if (stc & STC.disable)
|
||||
|
@ -1005,8 +1004,9 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
|
|||
}
|
||||
Expression ex = new ThisExp(loc);
|
||||
ex = new DotVarExp(loc, ex, fd, false);
|
||||
ex = new CallExp(loc, ex);
|
||||
e = Expression.combine(ex, e);
|
||||
CallExp ce = new CallExp(loc, ex);
|
||||
ce.directcall = true;
|
||||
e = Expression.combine(e, ce);
|
||||
}
|
||||
auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor);
|
||||
dd.generated = true;
|
||||
|
@ -1014,19 +1014,20 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc)
|
|||
dd.fbody = new ExpStatement(loc, e);
|
||||
ad.members.push(dd);
|
||||
dd.dsymbolSemantic(sc);
|
||||
xdtor = dd;
|
||||
ad.aggrDtor = dd;
|
||||
break;
|
||||
}
|
||||
|
||||
ad.primaryDtor = xdtor;
|
||||
// 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;
|
||||
|
||||
|
|
7
gcc/d/dmd/common/README.md
Normal file
7
gcc/d/dmd/common/README.md
Normal 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
576
gcc/d/dmd/common/file.d
Normal 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)();
|
||||
}
|
|
@ -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);
|
|
@ -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
209
gcc/d/dmd/common/string.d
Normal 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]);
|
||||
}
|
|
@ -28,7 +28,7 @@ import dmd.globals;
|
|||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.typesem;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.tokens;
|
||||
|
@ -452,7 +452,6 @@ extern (C++) final class StaticForeach : RootObject
|
|||
sc = sc.startCTFE();
|
||||
aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
|
||||
sc = sc.endCTFE();
|
||||
aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
|
||||
}
|
||||
|
||||
if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror)
|
||||
|
|
|
@ -23,7 +23,7 @@ import dmd.lexer;
|
|||
import dmd.parse;
|
||||
import dmd.errors;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
|
@ -72,6 +72,7 @@ final class CParser(AST) : Parser!AST
|
|||
{
|
||||
//printf("cparseTranslationUnit()\n");
|
||||
symbols = new AST.Dsymbols();
|
||||
addBuiltinDeclarations();
|
||||
while (1)
|
||||
{
|
||||
if (token.value == TOK.endOfFile)
|
||||
|
@ -756,7 +757,6 @@ final class CParser(AST) : Parser!AST
|
|||
switch (token.value)
|
||||
{
|
||||
case TOK.dot:
|
||||
case TOK.arrow:
|
||||
nextToken();
|
||||
if (token.value == TOK.identifier)
|
||||
{
|
||||
|
@ -767,6 +767,19 @@ final class CParser(AST) : Parser!AST
|
|||
error("identifier expected following `.`, not `%s`", token.toChars());
|
||||
break;
|
||||
|
||||
case TOK.arrow:
|
||||
nextToken();
|
||||
if (token.value == TOK.identifier)
|
||||
{
|
||||
Identifier id = token.ident;
|
||||
auto die = new AST.DotIdExp(loc, e, id);
|
||||
die.arrow = true;
|
||||
e = die;
|
||||
break;
|
||||
}
|
||||
error("identifier expected following `->`, not `%s`", token.toChars());
|
||||
break;
|
||||
|
||||
case TOK.plusPlus:
|
||||
e = new AST.PostExp(TOK.plusPlus, loc, e);
|
||||
break;
|
||||
|
@ -949,6 +962,7 @@ final class CParser(AST) : Parser!AST
|
|||
nextToken();
|
||||
auto t = cparseTypeName();
|
||||
check(TOK.rightParenthesis);
|
||||
pt = &token;
|
||||
|
||||
if (token.value == TOK.leftCurly)
|
||||
{
|
||||
|
@ -957,6 +971,17 @@ final class CParser(AST) : Parser!AST
|
|||
auto ce = new AST.CompoundLiteralExp(loc, t, ci);
|
||||
return cparsePostfixOperators(ce);
|
||||
}
|
||||
else if (t.isTypeIdentifier() &&
|
||||
token.value == TOK.leftParenthesis &&
|
||||
!isCastExpression(pt))
|
||||
{
|
||||
/* this might actually be a function
|
||||
* call that looks like `(a)(b)` or even `(a)(b,c)`
|
||||
*/
|
||||
auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident);
|
||||
ie.parens = true; // disambiguate it from being a declaration
|
||||
return new AST.CallExp(loc, ie, cparseArguments());
|
||||
}
|
||||
else
|
||||
{
|
||||
// ( type-name ) cast-expression
|
||||
|
@ -1451,6 +1476,7 @@ final class CParser(AST) : Parser!AST
|
|||
|
||||
auto symbolsSave = symbols;
|
||||
Specifier specifier;
|
||||
specifier.packalign = this.packalign;
|
||||
auto tspec = cparseDeclarationSpecifiers(level, specifier);
|
||||
|
||||
/* If a declarator does not follow, it is unnamed
|
||||
|
@ -1459,7 +1485,8 @@ final class CParser(AST) : Parser!AST
|
|||
{
|
||||
nextToken();
|
||||
auto tt = tspec.isTypeTag();
|
||||
if (!tt || !tt.id)
|
||||
if (!tt ||
|
||||
!tt.id && (tt.tok == TOK.struct_ || tt.tok == TOK.union_))
|
||||
return; // legal but meaningless empty declaration, ignore it
|
||||
|
||||
/* `struct tag;` and `struct tag { ... };`
|
||||
|
@ -1493,7 +1520,7 @@ final class CParser(AST) : Parser!AST
|
|||
{
|
||||
Identifier id;
|
||||
AST.Expression asmname;
|
||||
auto dt = cparseDeclarator(DTR.xdirect, tspec, id);
|
||||
auto dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
|
||||
if (!dt)
|
||||
{
|
||||
panic();
|
||||
|
@ -1674,6 +1701,8 @@ final class CParser(AST) : Parser!AST
|
|||
return;
|
||||
|
||||
case TOK.comma:
|
||||
if (!symbolsSave)
|
||||
symbolsSave = symbols;
|
||||
nextToken();
|
||||
break;
|
||||
|
||||
|
@ -1720,8 +1749,9 @@ final class CParser(AST) : Parser!AST
|
|||
*/
|
||||
auto pl = ft.parameterList;
|
||||
pl.hasIdentifierList = true; // semantic needs to know to adjust parameter types
|
||||
if (pl.varargs != AST.VarArg.none)
|
||||
if (pl.varargs != AST.VarArg.none && pl.length)
|
||||
error("function identifier-list cannot end with `...`");
|
||||
ft.parameterList.varargs = AST.VarArg.variadic; // but C11 allows extra arguments
|
||||
auto plLength = pl.length;
|
||||
if (symbols.length != plLength)
|
||||
error("%d identifiers does not match %d declarations", cast(int)plLength, cast(int)symbols.length);
|
||||
|
@ -1756,7 +1786,10 @@ final class CParser(AST) : Parser!AST
|
|||
}
|
||||
}
|
||||
if (!p.type)
|
||||
{
|
||||
error("no declaration for identifier `%s`", p.ident.toChars());
|
||||
p.type = AST.Type.terror;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2240,14 +2273,14 @@ final class CParser(AST) : Parser!AST
|
|||
* declarator = declarator kind
|
||||
* t = base type to start with
|
||||
* pident = set to Identifier if there is one, null if not
|
||||
* storageClass = any storage classes seen so far that apply to a function
|
||||
* specifier = specifiers in and out
|
||||
* Returns:
|
||||
* type declared. If a TypeFunction is returned, this.symbols is the
|
||||
* symbol table for the parameter-type-list, which will contain any
|
||||
* declared struct, union or enum tags.
|
||||
*/
|
||||
private AST.Type cparseDeclarator(DTR declarator, AST.Type t,
|
||||
out Identifier pident, StorageClass storageClass = 0)
|
||||
out Identifier pident, ref Specifier specifier)
|
||||
{
|
||||
//printf("cparseDeclarator(%d)\n", declarator);
|
||||
AST.Types constTypes; // all the Types that will need `const` applied to them
|
||||
|
@ -2285,6 +2318,8 @@ final class CParser(AST) : Parser!AST
|
|||
const mod = cparseTypeQualifierList();
|
||||
if (mod & MOD.xconst)
|
||||
constTypes.push(t);
|
||||
if (token.value == TOK.__attribute__)
|
||||
cparseGnuAttributes(specifier);
|
||||
continue;
|
||||
|
||||
default:
|
||||
|
@ -2352,8 +2387,9 @@ final class CParser(AST) : Parser!AST
|
|||
}
|
||||
else
|
||||
{
|
||||
// An array of unknown size, fake it with a DArray
|
||||
ta = new AST.TypeDArray(t); // []
|
||||
/* C11 6.7.6.2-4 An [ ] array is an incomplete array type
|
||||
*/
|
||||
ta = new AST.TypeSArray(t);
|
||||
}
|
||||
check(TOK.rightBracket);
|
||||
|
||||
|
@ -2388,7 +2424,7 @@ final class CParser(AST) : Parser!AST
|
|||
/* C11 6.7.6.2-1: the element type shall not be an incomplete or
|
||||
* function type.
|
||||
*/
|
||||
if (ta.isTypeDArray() && !isVLA)
|
||||
if (ta.isTypeSArray() && ta.isTypeSArray().isIncomplete() && !isVLA)
|
||||
error("array type has incomplete element type `%s`", ta.toChars());
|
||||
}
|
||||
|
||||
|
@ -2489,9 +2525,10 @@ final class CParser(AST) : Parser!AST
|
|||
AST.Type cparseTypeName()
|
||||
{
|
||||
Specifier specifier;
|
||||
specifier.packalign.setDefault();
|
||||
auto tspec = cparseSpecifierQualifierList(LVL.global, specifier);
|
||||
Identifier id;
|
||||
return cparseDeclarator(DTR.xabstract, tspec, id);
|
||||
return cparseDeclarator(DTR.xabstract, tspec, id, specifier);
|
||||
}
|
||||
|
||||
/***********************************
|
||||
|
@ -2525,13 +2562,19 @@ final class CParser(AST) : Parser!AST
|
|||
StorageClass varargsStc;
|
||||
|
||||
check(TOK.leftParenthesis);
|
||||
if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis)
|
||||
if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void)
|
||||
{
|
||||
nextToken();
|
||||
nextToken();
|
||||
return AST.ParameterList(parameters, varargs, varargsStc);
|
||||
}
|
||||
|
||||
if (token.value == TOK.rightParenthesis) // func()
|
||||
{
|
||||
nextToken();
|
||||
return AST.ParameterList(parameters, AST.VarArg.variadic, varargsStc);
|
||||
}
|
||||
|
||||
/* The check for identifier-list comes later,
|
||||
* when doing the trailing declaration-list (opt)
|
||||
*/
|
||||
|
@ -2541,6 +2584,8 @@ final class CParser(AST) : Parser!AST
|
|||
break;
|
||||
if (token.value == TOK.dotDotDot)
|
||||
{
|
||||
if (parameters.length == 0) // func(...)
|
||||
error("named parameter required before `...`");
|
||||
varargs = AST.VarArg.variadic; // C-style variadics
|
||||
nextToken();
|
||||
check(TOK.rightParenthesis);
|
||||
|
@ -2548,10 +2593,16 @@ final class CParser(AST) : Parser!AST
|
|||
}
|
||||
|
||||
Specifier specifier;
|
||||
specifier.packalign.setDefault();
|
||||
auto tspec = cparseDeclarationSpecifiers(LVL.prototype, specifier);
|
||||
if (tspec && specifier.mod & MOD.xconst)
|
||||
{
|
||||
tspec = toConst(tspec);
|
||||
specifier.mod = MOD.xnone; // 'used' it
|
||||
}
|
||||
|
||||
Identifier id;
|
||||
auto t = cparseDeclarator(DTR.xparameter, tspec, id);
|
||||
auto t = cparseDeclarator(DTR.xparameter, tspec, id, specifier);
|
||||
if (specifier.mod & MOD.xconst)
|
||||
t = toConst(t);
|
||||
auto param = new AST.Parameter(STC.parameter, t, id, null, null);
|
||||
|
@ -2920,6 +2971,7 @@ final class CParser(AST) : Parser!AST
|
|||
* enum gnu-attributes (opt) identifier
|
||||
*/
|
||||
Specifier specifier;
|
||||
specifier.packalign.setDefault();
|
||||
if (token.value == TOK.__attribute__)
|
||||
cparseGnuAttributes(specifier);
|
||||
|
||||
|
@ -2950,6 +3002,16 @@ final class CParser(AST) : Parser!AST
|
|||
nextToken();
|
||||
auto mloc = token.loc;
|
||||
|
||||
if (token.value == TOK.__attribute__)
|
||||
{
|
||||
/* gnu-attributes can appear here, but just scan and ignore them
|
||||
* https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
|
||||
*/
|
||||
Specifier specifierx;
|
||||
specifierx.packalign.setDefault();
|
||||
cparseGnuAttributes(specifierx);
|
||||
}
|
||||
|
||||
AST.Expression value;
|
||||
if (token.value == TOK.assign)
|
||||
{
|
||||
|
@ -2958,6 +3020,16 @@ final class CParser(AST) : Parser!AST
|
|||
// TODO C11 6.7.2.2-2 value must fit into an int
|
||||
}
|
||||
|
||||
if (token.value == TOK.__attribute__)
|
||||
{
|
||||
/* gnu-attributes can appear here, but just scan and ignore them
|
||||
* https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html
|
||||
*/
|
||||
Specifier specifierx;
|
||||
specifierx.packalign.setDefault();
|
||||
cparseGnuAttributes(specifierx);
|
||||
}
|
||||
|
||||
auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null);
|
||||
members.push(em);
|
||||
|
||||
|
@ -3037,14 +3109,12 @@ final class CParser(AST) : Parser!AST
|
|||
check(TOK.rightCurly);
|
||||
|
||||
if ((*members).length == 0) // C11 6.7.2.1-8
|
||||
/* TODO: not strict enough, should really be contains "no named members",
|
||||
* not just "no members".
|
||||
* I.e. an unnamed bit field, _Static_assert, etc, are not named members,
|
||||
* but will pass this check.
|
||||
* Be careful to detect named members that come anonymous structs.
|
||||
* Correctly doing this will likely mean moving it to typesem.d.
|
||||
{
|
||||
/* allow empty structs as an extension
|
||||
* struct-declarator-list:
|
||||
* struct-declarator (opt)
|
||||
*/
|
||||
error("empty struct-declaration-list for `%s %s`", Token.toChars(structOrUnion), tag ? tag.toChars() : "Anonymous".ptr);
|
||||
}
|
||||
}
|
||||
else if (!tag)
|
||||
error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion));
|
||||
|
@ -3083,7 +3153,13 @@ final class CParser(AST) : Parser!AST
|
|||
|
||||
auto symbolsSave = symbols;
|
||||
Specifier specifier;
|
||||
specifier.packalign = this.packalign;
|
||||
auto tspec = cparseSpecifierQualifierList(LVL.member, specifier);
|
||||
if (tspec && specifier.mod & MOD.xconst)
|
||||
{
|
||||
tspec = toConst(tspec);
|
||||
specifier.mod = MOD.xnone; // 'used' it
|
||||
}
|
||||
|
||||
/* If a declarator does not follow, it is unnamed
|
||||
*/
|
||||
|
@ -3139,12 +3215,14 @@ final class CParser(AST) : Parser!AST
|
|||
dt = tspec;
|
||||
}
|
||||
else
|
||||
dt = cparseDeclarator(DTR.xdirect, tspec, id);
|
||||
if (!dt)
|
||||
{
|
||||
panic();
|
||||
nextToken();
|
||||
break; // error recovery
|
||||
dt = cparseDeclarator(DTR.xdirect, tspec, id, specifier);
|
||||
if (!dt)
|
||||
{
|
||||
panic();
|
||||
nextToken();
|
||||
break; // error recovery
|
||||
}
|
||||
}
|
||||
|
||||
AST.Expression width;
|
||||
|
@ -3155,9 +3233,6 @@ final class CParser(AST) : Parser!AST
|
|||
width = cparseConstantExp();
|
||||
}
|
||||
|
||||
if (specifier.mod & MOD.xconst)
|
||||
dt = toConst(dt);
|
||||
|
||||
/* GNU Extensions
|
||||
* struct-declarator:
|
||||
* declarator gnu-attributes (opt)
|
||||
|
@ -3234,8 +3309,8 @@ final class CParser(AST) : Parser!AST
|
|||
*/
|
||||
private bool isCDeclaration(ref Token* pt)
|
||||
{
|
||||
//printf("isCDeclaration()\n");
|
||||
auto t = pt;
|
||||
//printf("isCDeclaration() %s\n", t.toChars());
|
||||
if (!isDeclarationSpecifiers(t))
|
||||
return false;
|
||||
|
||||
|
@ -3360,8 +3435,8 @@ final class CParser(AST) : Parser!AST
|
|||
*/
|
||||
private bool isAssignmentExpression(ref Token* pt)
|
||||
{
|
||||
//printf("isAssignmentExpression()\n");
|
||||
auto t = pt;
|
||||
//printf("isAssignmentExpression() %s\n", t.toChars());
|
||||
|
||||
/* This doesn't actually check for grammar matching an
|
||||
* assignment-expression. It just matches ( ) [ ] looking for
|
||||
|
@ -3384,6 +3459,15 @@ final class CParser(AST) : Parser!AST
|
|||
case TOK.leftParenthesis:
|
||||
if (!skipParens(t, &t))
|
||||
return false;
|
||||
/*
|
||||
https://issues.dlang.org/show_bug.cgi?id=22267
|
||||
Fix issue 22267: If the parser encounters the following
|
||||
`identifier variableName = (expression);`
|
||||
the initializer is not identified as such since the parentheses
|
||||
cause the parser to keep walking indefinitely
|
||||
(whereas `(1) + 1` would not be affected.).
|
||||
*/
|
||||
any = true;
|
||||
continue;
|
||||
|
||||
case TOK.leftBracket:
|
||||
|
@ -3391,6 +3475,11 @@ final class CParser(AST) : Parser!AST
|
|||
return false;
|
||||
continue;
|
||||
|
||||
case TOK.leftCurly:
|
||||
if (!skipBraces(t))
|
||||
return false;
|
||||
continue;
|
||||
|
||||
default:
|
||||
any = true; // assume token was part of an a-e
|
||||
t = peek(t);
|
||||
|
@ -3427,6 +3516,7 @@ final class CParser(AST) : Parser!AST
|
|||
|
||||
auto t = pt;
|
||||
|
||||
bool seenType;
|
||||
bool any;
|
||||
while (1)
|
||||
{
|
||||
|
@ -3445,9 +3535,19 @@ final class CParser(AST) : Parser!AST
|
|||
case TOK._Bool:
|
||||
//case TOK._Imaginary:
|
||||
case TOK._Complex:
|
||||
case TOK.identifier: // typedef-name
|
||||
t = peek(t);
|
||||
seenType = true;
|
||||
any = true;
|
||||
continue;
|
||||
|
||||
case TOK.identifier: // typedef-name
|
||||
if (!seenType)
|
||||
{
|
||||
t = peek(t);
|
||||
seenType = true;
|
||||
any = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case TOK.struct_:
|
||||
|
@ -3878,6 +3978,10 @@ final class CParser(AST) : Parser!AST
|
|||
t = tk;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tk.value == TOK.leftParenthesis && peek(tk).value == TOK.rightParenthesis)
|
||||
return false; // (type-name)() is not a cast (it might be a function call)
|
||||
|
||||
if (!isCastExpression(tk, true))
|
||||
{
|
||||
if (afterParenType) // could be ( type-name ) ( unary-expression )
|
||||
|
@ -4071,6 +4175,7 @@ final class CParser(AST) : Parser!AST
|
|||
SCW scw; /// storage-class specifiers
|
||||
MOD mod; /// type qualifiers
|
||||
AST.Expressions* alignExps; /// alignment
|
||||
structalign_t packalign; /// #pragma pack alignment value
|
||||
}
|
||||
|
||||
/***********************
|
||||
|
@ -4089,19 +4194,19 @@ final class CParser(AST) : Parser!AST
|
|||
if (level == LVL.global)
|
||||
{
|
||||
if (specifier.scw & SCW.xextern)
|
||||
stc = AST.STC.extern_;
|
||||
stc = AST.STC.extern_;
|
||||
}
|
||||
else if (level == LVL.local)
|
||||
{
|
||||
if (specifier.scw & SCW.xextern)
|
||||
stc = AST.STC.extern_;
|
||||
stc = AST.STC.extern_;
|
||||
else if (specifier.scw & SCW.xstatic)
|
||||
stc = AST.STC.static_;
|
||||
}
|
||||
else if (level == LVL.member)
|
||||
{
|
||||
if (specifier.scw & SCW.xextern)
|
||||
stc = AST.STC.extern_;
|
||||
stc = AST.STC.extern_;
|
||||
else if (specifier.scw & SCW.xstatic)
|
||||
stc = AST.STC.static_;
|
||||
}
|
||||
|
@ -4111,21 +4216,23 @@ final class CParser(AST) : Parser!AST
|
|||
if (level == LVL.global)
|
||||
{
|
||||
if (specifier.scw & SCW.xextern)
|
||||
stc = AST.STC.extern_ | AST.STC.gshared;
|
||||
stc = AST.STC.extern_ | AST.STC.gshared;
|
||||
else if (specifier.scw & SCW.xstatic)
|
||||
stc = AST.STC.gshared | AST.STC.static_;
|
||||
else
|
||||
stc = AST.STC.gshared;
|
||||
}
|
||||
else if (level == LVL.local)
|
||||
{
|
||||
if (specifier.scw & SCW.xextern)
|
||||
stc = AST.STC.extern_ | AST.STC.gshared;
|
||||
stc = AST.STC.extern_ | AST.STC.gshared;
|
||||
else if (specifier.scw & SCW.xstatic)
|
||||
stc = AST.STC.gshared;
|
||||
}
|
||||
else if (level == LVL.member)
|
||||
{
|
||||
if (specifier.scw & SCW.xextern)
|
||||
stc = AST.STC.extern_ | AST.STC.gshared;
|
||||
stc = AST.STC.extern_ | AST.STC.gshared;
|
||||
else if (specifier.scw & SCW.xstatic)
|
||||
stc = AST.STC.gshared;
|
||||
}
|
||||
|
@ -4235,15 +4342,59 @@ final class CParser(AST) : Parser!AST
|
|||
*/
|
||||
private AST.Dsymbol applySpecifier(AST.Dsymbol s, ref Specifier specifier)
|
||||
{
|
||||
//printf("applySpecifier() %s\n", s.toChars());
|
||||
if (specifier.alignExps)
|
||||
{
|
||||
//printf(" applying _Alignas %s, packalign %d\n", (*specifier.alignExps)[0].toChars(), cast(int)specifier.packalign);
|
||||
// Wrap declaration in an AlignDeclaration
|
||||
auto decls = new AST.Dsymbols(1);
|
||||
(*decls)[0] = s;
|
||||
s = new AST.AlignDeclaration(s.loc, specifier.alignExps, decls);
|
||||
}
|
||||
else if (!specifier.packalign.isDefault())
|
||||
{
|
||||
//printf(" applying packalign %d\n", cast(int)specifier.packalign);
|
||||
// Wrap #pragma pack in an AlignDeclaration
|
||||
auto decls = new AST.Dsymbols(1);
|
||||
(*decls)[0] = s;
|
||||
s = new AST.AlignDeclaration(s.loc, specifier.packalign, decls);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* Add global target-dependent builtin declarations.
|
||||
*/
|
||||
private void addBuiltinDeclarations()
|
||||
{
|
||||
void genBuiltinFunc(Identifier id, AST.VarArg va)
|
||||
{
|
||||
auto tva_list = new AST.TypeIdentifier(Loc.initial, Id.builtin_va_list);
|
||||
auto parameters = new AST.Parameters();
|
||||
parameters.push(new AST.Parameter(STC.parameter | STC.ref_, tva_list, null, null, null));
|
||||
auto pl = AST.ParameterList(parameters, va, 0);
|
||||
auto tf = new AST.TypeFunction(pl, AST.Type.tvoid, LINK.c, 0);
|
||||
auto s = new AST.FuncDeclaration(Loc.initial, Loc.initial, id, AST.STC.static_, tf, false);
|
||||
symbols.push(s);
|
||||
}
|
||||
|
||||
/* void __builtin_va_start(__builtin_va_list, ...);
|
||||
* The second argument is supposed to be of any type, so fake it with the ...
|
||||
*/
|
||||
genBuiltinFunc(Id.builtin_va_start, AST.VarArg.variadic);
|
||||
|
||||
/* void __builtin_va_end(__builtin_va_list);
|
||||
*/
|
||||
genBuiltinFunc(Id.builtin_va_end, AST.VarArg.none);
|
||||
|
||||
/* struct __va_list_tag
|
||||
* {
|
||||
* uint, uint, void*, void*
|
||||
* }
|
||||
*/
|
||||
auto s = new AST.StructDeclaration(Loc.initial, Id.va_list_tag, false);
|
||||
symbols.push(s);
|
||||
}
|
||||
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ import dmd.identifier;
|
|||
import dmd.mtype;
|
||||
import dmd.nspace;
|
||||
import dmd.root.array;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.target;
|
||||
|
@ -98,21 +98,20 @@ extern(C++) const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset)
|
|||
}
|
||||
|
||||
/******************************
|
||||
* Determine if sym is the 'primary' destructor, that is,
|
||||
* the most-aggregate destructor (the one that is defined as __xdtor)
|
||||
* Determine if sym is a full aggregate destructor.
|
||||
* Params:
|
||||
* sym = Dsymbol
|
||||
* Returns:
|
||||
* true if sym is the primary destructor for an aggregate
|
||||
* true if sym is an aggregate destructor
|
||||
*/
|
||||
bool isPrimaryDtor(const Dsymbol sym)
|
||||
bool isAggregateDtor(const Dsymbol sym)
|
||||
{
|
||||
const dtor = sym.isDtorDeclaration();
|
||||
if (!dtor)
|
||||
return false;
|
||||
const ad = dtor.isMember();
|
||||
assert(ad);
|
||||
return dtor == ad.primaryDtor;
|
||||
return dtor == ad.aggrDtor;
|
||||
}
|
||||
|
||||
/// Context used when processing pre-semantic AST
|
||||
|
@ -1069,7 +1068,7 @@ private final class CppMangleVisitor : Visitor
|
|||
|
||||
if (auto ctor = d.isCtorDeclaration())
|
||||
buf.writestring(ctor.isCpCtor ? "C2" : "C1");
|
||||
else if (d.isPrimaryDtor())
|
||||
else if (d.isAggregateDtor())
|
||||
buf.writestring("D1");
|
||||
else if (d.ident && d.ident == Id.assign)
|
||||
buf.writestring("aS");
|
||||
|
@ -1184,7 +1183,7 @@ private final class CppMangleVisitor : Visitor
|
|||
mangleFunctionParameters(tf.parameterList);
|
||||
return;
|
||||
}
|
||||
else if (d.isPrimaryDtor())
|
||||
else if (d.isAggregateDtor())
|
||||
{
|
||||
buf.writestring("D1");
|
||||
mangleFunctionParameters(tf.parameterList);
|
||||
|
|
|
@ -685,6 +685,11 @@ bool isSafePointerCast(Type srcPointee, Type destPointee)
|
|||
// It's OK if both are the same (modulo const)
|
||||
if (srcPointee.constConv(destPointee))
|
||||
return true;
|
||||
|
||||
// It's ok to cast from/to shared because CTFE is single threaded anyways
|
||||
if (srcPointee.unSharedOf() == destPointee.unSharedOf())
|
||||
return true;
|
||||
|
||||
// It's OK if function pointers differ only in safe/pure/nothrow
|
||||
if (srcPointee.ty == Tfunction && destPointee.ty == Tfunction)
|
||||
return srcPointee.covariant(destPointee) == Covariant.yes ||
|
||||
|
|
|
@ -31,12 +31,13 @@ import dmd.func;
|
|||
import dmd.globals;
|
||||
import dmd.impcnvtab;
|
||||
import dmd.id;
|
||||
import dmd.importc;
|
||||
import dmd.init;
|
||||
import dmd.intrange;
|
||||
import dmd.mtype;
|
||||
import dmd.opover;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.tokens;
|
||||
import dmd.typesem;
|
||||
|
@ -1445,6 +1446,29 @@ MATCH implicitConvTo(Expression e, Type t)
|
|||
if (tb.ty == Tpointer && e.e1.op == TOK.string_)
|
||||
e.e1.accept(this);
|
||||
}
|
||||
|
||||
override void visit(TupleExp e)
|
||||
{
|
||||
result = e.type.implicitConvTo(t);
|
||||
if (result != MATCH.nomatch)
|
||||
return;
|
||||
|
||||
/* If target type is a tuple of same length, test conversion of
|
||||
* each expression to the corresponding type in the tuple.
|
||||
*/
|
||||
TypeTuple totuple = t.isTypeTuple();
|
||||
if (totuple && e.exps.length == totuple.arguments.length)
|
||||
{
|
||||
result = MATCH.exact;
|
||||
foreach (i, ex; *e.exps)
|
||||
{
|
||||
auto to = (*totuple.arguments)[i].type;
|
||||
MATCH mi = ex.implicitConvTo(to);
|
||||
if (mi < result)
|
||||
result = mi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope ImplicitConvTo v = new ImplicitConvTo(t);
|
||||
|
@ -1476,12 +1500,8 @@ MATCH cimplicitConvTo(Expression e, Type t)
|
|||
return MATCH.convert;
|
||||
if (tb.isintegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6
|
||||
return MATCH.convert;
|
||||
if (tb.ty == Tpointer && typeb.ty == Tpointer)
|
||||
{
|
||||
if (tb.isTypePointer().next.ty == Tvoid ||
|
||||
typeb.isTypePointer().next.ty == Tvoid)
|
||||
return MATCH.convert; // convert to/from void* C11 6.3.2.3-1
|
||||
}
|
||||
if (tb.ty == Tpointer && typeb.ty == Tpointer) // C11 6.3.2.3-7
|
||||
return MATCH.convert;
|
||||
|
||||
return implicitConvTo(e, t);
|
||||
}
|
||||
|
@ -2189,13 +2209,20 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
|
|||
return;
|
||||
}
|
||||
|
||||
/* If target type is a tuple of same length, cast each expression to
|
||||
* the corresponding type in the tuple.
|
||||
*/
|
||||
TypeTuple totuple;
|
||||
if (auto tt = t.isTypeTuple())
|
||||
totuple = e.exps.length == tt.arguments.length ? tt : null;
|
||||
|
||||
TupleExp te = e.copy().isTupleExp();
|
||||
te.e0 = e.e0 ? e.e0.copy() : null;
|
||||
te.exps = e.exps.copy();
|
||||
for (size_t i = 0; i < te.exps.dim; i++)
|
||||
{
|
||||
Expression ex = (*te.exps)[i];
|
||||
ex = ex.castTo(sc, t);
|
||||
ex = ex.castTo(sc, totuple ? (*totuple.arguments)[i].type : t);
|
||||
(*te.exps)[i] = ex;
|
||||
}
|
||||
result = te;
|
||||
|
@ -2821,6 +2848,13 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
|
|||
Expression e1 = pe1;
|
||||
Expression e2 = pe2;
|
||||
|
||||
// ImportC: do array/function conversions
|
||||
if (sc)
|
||||
{
|
||||
e1 = e1.arrayFuncConv(sc);
|
||||
e2 = e2.arrayFuncConv(sc);
|
||||
}
|
||||
|
||||
Type Lret(Type result)
|
||||
{
|
||||
pe1 = e1;
|
||||
|
@ -2838,7 +2872,7 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Converts one of the expression too the other
|
||||
/// Converts one of the expression to the other
|
||||
Type convert(ref Expression from, Type to)
|
||||
{
|
||||
from = from.castTo(sc, to);
|
||||
|
@ -2856,6 +2890,22 @@ Type typeMerge(Scope* sc, TOK op, ref Expression pe1, ref Expression pe2)
|
|||
Type t1b = e1.type.toBasetype();
|
||||
Type t2b = e2.type.toBasetype();
|
||||
|
||||
if (sc && sc.flags & SCOPE.Cfile)
|
||||
{
|
||||
// Integral types can be implicitly converted to pointers
|
||||
if ((t1b.ty == Tpointer) != (t2b.ty == Tpointer))
|
||||
{
|
||||
if (t1b.isintegral())
|
||||
{
|
||||
return convert(e1, t2b);
|
||||
}
|
||||
else if (t2b.isintegral())
|
||||
{
|
||||
return convert(e2, t1b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (op != TOK.question || t1b.ty != t2b.ty && (t1b.isTypeBasic() && t2b.isTypeBasic()))
|
||||
{
|
||||
if (op == TOK.question && t1b.ty.isSomeChar() && t2b.ty.isSomeChar())
|
||||
|
@ -3132,6 +3182,14 @@ Lagain:
|
|||
Lcc:
|
||||
while (1)
|
||||
{
|
||||
MATCH i1woat = MATCH.exact;
|
||||
MATCH i2woat = MATCH.exact;
|
||||
|
||||
if (auto t2c = t2.isTypeClass())
|
||||
i1woat = t2c.implicitConvToWithoutAliasThis(t1);
|
||||
if (auto t1c = t1.isTypeClass())
|
||||
i2woat = t1c.implicitConvToWithoutAliasThis(t2);
|
||||
|
||||
MATCH i1 = e2.implicitConvTo(t1);
|
||||
MATCH i2 = e1.implicitConvTo(t2);
|
||||
|
||||
|
@ -3144,11 +3202,26 @@ Lagain:
|
|||
i2 = MATCH.nomatch;
|
||||
}
|
||||
|
||||
if (i2)
|
||||
// Match but without 'alias this' on classes
|
||||
if (i2 && i2woat)
|
||||
return coerce(t2);
|
||||
if (i1)
|
||||
if (i1 && i1woat)
|
||||
return coerce(t1);
|
||||
|
||||
// Here use implicitCastTo() instead of castTo() to try 'alias this' on classes
|
||||
Type coerceImplicit(Type towards)
|
||||
{
|
||||
e1 = e1.implicitCastTo(sc, towards);
|
||||
e2 = e2.implicitCastTo(sc, towards);
|
||||
return Lret(towards);
|
||||
}
|
||||
|
||||
// Implicit conversion with 'alias this'
|
||||
if (i2)
|
||||
return coerceImplicit(t2);
|
||||
if (i1)
|
||||
return coerceImplicit(t1);
|
||||
|
||||
if (t1.ty == Tclass && t2.ty == Tclass)
|
||||
{
|
||||
TypeClass tc1 = t1.isTypeClass();
|
||||
|
@ -3257,29 +3330,26 @@ Lagain:
|
|||
}
|
||||
}
|
||||
|
||||
if (t1.ty == Tstruct || t2.ty == Tstruct)
|
||||
if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
|
||||
{
|
||||
if (t1.ty == Tstruct && t1.isTypeStruct().sym.aliasthis)
|
||||
{
|
||||
if (isRecursiveAliasThis(att1, e1.type))
|
||||
return null;
|
||||
//printf("att tmerge(s || s) e1 = %s\n", e1.type.toChars());
|
||||
e1 = resolveAliasThis(sc, e1);
|
||||
t1 = e1.type;
|
||||
t = t1;
|
||||
goto Lagain;
|
||||
}
|
||||
if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
|
||||
{
|
||||
if (isRecursiveAliasThis(att2, e2.type))
|
||||
return null;
|
||||
//printf("att tmerge(s || s) e2 = %s\n", e2.type.toChars());
|
||||
e2 = resolveAliasThis(sc, e2);
|
||||
t2 = e2.type;
|
||||
t = t2;
|
||||
goto Lagain;
|
||||
}
|
||||
return null;
|
||||
if (isRecursiveAliasThis(att1, e1.type))
|
||||
return null;
|
||||
//printf("att tmerge(s || s) e1 = %s\n", e1.type.toChars());
|
||||
e1 = resolveAliasThis(sc, e1);
|
||||
t1 = e1.type;
|
||||
t = t1;
|
||||
goto Lagain;
|
||||
}
|
||||
|
||||
if (t2.ty == Tstruct && t2.isTypeStruct().sym.aliasthis)
|
||||
{
|
||||
if (isRecursiveAliasThis(att2, e2.type))
|
||||
return null;
|
||||
//printf("att tmerge(s || s) e2 = %s\n", e2.type.toChars());
|
||||
e2 = resolveAliasThis(sc, e2);
|
||||
t2 = e2.type;
|
||||
t = t2;
|
||||
goto Lagain;
|
||||
}
|
||||
|
||||
if ((e1.op == TOK.string_ || e1.op == TOK.null_) && e1.implicitConvTo(t2))
|
||||
|
|
|
@ -20,6 +20,7 @@ import dmd.aggregate;
|
|||
import dmd.apply;
|
||||
import dmd.arraytypes;
|
||||
import dmd.astenums;
|
||||
import dmd.attrib;
|
||||
import dmd.gluelayer;
|
||||
import dmd.declaration;
|
||||
import dmd.dscope;
|
||||
|
@ -367,7 +368,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
|
|||
baseok = Baseok.none;
|
||||
}
|
||||
|
||||
static ClassDeclaration create(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
|
||||
static ClassDeclaration create(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject)
|
||||
{
|
||||
return new ClassDeclaration(loc, id, baseclasses, members, inObject);
|
||||
}
|
||||
|
@ -607,7 +608,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
|
|||
|
||||
if (!b.sym.alignsize)
|
||||
b.sym.alignsize = target.ptrsize;
|
||||
alignmember(b.sym.alignsize, b.sym.alignsize, &offset);
|
||||
alignmember(structalign_t(cast(ushort)b.sym.alignsize), b.sym.alignsize, &offset);
|
||||
assert(bi < vtblInterfaces.dim);
|
||||
|
||||
BaseClass* bv = (*vtblInterfaces)[bi];
|
||||
|
@ -725,6 +726,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
|
|||
|
||||
void searchVtbl(ref Dsymbols vtbl)
|
||||
{
|
||||
bool seenInterfaceVirtual;
|
||||
foreach (s; vtbl)
|
||||
{
|
||||
auto fd = s.isFuncDeclaration();
|
||||
|
@ -748,6 +750,23 @@ extern (C++) class ClassDeclaration : AggregateDeclaration
|
|||
if (fd == fdmatch)
|
||||
continue;
|
||||
|
||||
/* Functions overriding interface functions for extern(C++) with VC++
|
||||
* are not in the normal vtbl, but in vtblFinal. If the implementation
|
||||
* is again overridden in a child class, both would be found here.
|
||||
* The function in the child class should override the function
|
||||
* in the base class, which is done here, because searchVtbl is first
|
||||
* called for the child class. Checking seenInterfaceVirtual makes
|
||||
* sure, that the compared functions are not in the same vtbl.
|
||||
*/
|
||||
if (fd.interfaceVirtual &&
|
||||
fd.interfaceVirtual is fdmatch.interfaceVirtual &&
|
||||
!seenInterfaceVirtual &&
|
||||
fdmatch.type.covariant(fd.type) == Covariant.yes)
|
||||
{
|
||||
seenInterfaceVirtual = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
// Function type matching: exact > covariant
|
||||
MATCH m1 = tf.equals(fd.type) ? MATCH.exact : MATCH.nomatch;
|
||||
|
|
|
@ -16,6 +16,7 @@ import core.stdc.stdio;
|
|||
import dmd.aggregate;
|
||||
import dmd.arraytypes;
|
||||
import dmd.astenums;
|
||||
import dmd.attrib;
|
||||
import dmd.ctorflow;
|
||||
import dmd.dclass;
|
||||
import dmd.delegatize;
|
||||
|
@ -34,7 +35,7 @@ import dmd.init;
|
|||
import dmd.initsem;
|
||||
import dmd.intrange;
|
||||
import dmd.mtype;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.target;
|
||||
import dmd.tokens;
|
||||
|
@ -705,7 +706,7 @@ extern (C++) final class AliasDeclaration : Declaration
|
|||
assert(s);
|
||||
}
|
||||
|
||||
static AliasDeclaration create(Loc loc, Identifier id, Type type)
|
||||
static AliasDeclaration create(const ref Loc loc, Identifier id, Type type)
|
||||
{
|
||||
return new AliasDeclaration(loc, id, type);
|
||||
}
|
||||
|
@ -1192,14 +1193,7 @@ extern (C++) class VarDeclaration : Declaration
|
|||
/* If coming after a bit field in progress,
|
||||
* advance past the field
|
||||
*/
|
||||
if (fieldState.inFlight)
|
||||
{
|
||||
fieldState.inFlight = false;
|
||||
if (0 && target.os & Target.OS.Posix)
|
||||
fieldState.offset += (fieldState.bitOffset + 7) / 8;
|
||||
else if (0 &&target.os == Target.OS.Windows)
|
||||
fieldState.offset += fieldState.fieldSize;
|
||||
}
|
||||
fieldState.inFlight = false;
|
||||
|
||||
const sz = t.size(loc);
|
||||
assert(sz != SIZE_INVALID && sz < uint.max);
|
||||
|
@ -1743,13 +1737,23 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
|
|||
|
||||
override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion)
|
||||
{
|
||||
//printf("BitFieldDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars());
|
||||
//printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), toChars());
|
||||
static void print(const ref FieldState fieldState)
|
||||
{
|
||||
printf("FieldState.offset = %d bytes\n", fieldState.offset);
|
||||
printf(" .fieldOffset = %d bytes\n", fieldState.fieldOffset);
|
||||
printf(" .bitOffset = %d bits\n", fieldState.bitOffset);
|
||||
printf(" .fieldSize = %d bytes\n", fieldState.fieldSize);
|
||||
printf(" .inFlight = %d\n\n", fieldState.inFlight);
|
||||
}
|
||||
//print(fieldState);
|
||||
|
||||
Type t = type.toBasetype();
|
||||
const bool anon = isAnonymous();
|
||||
|
||||
// List in ad.fields. Even if the type is error, it's necessary to avoid
|
||||
// pointless error diagnostic "more initializers than fields" on struct literal.
|
||||
if (!isAnonymous())
|
||||
if (!anon)
|
||||
ad.fields.push(this);
|
||||
|
||||
if (t.ty == Terror)
|
||||
|
@ -1760,17 +1764,36 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
|
|||
uint memsize = cast(uint)sz; // size of member
|
||||
uint memalignsize = target.fieldalign(t); // size of member for alignment purposes
|
||||
|
||||
if (fieldWidth == 0 && !isAnonymous())
|
||||
if (fieldWidth == 0 && !anon)
|
||||
error(loc, "named bit fields cannot have 0 width");
|
||||
if (fieldWidth > memsize * 8)
|
||||
error(loc, "bit field width %d is larger than type", fieldWidth);
|
||||
|
||||
const style = target.c.bitFieldStyle;
|
||||
|
||||
void startNewField()
|
||||
{
|
||||
uint alignsize;
|
||||
if (style == TargetC.BitFieldStyle.Gcc_Clang)
|
||||
{
|
||||
if (fieldWidth > 32)
|
||||
alignsize = memalignsize;
|
||||
else if (fieldWidth > 16)
|
||||
alignsize = 4;
|
||||
else if (fieldWidth > 8)
|
||||
alignsize = 2;
|
||||
else
|
||||
alignsize = 1;
|
||||
}
|
||||
else
|
||||
alignsize = memsize; // not memalignsize
|
||||
|
||||
uint dummy;
|
||||
offset = AggregateDeclaration.placeField(
|
||||
&fieldState.offset,
|
||||
memsize, memalignsize, alignment,
|
||||
&ad.structsize, &ad.alignsize,
|
||||
memsize, alignsize, alignment,
|
||||
&ad.structsize,
|
||||
(anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? &dummy : &ad.alignsize,
|
||||
isunion);
|
||||
|
||||
fieldState.inFlight = true;
|
||||
|
@ -1779,19 +1802,92 @@ extern (C++) class BitFieldDeclaration : VarDeclaration
|
|||
fieldState.fieldSize = memsize;
|
||||
}
|
||||
|
||||
if (!fieldState.inFlight || fieldWidth == 0)
|
||||
if (style == TargetC.BitFieldStyle.Gcc_Clang)
|
||||
{
|
||||
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)
|
||||
|
|
|
@ -188,7 +188,7 @@ public:
|
|||
Dsymbol *overnext; // next in overload list
|
||||
Dsymbol *_import; // !=NULL if unresolved internal alias for selective import
|
||||
|
||||
static AliasDeclaration *create(Loc loc, Identifier *id, Type *type);
|
||||
static AliasDeclaration *create(const Loc &loc, Identifier *id, Type *type);
|
||||
AliasDeclaration *syntaxCopy(Dsymbol *);
|
||||
bool overloadInsert(Dsymbol *s);
|
||||
const char *kind() const;
|
||||
|
@ -511,7 +511,7 @@ enum class BUILTIN : unsigned char
|
|||
toPrecReal
|
||||
};
|
||||
|
||||
Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments);
|
||||
Expression *eval_builtin(const Loc &loc, FuncDeclaration *fd, Expressions *arguments);
|
||||
BUILTIN isBuiltin(FuncDeclaration *fd);
|
||||
|
||||
class FuncDeclaration : public Declaration
|
||||
|
@ -535,6 +535,8 @@ public:
|
|||
VarDeclaration *vresult; // result variable for out contracts
|
||||
LabelDsymbol *returnLabel; // where the return goes
|
||||
|
||||
void *isTypeIsolatedCache; // An AA on the D side to cache an expensive check result
|
||||
|
||||
// used to prevent symbols in different
|
||||
// scopes from having the same name
|
||||
DsymbolTable *localsymtab;
|
||||
|
@ -839,9 +841,6 @@ public:
|
|||
class NewDeclaration : public FuncDeclaration
|
||||
{
|
||||
public:
|
||||
Parameters *parameters;
|
||||
VarArg varargs;
|
||||
|
||||
NewDeclaration *syntaxCopy(Dsymbol *);
|
||||
const char *kind() const;
|
||||
bool isVirtual() const;
|
||||
|
|
|
@ -42,6 +42,7 @@ import dmd.mtype;
|
|||
import dmd.printast;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.array;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.region;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.statement;
|
||||
|
@ -75,6 +76,7 @@ public Expression ctfeInterpret(Expression e)
|
|||
case TOK.template_: // non-eponymous template/instance
|
||||
case TOK.scope_: // ditto
|
||||
case TOK.dotTemplateDeclaration: // ditto, e.e1 doesn't matter here
|
||||
case TOK.dotTemplateInstance: // ditto
|
||||
case TOK.dot: // ditto
|
||||
if (e.type.ty == Terror)
|
||||
return ErrorExp.get();
|
||||
|
@ -2167,26 +2169,20 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
// Note: This is a workaround for
|
||||
// https://issues.dlang.org/show_bug.cgi?id=17351
|
||||
// The aforementioned bug triggers when passing manifest constant by `ref`.
|
||||
// If there was not a previous reference to them, they are
|
||||
// not cached and trigger a "cannot be read at compile time".
|
||||
// This fix is a crude solution to get it to work. A more proper
|
||||
// approach would be to resolve the forward reference, but that is
|
||||
// much more involved.
|
||||
if (goal == CTFEGoal.LValue && e.var.type.isMutable())
|
||||
if (goal == CTFEGoal.LValue)
|
||||
{
|
||||
if (auto v = e.var.isVarDeclaration())
|
||||
{
|
||||
if (!v.isDataseg() && !v.isCTFE() && !istate)
|
||||
{
|
||||
e.error("variable `%s` cannot be read at compile time", v.toChars());
|
||||
result = CTFEExp.cantexp;
|
||||
return;
|
||||
}
|
||||
if (!hasValue(v))
|
||||
{
|
||||
// Compile-time known non-CTFE variable from an outer context
|
||||
// e.g. global or from a ref argument
|
||||
if (v.isConst() || v.isImmutable())
|
||||
{
|
||||
result = getVarExp(e.loc, istate, v, goal);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!v.isCTFE() && v.isDataseg())
|
||||
e.error("static variable `%s` cannot be read at compile time", v.toChars());
|
||||
else // CTFE initiated from inside a function
|
||||
|
@ -2201,7 +2197,7 @@ public:
|
|||
Expression ev = getValue(v);
|
||||
if (ev.op == TOK.variable ||
|
||||
ev.op == TOK.index ||
|
||||
ev.op == TOK.slice ||
|
||||
(ev.op == TOK.slice && ev.type.toBasetype().ty == Tsarray) ||
|
||||
ev.op == TOK.dotVariable)
|
||||
{
|
||||
result = interpret(pue, ev, istate, goal);
|
||||
|
@ -2836,8 +2832,7 @@ public:
|
|||
auto se = ctfeEmplaceExp!StructLiteralExp(e.loc, cast(StructDeclaration)cd, elems, e.newtype);
|
||||
se.origin = se;
|
||||
se.ownedByCtfe = OwnedBy.ctfe;
|
||||
emplaceExp!(ClassReferenceExp)(pue, e.loc, se, e.type);
|
||||
Expression eref = pue.exp();
|
||||
Expression eref = ctfeEmplaceExp!ClassReferenceExp(e.loc, se, e.type);
|
||||
if (e.member)
|
||||
{
|
||||
// Call constructor
|
||||
|
@ -6024,12 +6019,23 @@ public:
|
|||
}
|
||||
if (e.to.ty == Tsarray)
|
||||
e1 = resolveSlice(e1);
|
||||
if (e.to.toBasetype().ty == Tbool && e1.type.ty == Tpointer)
|
||||
|
||||
auto tobt = e.to.toBasetype();
|
||||
if (tobt.ty == Tbool && e1.type.ty == Tpointer)
|
||||
{
|
||||
emplaceExp!(IntegerExp)(pue, e.loc, e1.op != TOK.null_, e.to);
|
||||
result = pue.exp();
|
||||
return;
|
||||
}
|
||||
else if (tobt.isTypeBasic() && e1.op == TOK.null_)
|
||||
{
|
||||
if (tobt.isintegral())
|
||||
emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to);
|
||||
else if (tobt.isreal())
|
||||
emplaceExp!(RealExp)(pue, e.loc, CTFloat.zero, e.to);
|
||||
result = pue.exp();
|
||||
return;
|
||||
}
|
||||
result = ctfeCast(pue, e.loc, e.type, e.to, e1);
|
||||
}
|
||||
|
||||
|
@ -6306,7 +6312,7 @@ public:
|
|||
auto tsa = cast(TypeSArray)v.type;
|
||||
auto len = cast(size_t)tsa.dim.toInteger();
|
||||
UnionExp ue = void;
|
||||
result = createBlockDuplicatedArrayLiteral(&ue, ex.loc, v.type, ex, len);
|
||||
result = createBlockDuplicatedArrayLiteral(&ue, e.loc, v.type, result, len);
|
||||
if (result == ue.exp())
|
||||
result = ue.copy();
|
||||
(*se.elements)[i] = result;
|
||||
|
|
|
@ -16,7 +16,7 @@ import core.stdc.string;
|
|||
import dmd.doc;
|
||||
import dmd.errors;
|
||||
import dmd.globals;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
|
||||
extern (C++) struct MacroTable
|
||||
|
|
|
@ -139,7 +139,7 @@ import dmd.id;
|
|||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.aav;
|
||||
import dmd.root.string;
|
||||
import dmd.root.stringtable;
|
||||
|
@ -1259,14 +1259,49 @@ public:
|
|||
|
||||
override void visit(Parameter p)
|
||||
{
|
||||
if (p.storageClass & STC.scope_ && !(p.storageClass & STC.scopeinferred))
|
||||
buf.writeByte('M');
|
||||
// https://dlang.org/spec/abi.html#Parameter
|
||||
|
||||
auto stc = p.storageClass;
|
||||
|
||||
// Inferred storage classes don't get mangled in
|
||||
if (stc & STC.scopeinferred)
|
||||
stc &= ~(STC.scope_ | STC.scopeinferred);
|
||||
if (stc & STC.returninferred)
|
||||
stc &= ~(STC.return_ | STC.returninferred);
|
||||
|
||||
// 'return inout ref' is the same as 'inout ref'
|
||||
if ((p.storageClass & (STC.return_ | STC.wild)) == STC.return_ &&
|
||||
!(p.storageClass & STC.returninferred))
|
||||
buf.writestring("Nk");
|
||||
switch (p.storageClass & (STC.IOR | STC.lazy_))
|
||||
if ((stc & (STC.return_ | STC.wild)) == (STC.return_ | STC.wild))
|
||||
stc &= ~STC.return_;
|
||||
|
||||
// much like hdrgen.stcToBuffer()
|
||||
string rrs;
|
||||
const isout = (stc & STC.out_) != 0;
|
||||
final switch (buildScopeRef(stc))
|
||||
{
|
||||
case ScopeRef.None:
|
||||
case ScopeRef.Scope:
|
||||
case ScopeRef.Ref:
|
||||
case ScopeRef.Return:
|
||||
case ScopeRef.RefScope:
|
||||
break;
|
||||
|
||||
case ScopeRef.ReturnScope: rrs = "NkM"; goto L1; // return scope
|
||||
case ScopeRef.ReturnRef: rrs = isout ? "NkJ" : "NkK"; goto L1; // return ref
|
||||
case ScopeRef.ReturnRef_Scope: rrs = isout ? "MNkJ" : "MNkK"; goto L1; // scope return ref
|
||||
case ScopeRef.Ref_ReturnScope: rrs = isout ? "NkMJ" : "NkMK"; goto L1; // return scope ref
|
||||
L1:
|
||||
buf.writestring(rrs);
|
||||
stc &= ~(STC.out_ | STC.scope_ | STC.ref_ | STC.return_);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stc & STC.scope_)
|
||||
buf.writeByte('M'); // scope
|
||||
|
||||
if (stc & STC.return_)
|
||||
buf.writestring("Nk"); // return
|
||||
|
||||
switch (stc & (STC.IOR | STC.lazy_))
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
@ -1288,10 +1323,10 @@ public:
|
|||
default:
|
||||
debug
|
||||
{
|
||||
printf("storageClass = x%llx\n", p.storageClass & (STC.IOR | STC.lazy_));
|
||||
printf("storageClass = x%llx\n", stc & (STC.IOR | STC.lazy_));
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
visitWithMask(p.type, (p.storageClass & STC.in_) ? MODFlags.const_ : 0);
|
||||
visitWithMask(p.type, (stc & STC.in_) ? MODFlags.const_ : 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import dmd.dsymbolsem;
|
|||
import dmd.errors;
|
||||
import dmd.expression;
|
||||
import dmd.expressionsem;
|
||||
import dmd.file_manager;
|
||||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
|
@ -39,7 +40,7 @@ import dmd.cparse;
|
|||
import dmd.root.array;
|
||||
import dmd.root.file;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.port;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
|
@ -50,113 +51,6 @@ import dmd.target;
|
|||
import dmd.utils;
|
||||
import dmd.visitor;
|
||||
|
||||
enum package_d = "package." ~ mars_ext;
|
||||
enum package_di = "package." ~ hdr_ext;
|
||||
|
||||
/********************************************
|
||||
* Look for the source file if it's different from filename.
|
||||
* Look for .di, .d, directory, and along global.path.
|
||||
* Does not open the file.
|
||||
* Params:
|
||||
* filename = as supplied by the user
|
||||
* path = path to look for filename
|
||||
* Returns:
|
||||
* the found file name or
|
||||
* `null` if it is not different from filename.
|
||||
*/
|
||||
private const(char)[] lookForSourceFile(const char[] filename, const char*[] path)
|
||||
{
|
||||
//printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr);
|
||||
/* Search along path[] for .di file, then .d file, then .i file, then .c file.
|
||||
*/
|
||||
const sdi = FileName.forceExt(filename, hdr_ext);
|
||||
if (FileName.exists(sdi) == 1)
|
||||
return sdi;
|
||||
scope(exit) FileName.free(sdi.ptr);
|
||||
|
||||
const sd = FileName.forceExt(filename, mars_ext);
|
||||
if (FileName.exists(sd) == 1)
|
||||
return sd;
|
||||
scope(exit) FileName.free(sd.ptr);
|
||||
|
||||
const si = FileName.forceExt(filename, i_ext);
|
||||
if (FileName.exists(si) == 1)
|
||||
return si;
|
||||
scope(exit) FileName.free(si.ptr);
|
||||
|
||||
const sc = FileName.forceExt(filename, c_ext);
|
||||
if (FileName.exists(sc) == 1)
|
||||
return sc;
|
||||
scope(exit) FileName.free(sc.ptr);
|
||||
|
||||
if (FileName.exists(filename) == 2)
|
||||
{
|
||||
/* The filename exists and it's a directory.
|
||||
* Therefore, the result should be: filename/package.d
|
||||
* iff filename/package.d is a file
|
||||
*/
|
||||
const ni = FileName.combine(filename, package_di);
|
||||
if (FileName.exists(ni) == 1)
|
||||
return ni;
|
||||
FileName.free(ni.ptr);
|
||||
|
||||
const n = FileName.combine(filename, package_d);
|
||||
if (FileName.exists(n) == 1)
|
||||
return n;
|
||||
FileName.free(n.ptr);
|
||||
}
|
||||
if (FileName.absolute(filename))
|
||||
return null;
|
||||
if (!path.length)
|
||||
return null;
|
||||
foreach (entry; path)
|
||||
{
|
||||
const p = entry.toDString();
|
||||
|
||||
const(char)[] n = FileName.combine(p, sdi);
|
||||
if (FileName.exists(n) == 1) {
|
||||
return n;
|
||||
}
|
||||
FileName.free(n.ptr);
|
||||
|
||||
n = FileName.combine(p, sd);
|
||||
if (FileName.exists(n) == 1) {
|
||||
return n;
|
||||
}
|
||||
FileName.free(n.ptr);
|
||||
|
||||
n = FileName.combine(p, si);
|
||||
if (FileName.exists(n) == 1) {
|
||||
return n;
|
||||
}
|
||||
FileName.free(n.ptr);
|
||||
|
||||
n = FileName.combine(p, sc);
|
||||
if (FileName.exists(n) == 1) {
|
||||
return n;
|
||||
}
|
||||
FileName.free(n.ptr);
|
||||
|
||||
const b = FileName.removeExt(filename);
|
||||
n = FileName.combine(p, b);
|
||||
FileName.free(b.ptr);
|
||||
if (FileName.exists(n) == 2)
|
||||
{
|
||||
const n2i = FileName.combine(n, package_di);
|
||||
if (FileName.exists(n2i) == 1)
|
||||
return n2i;
|
||||
FileName.free(n2i.ptr);
|
||||
const n2 = FileName.combine(n, package_d);
|
||||
if (FileName.exists(n2) == 1) {
|
||||
return n2;
|
||||
}
|
||||
FileName.free(n2.ptr);
|
||||
}
|
||||
FileName.free(n.ptr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// function used to call semantic3 on a module's dependencies
|
||||
void semantic3OnDependencies(Module m)
|
||||
{
|
||||
|
@ -414,8 +308,8 @@ extern (C++) class Package : ScopeDsymbol
|
|||
packages ~= s.ident;
|
||||
reverse(packages);
|
||||
|
||||
if (lookForSourceFile(getFilename(packages, ident), global.path ? (*global.path)[] : null))
|
||||
Module.load(Loc(), packages, this.ident);
|
||||
if (FileManager.lookForSourceFile(getFilename(packages, ident), global.path ? (*global.path)[] : null))
|
||||
Module.load(Loc.initial, packages, this.ident);
|
||||
else
|
||||
isPkgMod = PKG.package_;
|
||||
}
|
||||
|
@ -598,12 +492,12 @@ extern (C++) final class Module : Package
|
|||
return new Module(Loc.initial, filename, ident, doDocComment, doHdrGen);
|
||||
}
|
||||
|
||||
extern (C++) static Module load(Loc loc, Identifiers* packages, Identifier ident)
|
||||
extern (C++) static Module load(const ref Loc loc, Identifiers* packages, Identifier ident)
|
||||
{
|
||||
return load(loc, packages ? (*packages)[] : null, ident);
|
||||
}
|
||||
|
||||
extern (D) static Module load(Loc loc, Identifier[] packages, Identifier ident)
|
||||
extern (D) static Module load(const ref Loc loc, Identifier[] packages, Identifier ident)
|
||||
{
|
||||
//printf("Module::load(ident = '%s')\n", ident.toChars());
|
||||
// Build module filename by turning:
|
||||
|
@ -612,7 +506,7 @@ extern (C++) final class Module : Package
|
|||
// foo\bar\baz
|
||||
const(char)[] filename = getFilename(packages, ident);
|
||||
// Look for the source file
|
||||
if (const result = lookForSourceFile(filename, global.path ? (*global.path)[] : null))
|
||||
if (const result = FileManager.lookForSourceFile(filename, global.path ? (*global.path)[] : null))
|
||||
filename = result; // leaks
|
||||
|
||||
auto m = new Module(loc, filename, ident, 0, 0);
|
||||
|
@ -737,7 +631,12 @@ extern (C++) final class Module : Package
|
|||
if (isPackageMod)
|
||||
.error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars());
|
||||
else
|
||||
error(loc, "is in file '%s' which cannot be read", srcfile.toChars());
|
||||
{
|
||||
.error(loc, "unable to read module `%s`", toChars());
|
||||
const pkgfile = FileName.combine(FileName.removeExt(srcfile.toString()), package_d);
|
||||
.errorSupplemental(loc, "Expected '%s' or '%s' in one of the following import paths:",
|
||||
srcfile.toChars(), pkgfile.ptr);
|
||||
}
|
||||
}
|
||||
if (!global.gag)
|
||||
{
|
||||
|
@ -776,14 +675,25 @@ extern (C++) final class Module : Package
|
|||
return true; // already read
|
||||
|
||||
//printf("Module::read('%s') file '%s'\n", toChars(), srcfile.toChars());
|
||||
auto readResult = File.read(srcfile.toChars());
|
||||
|
||||
if (global.params.emitMakeDeps)
|
||||
{
|
||||
global.params.makeDeps.push(srcfile.toChars());
|
||||
}
|
||||
|
||||
return loadSourceBuffer(loc, readResult);
|
||||
if (auto readResult = FileManager.fileManager.lookup(srcfile))
|
||||
{
|
||||
srcBuffer = readResult;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto readResult = File.read(srcfile.toChars());
|
||||
if (loadSourceBuffer(loc, readResult))
|
||||
{
|
||||
FileManager.fileManager.add(srcfile, srcBuffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// syntactic parse
|
||||
|
|
|
@ -45,7 +45,7 @@ import dmd.mtype;
|
|||
import dmd.root.array;
|
||||
import dmd.root.file;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.port;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.string;
|
||||
|
@ -3995,8 +3995,8 @@ private size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const r
|
|||
const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments);
|
||||
if (iDelimiterRowEnd)
|
||||
{
|
||||
const delta = replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true);
|
||||
if (delta)
|
||||
size_t delta;
|
||||
if (replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true, delta))
|
||||
{
|
||||
buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd);
|
||||
buf.insert(iEnd + delta, "$(TBODY ");
|
||||
|
@ -4023,12 +4023,15 @@ private size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const r
|
|||
* headerRow = if `true` then the number of columns will be enforced to match
|
||||
* `columnAlignments.length` and the row will be surrounded by a
|
||||
* `THEAD` macro
|
||||
* Returns: the number of characters added by replacing the row, or `0` if unchanged
|
||||
* delta = the number of characters added by replacing the row, or `0` if unchanged
|
||||
* Returns: `true` if a table row was found and replaced
|
||||
*/
|
||||
private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow)
|
||||
private bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta)
|
||||
{
|
||||
delta = 0;
|
||||
|
||||
if (!columnAlignments.length || iStart == iEnd)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
iStart = skipChars(buf, iStart, " \t");
|
||||
int cellCount = 0;
|
||||
|
@ -4045,7 +4048,7 @@ private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, co
|
|||
++cellCount;
|
||||
|
||||
if (headerRow && cellCount != columnAlignments.length)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
if (headerRow && global.params.vmarkdown)
|
||||
{
|
||||
|
@ -4053,8 +4056,6 @@ private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, co
|
|||
message(loc, "Ddoc: formatting table '%.*s'", cast(int)s.length, s.ptr);
|
||||
}
|
||||
|
||||
size_t delta = 0;
|
||||
|
||||
void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di)
|
||||
{
|
||||
const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di);
|
||||
|
@ -4146,7 +4147,7 @@ private size_t replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, co
|
|||
delta += 9;
|
||||
}
|
||||
|
||||
return delta;
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************
|
||||
|
@ -4182,7 +4183,8 @@ private size_t endTable(ref OutBuffer buf, size_t i, ref TableColumnAlignment[]
|
|||
*/
|
||||
private size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments)
|
||||
{
|
||||
size_t delta = replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false);
|
||||
size_t delta;
|
||||
replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false, delta);
|
||||
delta += endTable(buf, iEnd + delta, columnAlignments);
|
||||
return delta;
|
||||
}
|
||||
|
@ -4263,8 +4265,8 @@ private void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, s
|
|||
i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments);
|
||||
else if (columnAlignments.length)
|
||||
{
|
||||
const delta = replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false);
|
||||
if (delta)
|
||||
size_t delta;
|
||||
if (replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false, delta))
|
||||
i += delta;
|
||||
else
|
||||
i += endTable(buf, i, columnAlignments);
|
||||
|
|
|
@ -33,7 +33,7 @@ import dmd.func;
|
|||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.speller;
|
||||
import dmd.statement;
|
||||
|
@ -730,12 +730,21 @@ struct Scope
|
|||
}
|
||||
}
|
||||
|
||||
/******************************
|
||||
*/
|
||||
structalign_t alignment()
|
||||
{
|
||||
if (aligndecl)
|
||||
return aligndecl.getAlignment(&this);
|
||||
{
|
||||
auto ad = aligndecl.getAlignment(&this);
|
||||
return ad.salign;
|
||||
}
|
||||
else
|
||||
return STRUCTALIGN_DEFAULT;
|
||||
{
|
||||
structalign_t sa;
|
||||
sa.setDefault();
|
||||
return sa;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************
|
||||
|
|
|
@ -16,6 +16,7 @@ module dmd.dstruct;
|
|||
import dmd.aggregate;
|
||||
import dmd.arraytypes;
|
||||
import dmd.astenums;
|
||||
import dmd.attrib;
|
||||
import dmd.declaration;
|
||||
import dmd.dmodule;
|
||||
import dmd.dscope;
|
||||
|
@ -127,7 +128,7 @@ extern (C++) void semanticTypeInfo(Scope* sc, Type t)
|
|||
*/
|
||||
if (!sd.members)
|
||||
return; // opaque struct
|
||||
if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.dtor && !sd.xhash && !search_toString(sd))
|
||||
if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd))
|
||||
return; // none of TypeInfo-specific members
|
||||
|
||||
// If the struct is in a non-root module, run semantic3 to get
|
||||
|
@ -232,7 +233,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
|
|||
}
|
||||
}
|
||||
|
||||
static StructDeclaration create(Loc loc, Identifier id, bool inObject)
|
||||
static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject)
|
||||
{
|
||||
return new StructDeclaration(loc, id, inObject);
|
||||
}
|
||||
|
@ -297,22 +298,46 @@ extern (C++) class StructDeclaration : AggregateDeclaration
|
|||
return;
|
||||
}
|
||||
|
||||
// 0 sized struct's are set to 1 byte
|
||||
if (structsize == 0)
|
||||
{
|
||||
hasNoFields = true;
|
||||
alignsize = 1;
|
||||
if (classKind != classKind.c) // C gets a struct size of 0
|
||||
structsize = 1;
|
||||
|
||||
// A fine mess of what size a zero sized struct should be
|
||||
final switch (classKind)
|
||||
{
|
||||
case ClassKind.d:
|
||||
case ClassKind.cpp:
|
||||
structsize = 1;
|
||||
break;
|
||||
|
||||
case ClassKind.c:
|
||||
case ClassKind.objc:
|
||||
if (target.c.bitFieldStyle == TargetC.BitFieldStyle.MS)
|
||||
{
|
||||
/* Undocumented MS behavior for:
|
||||
* struct S { int :0; };
|
||||
*/
|
||||
structsize = 4;
|
||||
}
|
||||
else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM)
|
||||
{
|
||||
structsize = 0;
|
||||
alignsize = 0;
|
||||
}
|
||||
else
|
||||
structsize = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Round struct size up to next alignsize boundary.
|
||||
// This will ensure that arrays of structs will get their internals
|
||||
// aligned properly.
|
||||
if (alignment == STRUCTALIGN_DEFAULT)
|
||||
if (alignment.isDefault() || alignment.isPack())
|
||||
structsize = (structsize + alignsize - 1) & ~(alignsize - 1);
|
||||
else
|
||||
structsize = (structsize + alignment - 1) & ~(alignment - 1);
|
||||
structsize = (structsize + alignment.get() - 1) & ~(alignment.get() - 1);
|
||||
|
||||
sizeok = Sizeok.done;
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ struct Visibility
|
|||
}
|
||||
}
|
||||
|
||||
enum PASS : int
|
||||
enum PASS : ubyte
|
||||
{
|
||||
init, // initial state
|
||||
semantic, // semantic() started
|
||||
|
@ -225,11 +225,13 @@ enum : int
|
|||
*/
|
||||
struct FieldState
|
||||
{
|
||||
uint offset; /// offset for next field
|
||||
uint offset; /// byte offset for next field
|
||||
|
||||
uint fieldOffset; /// offset for the start of the bit field
|
||||
uint fieldOffset; /// byte offset for the start of the bit field
|
||||
uint fieldSize; /// byte size of field
|
||||
uint fieldAlign; /// byte alignment of field
|
||||
uint bitOffset; /// bit offset for field
|
||||
uint fieldSize; /// size of field in bytes
|
||||
|
||||
bool inFlight; /// bit field is in flight
|
||||
}
|
||||
|
||||
|
@ -793,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();
|
||||
}
|
||||
|
|
|
@ -108,7 +108,21 @@ struct Visibility
|
|||
|
||||
/* State of symbol in winding its way through the passes of the compiler
|
||||
*/
|
||||
enum PASS
|
||||
enum class PASS : uint8_t
|
||||
{
|
||||
init, // initial state
|
||||
semantic, // semantic() started
|
||||
semanticdone, // semantic() done
|
||||
semantic2, // semantic2() started
|
||||
semantic2done, // semantic2() done
|
||||
semantic3, // semantic3() started
|
||||
semantic3done, // semantic3() done
|
||||
inline_, // inline started
|
||||
inlinedone, // inline done
|
||||
obj // toObjFile() run
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PASSinit, // initial state
|
||||
PASSsemantic, // semantic() started
|
||||
|
@ -145,8 +159,10 @@ struct FieldState
|
|||
unsigned offset;
|
||||
|
||||
unsigned fieldOffset;
|
||||
unsigned fieldSize;
|
||||
unsigned fieldAlign;
|
||||
unsigned bitOffset;
|
||||
unsigned fieldSice;
|
||||
|
||||
bool inFlight;
|
||||
};
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ import dmd.objc;
|
|||
import dmd.opover;
|
||||
import dmd.parse;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.semantic2;
|
||||
|
@ -109,17 +109,18 @@ extern(C++) void dsymbolSemantic(Dsymbol dsym, Scope* sc)
|
|||
* ad = AlignmentDeclaration
|
||||
* sc = context
|
||||
* Returns:
|
||||
* alignment as numerical value that is never 0.
|
||||
* STRUCTALIGN_DEFAULT is used instead.
|
||||
* STRUCTALIGN_DEFAULT is returned for errors
|
||||
* ad with alignment value determined
|
||||
*/
|
||||
structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
|
||||
AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc)
|
||||
{
|
||||
if (ad.salign != ad.UNKNOWN) // UNKNOWN is 0
|
||||
return ad.salign;
|
||||
if (!ad.salign.isUnknown()) // UNKNOWN is 0
|
||||
return ad;
|
||||
|
||||
if (!ad.exps)
|
||||
return ad.salign = STRUCTALIGN_DEFAULT;
|
||||
{
|
||||
ad.salign.setDefault();
|
||||
return ad;
|
||||
}
|
||||
|
||||
dinteger_t strictest = 0; // strictest alignment
|
||||
bool errors;
|
||||
|
@ -140,7 +141,7 @@ structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
|
|||
if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment
|
||||
continue;
|
||||
|
||||
if (n < 1 || n & (n - 1) || structalign_t.max < n || !e.type.isintegral())
|
||||
if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isintegral())
|
||||
{
|
||||
error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
|
||||
errors = true;
|
||||
|
@ -150,10 +151,12 @@ structalign_t getAlignment(AlignDeclaration ad, Scope* sc)
|
|||
}
|
||||
}
|
||||
|
||||
ad.salign = (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
|
||||
? STRUCTALIGN_DEFAULT
|
||||
: cast(structalign_t) strictest;
|
||||
return ad.salign;
|
||||
if (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect
|
||||
ad.salign.setDefault();
|
||||
else
|
||||
ad.salign.set(cast(uint) strictest);
|
||||
|
||||
return ad;
|
||||
}
|
||||
|
||||
const(char)* getMessage(DeprecatedDeclaration dd)
|
||||
|
@ -365,16 +368,31 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
|||
|
||||
dsym.semanticRun = PASS.semantic;
|
||||
|
||||
/* Pick up storage classes from context, but except synchronized,
|
||||
* override, abstract, and final.
|
||||
*/
|
||||
dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
|
||||
// 'static foreach' variables should not inherit scope properties
|
||||
// https://issues.dlang.org/show_bug.cgi?id=19482
|
||||
if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local))
|
||||
{
|
||||
dsym.linkage = LINK.d;
|
||||
dsym.visibility = Visibility(Visibility.Kind.public_);
|
||||
dsym.overlapped = false; // unset because it is modified early on this function
|
||||
dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope()
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Pick up storage classes from context, but except synchronized,
|
||||
* override, abstract, and final.
|
||||
*/
|
||||
dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_));
|
||||
dsym.userAttribDecl = sc.userAttribDecl;
|
||||
dsym.cppnamespace = sc.namespace;
|
||||
dsym.linkage = sc.linkage;
|
||||
dsym.visibility = sc.visibility;
|
||||
dsym.alignment = sc.alignment();
|
||||
}
|
||||
|
||||
if (dsym.storage_class & STC.extern_ && dsym._init)
|
||||
dsym.error("extern symbols cannot have initializers");
|
||||
|
||||
dsym.userAttribDecl = sc.userAttribDecl;
|
||||
dsym.cppnamespace = sc.namespace;
|
||||
|
||||
AggregateDeclaration ad = dsym.isThis();
|
||||
if (ad)
|
||||
dsym.storage_class |= ad.storage_class & STC.TYPECTOR;
|
||||
|
@ -431,16 +449,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
|||
dsym.errors = true;
|
||||
|
||||
dsym.type.checkDeprecated(dsym.loc, sc);
|
||||
dsym.linkage = sc.linkage;
|
||||
dsym.parent = sc.parent;
|
||||
//printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars());
|
||||
dsym.visibility = sc.visibility;
|
||||
|
||||
/* If scope's alignment is the default, use the type's alignment,
|
||||
* otherwise the scope overrrides.
|
||||
*/
|
||||
dsym.alignment = sc.alignment();
|
||||
if (dsym.alignment == STRUCTALIGN_DEFAULT)
|
||||
if (dsym.alignment.isDefault())
|
||||
dsym.alignment = dsym.type.alignment(); // use type's alignment
|
||||
|
||||
//printf("sc.stc = %x\n", sc.stc);
|
||||
|
@ -935,6 +950,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
|||
sc = sc.push();
|
||||
sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable);
|
||||
|
||||
if (sc.flags & SCOPE.Cfile &&
|
||||
dsym.type.isTypeSArray() &&
|
||||
dsym.type.isTypeSArray().isIncomplete() &&
|
||||
dsym._init.isVoidInitializer() &&
|
||||
!(dsym.storage_class & STC.field))
|
||||
{
|
||||
dsym.error("incomplete array type must have initializer");
|
||||
}
|
||||
|
||||
ExpInitializer ei = dsym._init.isExpInitializer();
|
||||
|
||||
if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424
|
||||
|
@ -971,6 +995,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
|||
ei = new ExpInitializer(dsym._init.loc, e);
|
||||
dsym._init = ei;
|
||||
}
|
||||
else if (sc.flags & SCOPE.Cfile && dsym.type.isTypeSArray() &&
|
||||
dsym.type.isTypeSArray().isIncomplete())
|
||||
{
|
||||
// C11 6.7.9-22 determine the size of the incomplete array,
|
||||
// or issue an error that the initializer is invalid.
|
||||
dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret);
|
||||
}
|
||||
|
||||
Expression exp = ei.exp;
|
||||
Expression e1 = new VarExp(dsym.loc, dsym);
|
||||
|
@ -1056,18 +1087,29 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
|
|||
// could crash (inf. recursion) on a mod/pkg referencing itself
|
||||
if (ei && (ei.exp.op != TOK.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage()))
|
||||
{
|
||||
Expression exp = ei.exp.syntaxCopy();
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -67,7 +67,7 @@ import dmd.initsem;
|
|||
import dmd.mtype;
|
||||
import dmd.opover;
|
||||
import dmd.root.array;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.semantic2;
|
||||
import dmd.semantic3;
|
||||
|
@ -985,9 +985,9 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
|
|||
buf.writenl();
|
||||
buf.writestring(" ");
|
||||
}
|
||||
buf.write((*parameters)[i]);
|
||||
write(buf, (*parameters)[i]);
|
||||
buf.writestring(" = ");
|
||||
buf.write(tiargs[i]);
|
||||
write(buf, tiargs[i]);
|
||||
}
|
||||
// write remaining variadic arguments on the last line
|
||||
if (variadic)
|
||||
|
@ -998,16 +998,16 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
|
|||
buf.writenl();
|
||||
buf.writestring(" ");
|
||||
}
|
||||
buf.write((*parameters)[end]);
|
||||
write(buf, (*parameters)[end]);
|
||||
buf.writestring(" = ");
|
||||
buf.writeByte('(');
|
||||
if (cast(int)tiargs.length - end > 0)
|
||||
{
|
||||
buf.write(tiargs[end]);
|
||||
write(buf, tiargs[end]);
|
||||
foreach (j; parameters.length .. tiargs.length)
|
||||
{
|
||||
buf.writestring(", ");
|
||||
buf.write(tiargs[j]);
|
||||
write(buf, tiargs[j]);
|
||||
}
|
||||
}
|
||||
buf.writeByte(')');
|
||||
|
@ -1919,8 +1919,15 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol
|
|||
/* If a semantic error occurs while doing alias this,
|
||||
* eg purity(https://issues.dlang.org/show_bug.cgi?id=7295),
|
||||
* just regard it as not a match.
|
||||
*/
|
||||
if (auto e = resolveAliasThis(sc, farg, true))
|
||||
*
|
||||
* We also save/restore sc.func.flags to avoid messing up
|
||||
* attribute inference in the evaluation.
|
||||
*/
|
||||
const oldflags = sc.func ? sc.func.flags : 0;
|
||||
auto e = resolveAliasThis(sc, farg, true);
|
||||
if (sc.func)
|
||||
sc.func.flags = oldflags;
|
||||
if (e)
|
||||
{
|
||||
farg = e;
|
||||
goto Lretry;
|
||||
|
@ -5340,7 +5347,7 @@ extern (C++) class TemplateParameter : ASTNode
|
|||
|
||||
abstract RootObject specialization();
|
||||
|
||||
abstract RootObject defaultArg(Loc instLoc, Scope* sc);
|
||||
abstract RootObject defaultArg(const ref Loc instLoc, Scope* sc);
|
||||
|
||||
abstract bool hasDefaultArg();
|
||||
|
||||
|
@ -5422,7 +5429,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter
|
|||
return specType;
|
||||
}
|
||||
|
||||
override final RootObject defaultArg(Loc instLoc, Scope* sc)
|
||||
override final RootObject defaultArg(const ref Loc instLoc, Scope* sc)
|
||||
{
|
||||
Type t = defaultType;
|
||||
if (t)
|
||||
|
@ -5541,7 +5548,7 @@ extern (C++) final class TemplateValueParameter : TemplateParameter
|
|||
return specValue;
|
||||
}
|
||||
|
||||
override RootObject defaultArg(Loc instLoc, Scope* sc)
|
||||
override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
|
||||
{
|
||||
Expression e = defaultValue;
|
||||
if (e)
|
||||
|
@ -5640,7 +5647,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter
|
|||
return specAlias;
|
||||
}
|
||||
|
||||
override RootObject defaultArg(Loc instLoc, Scope* sc)
|
||||
override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
|
||||
{
|
||||
RootObject da = defaultAlias;
|
||||
Type ta = isType(defaultAlias);
|
||||
|
@ -5741,7 +5748,7 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter
|
|||
return null;
|
||||
}
|
||||
|
||||
override RootObject defaultArg(Loc instLoc, Scope* sc)
|
||||
override RootObject defaultArg(const ref Loc instLoc, Scope* sc)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -8413,3 +8420,11 @@ private struct MATCHpair
|
|||
this.mfa = mfa;
|
||||
}
|
||||
}
|
||||
|
||||
private void write(ref OutBuffer buf, RootObject obj)
|
||||
{
|
||||
if (obj)
|
||||
{
|
||||
buf.writestring(obj.toChars());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import core.stdc.ctype;
|
|||
|
||||
import dmd.astcodegen;
|
||||
import dmd.arraytypes;
|
||||
import dmd.attrib;
|
||||
import dmd.dsymbol;
|
||||
import dmd.errors;
|
||||
import dmd.globals;
|
||||
|
@ -25,7 +26,7 @@ import dmd.root.filename;
|
|||
import dmd.visitor;
|
||||
import dmd.tokens;
|
||||
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.utils;
|
||||
|
||||
//debug = Debug_DtoH;
|
||||
|
@ -254,7 +255,7 @@ public:
|
|||
Identifier ident;
|
||||
|
||||
/// Original type of the currently visited declaration
|
||||
AST.Type* origType;
|
||||
AST.Type origType;
|
||||
|
||||
/// Last written visibility level applying to the current scope
|
||||
AST.Visibility.Kind currentVisibility;
|
||||
|
@ -706,6 +707,10 @@ public:
|
|||
// printf("FuncDeclaration %s %s\n", fd.toPrettyChars(), fd.type.toChars());
|
||||
visited[cast(void*)fd] = true;
|
||||
|
||||
// silently ignore non-user-defined destructors
|
||||
if (fd.generated && fd.isDtorDeclaration())
|
||||
return;
|
||||
|
||||
// Note that tf might be null for templated (member) functions
|
||||
auto tf = cast(AST.TypeFunction)fd.type;
|
||||
if ((tf && tf.linkage != LINK.c && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration()))
|
||||
|
@ -841,12 +846,12 @@ public:
|
|||
return;
|
||||
|
||||
if (vd.originalType && vd.type == AST.Type.tsize_t)
|
||||
origType = &vd.originalType;
|
||||
origType = vd.originalType;
|
||||
scope(exit) origType = null;
|
||||
|
||||
if (vd.alignment != STRUCTALIGN_DEFAULT)
|
||||
if (!vd.alignment.isDefault())
|
||||
{
|
||||
buf.printf("// Ignoring var %s alignment %u", vd.toChars(), vd.alignment);
|
||||
buf.printf("// Ignoring var %s alignment %d", vd.toChars(), vd.alignment.get());
|
||||
buf.writenl();
|
||||
}
|
||||
|
||||
|
@ -1008,12 +1013,12 @@ public:
|
|||
if (ad.originalType && ad.type.ty == AST.Tpointer &&
|
||||
(cast(AST.TypePointer)t).nextOf.ty == AST.Tfunction)
|
||||
{
|
||||
origType = &ad.originalType;
|
||||
origType = ad.originalType;
|
||||
}
|
||||
scope(exit) origType = null;
|
||||
|
||||
buf.writestring("typedef ");
|
||||
typeToBuffer(origType ? *origType : t, ad);
|
||||
typeToBuffer(origType !is null ? origType : t, ad);
|
||||
writeDeclEnd();
|
||||
return;
|
||||
}
|
||||
|
@ -1346,7 +1351,7 @@ public:
|
|||
|
||||
/// Starts a custom alignment section using `#pragma pack` if
|
||||
/// `alignment` specifies a custom alignment
|
||||
private void pushAlignToBuffer(uint alignment)
|
||||
private void pushAlignToBuffer(structalign_t alignment)
|
||||
{
|
||||
// DMD ensures alignment is a power of two
|
||||
//assert(alignment > 0 && ((alignment & (alignment - 1)) == 0),
|
||||
|
@ -1354,20 +1359,20 @@ public:
|
|||
|
||||
// When no alignment is specified, `uint.max` is the default
|
||||
// FIXME: alignment is 0 for structs templated members
|
||||
if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
|
||||
if (alignment.isDefault() || (tdparent && alignment.isUnknown()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
buf.printf("#pragma pack(push, %d)", alignment);
|
||||
buf.printf("#pragma pack(push, %d)", alignment.get());
|
||||
buf.writenl();
|
||||
}
|
||||
|
||||
/// Ends a custom alignment section using `#pragma pack` if
|
||||
/// `alignment` specifies a custom alignment
|
||||
private void popAlignToBuffer(uint alignment)
|
||||
private void popAlignToBuffer(structalign_t alignment)
|
||||
{
|
||||
if (alignment == STRUCTALIGN_DEFAULT || (tdparent && alignment == 0))
|
||||
if (alignment.isDefault() || (tdparent && alignment.isUnknown()))
|
||||
return;
|
||||
|
||||
buf.writestringln("#pragma pack(pop)");
|
||||
|
@ -1645,7 +1650,7 @@ public:
|
|||
}
|
||||
|
||||
this.ident = s.ident;
|
||||
auto type = origType ? *origType : t;
|
||||
auto type = origType !is null ? origType : t;
|
||||
AST.Dsymbol customLength;
|
||||
|
||||
// Check for quirks that are usually resolved during semantic
|
||||
|
|
|
@ -22,7 +22,7 @@ import dmd.dsymbol;
|
|||
import dmd.dsymbolsem;
|
||||
import dmd.globals;
|
||||
import dmd.identifier;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.visitor;
|
||||
|
||||
/***********************************************************
|
||||
|
|
|
@ -59,7 +59,7 @@ import dmd.opover;
|
|||
import dmd.optimize;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
|
@ -1201,20 +1201,14 @@ extern (C++) abstract class Expression : ASTNode
|
|||
// DtorDeclaration without parents should fail at an earlier stage
|
||||
auto ad = cast(AggregateDeclaration) f.toParent2();
|
||||
assert(ad);
|
||||
assert(ad.dtors.length);
|
||||
|
||||
// Search for the user-defined destructor (if any)
|
||||
foreach(dtor; ad.dtors)
|
||||
if (ad.userDtors.dim)
|
||||
{
|
||||
if (dtor.generated)
|
||||
continue;
|
||||
|
||||
if (!check(dtor)) // doesn't match check (e.g. is impure as well)
|
||||
if (!check(ad.userDtors[0])) // doesn't match check (e.g. is impure as well)
|
||||
return;
|
||||
|
||||
// Sanity check
|
||||
assert(!check(cast(DtorDeclaration) ad.fieldDtor));
|
||||
break;
|
||||
assert(!check(ad.fieldDtor));
|
||||
}
|
||||
|
||||
dd.loc.errorSupplemental("%s`%s.~this` is %.*s because of the following field's destructors:",
|
||||
|
@ -1781,13 +1775,13 @@ extern (C++) final class IntegerExp : Expression
|
|||
this.value = cast(d_int32)value;
|
||||
}
|
||||
|
||||
static IntegerExp create(Loc loc, dinteger_t value, Type type)
|
||||
static IntegerExp create(const ref Loc loc, dinteger_t value, Type type)
|
||||
{
|
||||
return new IntegerExp(loc, value, type);
|
||||
}
|
||||
|
||||
// Same as create, but doesn't allocate memory.
|
||||
static void emplace(UnionExp* pue, Loc loc, dinteger_t value, Type type)
|
||||
static void emplace(UnionExp* pue, const ref Loc loc, dinteger_t value, Type type)
|
||||
{
|
||||
emplaceExp!(IntegerExp)(pue, loc, value, type);
|
||||
}
|
||||
|
@ -2052,13 +2046,13 @@ extern (C++) final class RealExp : Expression
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
static RealExp create(Loc loc, real_t value, Type type)
|
||||
static RealExp create(const ref Loc loc, real_t value, Type type)
|
||||
{
|
||||
return new RealExp(loc, value, type);
|
||||
}
|
||||
|
||||
// Same as create, but doesn't allocate memory.
|
||||
static void emplace(UnionExp* pue, Loc loc, real_t value, Type type)
|
||||
static void emplace(UnionExp* pue, const ref Loc loc, real_t value, Type type)
|
||||
{
|
||||
emplaceExp!(RealExp)(pue, loc, value, type);
|
||||
}
|
||||
|
@ -2127,13 +2121,13 @@ extern (C++) final class ComplexExp : Expression
|
|||
//printf("ComplexExp::ComplexExp(%s)\n", toChars());
|
||||
}
|
||||
|
||||
static ComplexExp create(Loc loc, complex_t value, Type type)
|
||||
static ComplexExp create(const ref Loc loc, complex_t value, Type type)
|
||||
{
|
||||
return new ComplexExp(loc, value, type);
|
||||
}
|
||||
|
||||
// Same as create, but doesn't allocate memory.
|
||||
static void emplace(UnionExp* pue, Loc loc, complex_t value, Type type)
|
||||
static void emplace(UnionExp* pue, const ref Loc loc, complex_t value, Type type)
|
||||
{
|
||||
emplaceExp!(ComplexExp)(pue, loc, value, type);
|
||||
}
|
||||
|
@ -2203,7 +2197,7 @@ extern (C++) class IdentifierExp : Expression
|
|||
this.ident = ident;
|
||||
}
|
||||
|
||||
static IdentifierExp create(Loc loc, Identifier ident)
|
||||
static IdentifierExp create(const ref Loc loc, Identifier ident)
|
||||
{
|
||||
return new IdentifierExp(loc, ident);
|
||||
}
|
||||
|
@ -2421,28 +2415,28 @@ extern (C++) final class StringExp : Expression
|
|||
this.postfix = postfix;
|
||||
}
|
||||
|
||||
static StringExp create(Loc loc, char* s)
|
||||
static StringExp create(const ref Loc loc, const(char)* s)
|
||||
{
|
||||
return new StringExp(loc, s.toDString());
|
||||
}
|
||||
|
||||
static StringExp create(Loc loc, void* string, size_t len)
|
||||
static StringExp create(const ref Loc loc, const(void)* string, size_t len)
|
||||
{
|
||||
return new StringExp(loc, string[0 .. len]);
|
||||
}
|
||||
|
||||
// Same as create, but doesn't allocate memory.
|
||||
static void emplace(UnionExp* pue, Loc loc, char* s)
|
||||
static void emplace(UnionExp* pue, const ref Loc loc, const(char)* s)
|
||||
{
|
||||
emplaceExp!(StringExp)(pue, loc, s.toDString());
|
||||
}
|
||||
|
||||
extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string)
|
||||
extern (D) static void emplace(UnionExp* pue, const ref Loc loc, const(void)[] string)
|
||||
{
|
||||
emplaceExp!(StringExp)(pue, loc, string);
|
||||
}
|
||||
|
||||
extern (D) static void emplace(UnionExp* pue, Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix)
|
||||
extern (D) static void emplace(UnionExp* pue, const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix)
|
||||
{
|
||||
emplaceExp!(StringExp)(pue, loc, string, len, sz, postfix);
|
||||
}
|
||||
|
@ -2863,7 +2857,7 @@ extern (C++) final class TupleExp : Expression
|
|||
}
|
||||
}
|
||||
|
||||
static TupleExp create(Loc loc, Expressions* exps)
|
||||
static TupleExp create(const ref Loc loc, Expressions* exps)
|
||||
{
|
||||
return new TupleExp(loc, exps);
|
||||
}
|
||||
|
@ -2946,13 +2940,13 @@ extern (C++) final class ArrayLiteralExp : Expression
|
|||
this.elements = elements;
|
||||
}
|
||||
|
||||
static ArrayLiteralExp create(Loc loc, Expressions* elements)
|
||||
static ArrayLiteralExp create(const ref Loc loc, Expressions* elements)
|
||||
{
|
||||
return new ArrayLiteralExp(loc, null, elements);
|
||||
}
|
||||
|
||||
// Same as create, but doesn't allocate memory.
|
||||
static void emplace(UnionExp* pue, Loc loc, Expressions* elements)
|
||||
static void emplace(UnionExp* pue, const ref Loc loc, Expressions* elements)
|
||||
{
|
||||
emplaceExp!(ArrayLiteralExp)(pue, loc, null, elements);
|
||||
}
|
||||
|
@ -3188,7 +3182,7 @@ extern (C++) final class StructLiteralExp : Expression
|
|||
//printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars());
|
||||
}
|
||||
|
||||
static StructLiteralExp create(Loc loc, StructDeclaration sd, void* elements, Type stype = null)
|
||||
static StructLiteralExp create(const ref Loc loc, StructDeclaration sd, void* elements, Type stype = null)
|
||||
{
|
||||
return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype);
|
||||
}
|
||||
|
@ -3532,7 +3526,7 @@ extern (C++) final class NewExp : Expression
|
|||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
static NewExp create(Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
|
||||
static NewExp create(const ref Loc loc, Expression thisexp, Expressions* newargs, Type newtype, Expressions* arguments)
|
||||
{
|
||||
return new NewExp(loc, thisexp, newargs, newtype, arguments);
|
||||
}
|
||||
|
@ -3653,7 +3647,7 @@ extern (C++) final class VarExp : SymbolExp
|
|||
this.type = var.type;
|
||||
}
|
||||
|
||||
static VarExp create(Loc loc, Declaration var, bool hasOverloads = true)
|
||||
static VarExp create(const ref Loc loc, Declaration var, bool hasOverloads = true)
|
||||
{
|
||||
return new VarExp(loc, var, hasOverloads);
|
||||
}
|
||||
|
@ -3965,6 +3959,7 @@ extern (C++) final class FuncExp : Expression
|
|||
auto tfy = new TypeFunction(tfx.parameterList, tof.next,
|
||||
tfx.linkage, STC.undefined_);
|
||||
tfy.mod = tfx.mod;
|
||||
tfy.trust = tfx.trust;
|
||||
tfy.isnothrow = tfx.isnothrow;
|
||||
tfy.isnogc = tfx.isnogc;
|
||||
tfy.purity = tfx.purity;
|
||||
|
@ -4688,6 +4683,7 @@ extern (C++) final class DotIdExp : UnaExp
|
|||
Identifier ident;
|
||||
bool noderef; // true if the result of the expression will never be dereferenced
|
||||
bool wantsym; // do not replace Symbol with its initializer during semantic()
|
||||
bool arrow; // ImportC: if -> instead of .
|
||||
|
||||
extern (D) this(const ref Loc loc, Expression e, Identifier ident)
|
||||
{
|
||||
|
@ -4695,7 +4691,7 @@ extern (C++) final class DotIdExp : UnaExp
|
|||
this.ident = ident;
|
||||
}
|
||||
|
||||
static DotIdExp create(Loc loc, Expression e, Identifier ident)
|
||||
static DotIdExp create(const ref Loc loc, Expression e, Identifier ident)
|
||||
{
|
||||
return new DotIdExp(loc, e, ident);
|
||||
}
|
||||
|
@ -4889,6 +4885,31 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp
|
|||
return ti.updateTempDecl(sc, s);
|
||||
}
|
||||
|
||||
override bool checkType()
|
||||
{
|
||||
// Same logic as ScopeExp.checkType()
|
||||
if (ti.tempdecl &&
|
||||
ti.semantictiargsdone &&
|
||||
ti.semanticRun == PASS.init)
|
||||
{
|
||||
error("partial %s `%s` has no type", ti.kind(), toChars());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
override bool checkValue()
|
||||
{
|
||||
if (ti.tempdecl &&
|
||||
ti.semantictiargsdone &&
|
||||
ti.semanticRun == PASS.init)
|
||||
|
||||
error("partial %s `%s` has no value", ti.kind(), toChars());
|
||||
else
|
||||
error("%s `%s` has no value", ti.kind(), ti.toChars());
|
||||
return true;
|
||||
}
|
||||
|
||||
override void accept(Visitor v)
|
||||
{
|
||||
v.visit(this);
|
||||
|
@ -4987,17 +5008,17 @@ extern (C++) final class CallExp : UnaExp
|
|||
this.f = fd;
|
||||
}
|
||||
|
||||
static CallExp create(Loc loc, Expression e, Expressions* exps)
|
||||
static CallExp create(const ref Loc loc, Expression e, Expressions* exps)
|
||||
{
|
||||
return new CallExp(loc, e, exps);
|
||||
}
|
||||
|
||||
static CallExp create(Loc loc, Expression e)
|
||||
static CallExp create(const ref Loc loc, Expression e)
|
||||
{
|
||||
return new CallExp(loc, e);
|
||||
}
|
||||
|
||||
static CallExp create(Loc loc, Expression e, Expression earg1)
|
||||
static CallExp create(const ref Loc loc, Expression e, Expression earg1)
|
||||
{
|
||||
return new CallExp(loc, e, earg1);
|
||||
}
|
||||
|
@ -5009,7 +5030,7 @@ extern (C++) final class CallExp : UnaExp
|
|||
* fd = the declaration of the function to call
|
||||
* earg1 = the function argument
|
||||
*/
|
||||
static CallExp create(Loc loc, FuncDeclaration fd, Expression earg1)
|
||||
static CallExp create(const ref Loc loc, FuncDeclaration fd, Expression earg1)
|
||||
{
|
||||
return new CallExp(loc, fd, earg1);
|
||||
}
|
||||
|
@ -5167,6 +5188,19 @@ extern (C++) final class PtrExp : UnaExp
|
|||
override Expression modifiableLvalue(Scope* sc, Expression e)
|
||||
{
|
||||
//printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type.toChars());
|
||||
Declaration var;
|
||||
if (auto se = e1.isSymOffExp())
|
||||
var = se.var;
|
||||
else if (auto ve = e1.isVarExp())
|
||||
var = ve.var;
|
||||
if (var && var.type.isFunction_Delegate_PtrToFunction())
|
||||
{
|
||||
if (var.type.isTypeFunction())
|
||||
error("function `%s` is not an lvalue and cannot be modified", var.toChars());
|
||||
else
|
||||
error("function pointed to by `%s` is not an lvalue and cannot be modified", var.toChars());
|
||||
return ErrorExp.get();
|
||||
}
|
||||
return Expression.modifiableLvalue(sc, e);
|
||||
}
|
||||
|
||||
|
@ -5331,13 +5365,13 @@ extern (C++) final class VectorExp : UnaExp
|
|||
to = cast(TypeVector)t;
|
||||
}
|
||||
|
||||
static VectorExp create(Loc loc, Expression e, Type t)
|
||||
static VectorExp create(const ref Loc loc, Expression e, Type t)
|
||||
{
|
||||
return new VectorExp(loc, e, t);
|
||||
}
|
||||
|
||||
// Same as create, but doesn't allocate memory.
|
||||
static void emplace(UnionExp* pue, Loc loc, Expression e, Type type)
|
||||
static void emplace(UnionExp* pue, const ref Loc loc, Expression e, Type type)
|
||||
{
|
||||
emplaceExp!(VectorExp)(pue, loc, e, type);
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ enum
|
|||
OWNEDcache // constant value cached for CTFE
|
||||
};
|
||||
|
||||
#define WANTvalue 0 // default
|
||||
#define WANTexpand 1 // expand const/immutable variables if possible
|
||||
|
||||
/**
|
||||
* Specifies how the checkModify deals with certain situations
|
||||
*/
|
||||
|
@ -239,8 +242,8 @@ class IntegerExp : public Expression
|
|||
public:
|
||||
dinteger_t value;
|
||||
|
||||
static IntegerExp *create(Loc loc, dinteger_t value, Type *type);
|
||||
static void emplace(UnionExp *pue, Loc loc, dinteger_t value, Type *type);
|
||||
static IntegerExp *create(const Loc &loc, dinteger_t value, Type *type);
|
||||
static void emplace(UnionExp *pue, const Loc &loc, dinteger_t value, Type *type);
|
||||
bool equals(const RootObject *o) const;
|
||||
dinteger_t toInteger();
|
||||
real_t toReal();
|
||||
|
@ -269,8 +272,8 @@ class RealExp : public Expression
|
|||
public:
|
||||
real_t value;
|
||||
|
||||
static RealExp *create(Loc loc, real_t value, Type *type);
|
||||
static void emplace(UnionExp *pue, Loc loc, real_t value, Type *type);
|
||||
static RealExp *create(const Loc &loc, real_t value, Type *type);
|
||||
static void emplace(UnionExp *pue, const Loc &loc, real_t value, Type *type);
|
||||
bool equals(const RootObject *o) const;
|
||||
dinteger_t toInteger();
|
||||
uinteger_t toUInteger();
|
||||
|
@ -286,8 +289,8 @@ class ComplexExp : public Expression
|
|||
public:
|
||||
complex_t value;
|
||||
|
||||
static ComplexExp *create(Loc loc, complex_t value, Type *type);
|
||||
static void emplace(UnionExp *pue, Loc loc, complex_t value, Type *type);
|
||||
static ComplexExp *create(const Loc &loc, complex_t value, Type *type);
|
||||
static void emplace(UnionExp *pue, const Loc &loc, complex_t value, Type *type);
|
||||
bool equals(const RootObject *o) const;
|
||||
dinteger_t toInteger();
|
||||
uinteger_t toUInteger();
|
||||
|
@ -303,7 +306,7 @@ class IdentifierExp : public Expression
|
|||
public:
|
||||
Identifier *ident;
|
||||
|
||||
static IdentifierExp *create(Loc loc, Identifier *ident);
|
||||
static IdentifierExp *create(const Loc &loc, Identifier *ident);
|
||||
bool isLvalue();
|
||||
Expression *toLvalue(Scope *sc, Expression *e);
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
@ -365,10 +368,9 @@ public:
|
|||
utf8_t postfix; // 'c', 'w', 'd'
|
||||
OwnedBy ownedByCtfe;
|
||||
|
||||
static StringExp *create(Loc loc, char *s);
|
||||
static StringExp *create(Loc loc, void *s, size_t len);
|
||||
static void emplace(UnionExp *pue, Loc loc, char *s);
|
||||
static void emplace(UnionExp *pue, Loc loc, void *s, size_t len);
|
||||
static StringExp *create(const Loc &loc, const char *s);
|
||||
static StringExp *create(const Loc &loc, const void *s, size_t len);
|
||||
static void emplace(UnionExp *pue, const Loc &loc, const char *s);
|
||||
bool equals(const RootObject *o) const;
|
||||
StringExp *toStringExp();
|
||||
StringExp *toUTF8(Scope *sc);
|
||||
|
@ -397,7 +399,7 @@ public:
|
|||
*/
|
||||
Expressions *exps;
|
||||
|
||||
static TupleExp *create(Loc loc, Expressions *exps);
|
||||
static TupleExp *create(const Loc &loc, Expressions *exps);
|
||||
TupleExp *toTupleExp();
|
||||
TupleExp *syntaxCopy();
|
||||
bool equals(const RootObject *o) const;
|
||||
|
@ -412,8 +414,8 @@ public:
|
|||
Expressions *elements;
|
||||
OwnedBy ownedByCtfe;
|
||||
|
||||
static ArrayLiteralExp *create(Loc loc, Expressions *elements);
|
||||
static void emplace(UnionExp *pue, Loc loc, Expressions *elements);
|
||||
static ArrayLiteralExp *create(const Loc &loc, Expressions *elements);
|
||||
static void emplace(UnionExp *pue, const Loc &loc, Expressions *elements);
|
||||
ArrayLiteralExp *syntaxCopy();
|
||||
bool equals(const RootObject *o) const;
|
||||
Expression *getElement(d_size_t i); // use opIndex instead
|
||||
|
@ -468,7 +470,7 @@ public:
|
|||
bool isOriginal; // used when moving instances to indicate `this is this.origin`
|
||||
OwnedBy ownedByCtfe;
|
||||
|
||||
static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
|
||||
static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL);
|
||||
bool equals(const RootObject *o) const;
|
||||
StructLiteralExp *syntaxCopy();
|
||||
Expression *getField(Type *type, unsigned offset);
|
||||
|
@ -528,7 +530,7 @@ public:
|
|||
bool onstack; // allocate on stack
|
||||
bool thrownew; // this NewExp is the expression of a ThrowStatement
|
||||
|
||||
static NewExp *create(Loc loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments);
|
||||
static NewExp *create(const Loc &loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments);
|
||||
NewExp *syntaxCopy();
|
||||
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
@ -576,7 +578,7 @@ class VarExp : public SymbolExp
|
|||
{
|
||||
public:
|
||||
bool delegateWasExtracted;
|
||||
static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true);
|
||||
static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true);
|
||||
bool equals(const RootObject *o) const;
|
||||
bool isLvalue();
|
||||
Expression *toLvalue(Scope *sc, Expression *e);
|
||||
|
@ -746,8 +748,9 @@ public:
|
|||
Identifier *ident;
|
||||
bool noderef; // true if the result of the expression will never be dereferenced
|
||||
bool wantsym; // do not replace Symbol with its initializer during semantic()
|
||||
bool arrow; // ImportC: if -> instead of .
|
||||
|
||||
static DotIdExp *create(Loc loc, Expression *e, Identifier *ident);
|
||||
static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident);
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
};
|
||||
|
||||
|
@ -780,6 +783,8 @@ public:
|
|||
|
||||
DotTemplateInstanceExp *syntaxCopy();
|
||||
bool findTempDecl(Scope *sc);
|
||||
bool checkType();
|
||||
bool checkValue();
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
};
|
||||
|
||||
|
@ -812,10 +817,10 @@ public:
|
|||
bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code)
|
||||
VarDeclaration *vthis2; // container for multi-context
|
||||
|
||||
static CallExp *create(Loc loc, Expression *e, Expressions *exps);
|
||||
static CallExp *create(Loc loc, Expression *e);
|
||||
static CallExp *create(Loc loc, Expression *e, Expression *earg1);
|
||||
static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1);
|
||||
static CallExp *create(const Loc &loc, Expression *e, Expressions *exps);
|
||||
static CallExp *create(const Loc &loc, Expression *e);
|
||||
static CallExp *create(const Loc &loc, Expression *e, Expression *earg1);
|
||||
static CallExp *create(const Loc &loc, FuncDeclaration *fd, Expression *earg1);
|
||||
|
||||
CallExp *syntaxCopy();
|
||||
bool isLvalue();
|
||||
|
@ -893,8 +898,8 @@ public:
|
|||
unsigned dim; // number of elements in the vector
|
||||
OwnedBy ownedByCtfe;
|
||||
|
||||
static VectorExp *create(Loc loc, Expression *e, Type *t);
|
||||
static void emplace(UnionExp *pue, Loc loc, Expression *e, Type *t);
|
||||
static VectorExp *create(const Loc &loc, Expression *e, Type *t);
|
||||
static void emplace(UnionExp *pue, const Loc &loc, Expression *e, Type *t);
|
||||
VectorExp *syntaxCopy();
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
};
|
||||
|
@ -1272,7 +1277,7 @@ public:
|
|||
|
||||
class GenericExp : Expression
|
||||
{
|
||||
Expression cntlExp;
|
||||
Expression *cntlExp;
|
||||
Types *types;
|
||||
Expressions *exps;
|
||||
|
||||
|
|
|
@ -43,12 +43,14 @@ import dmd.dtemplate;
|
|||
import dmd.errors;
|
||||
import dmd.escape;
|
||||
import dmd.expression;
|
||||
import dmd.file_manager;
|
||||
import dmd.func;
|
||||
import dmd.globals;
|
||||
import dmd.hdrgen;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.imphint;
|
||||
import dmd.importc;
|
||||
import dmd.init;
|
||||
import dmd.initsem;
|
||||
import dmd.inline;
|
||||
|
@ -62,7 +64,7 @@ import dmd.printast;
|
|||
import dmd.root.ctfloat;
|
||||
import dmd.root.file;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.semantic2;
|
||||
|
@ -1239,6 +1241,10 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 =
|
|||
tthis = dve.e1.type;
|
||||
goto Lfd;
|
||||
}
|
||||
else if (sc && sc.flags & SCOPE.Cfile && e1.op == TOK.variable && !e2)
|
||||
{
|
||||
// ImportC: do not implicitly call function if no ( ) are present
|
||||
}
|
||||
else if (e1.op == TOK.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration()))
|
||||
{
|
||||
s = (cast(VarExp)e1).var;
|
||||
|
@ -1388,7 +1394,6 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
|
|||
|
||||
Type t0 = null;
|
||||
Expression e0 = null;
|
||||
size_t j0 = ~0;
|
||||
bool foundType;
|
||||
|
||||
for (size_t i = 0; i < exps.dim; i++)
|
||||
|
@ -1441,17 +1446,16 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
|
|||
{
|
||||
// https://issues.dlang.org/show_bug.cgi?id=21285
|
||||
// Functions and delegates don't convert correctly with castTo below
|
||||
exps[j0] = condexp.e1;
|
||||
exps[i] = condexp.e1;
|
||||
e = condexp.e2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert to common type
|
||||
exps[j0] = condexp.e1.castTo(sc, condexp.type);
|
||||
exps[i] = condexp.e1.castTo(sc, condexp.type);
|
||||
e = condexp.e2.castTo(sc, condexp.type);
|
||||
}
|
||||
}
|
||||
j0 = i;
|
||||
e0 = e;
|
||||
t0 = e.type;
|
||||
if (e.op != TOK.error)
|
||||
|
@ -1599,6 +1603,7 @@ private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool repo
|
|||
{
|
||||
Expression arg = (*exps)[i];
|
||||
arg = resolveProperties(sc, arg);
|
||||
arg = arg.arrayFuncConv(sc);
|
||||
if (arg.op == TOK.type)
|
||||
{
|
||||
// for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684
|
||||
|
@ -4297,7 +4302,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
* expr.foo!(tiargs)(funcargs)
|
||||
*/
|
||||
Ldotti:
|
||||
if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
|
||||
if (exp.e1.op == TOK.dotTemplateInstance)
|
||||
{
|
||||
DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)exp.e1;
|
||||
TemplateInstance ti = se.ti;
|
||||
|
@ -4352,7 +4357,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
* We handle such earlier, so go back.
|
||||
* Note that in the rewrite, we carefully did not run semantic() on e1
|
||||
*/
|
||||
if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type)
|
||||
if (exp.e1.op == TOK.dotTemplateInstance)
|
||||
{
|
||||
goto Ldotti;
|
||||
}
|
||||
|
@ -4419,6 +4424,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
}
|
||||
else if (exp.e1.op == TOK.type && (sc && sc.flags & SCOPE.Cfile))
|
||||
{
|
||||
const numArgs = exp.arguments ? exp.arguments.length : 0;
|
||||
if (e1org.parens && numArgs >= 1)
|
||||
{
|
||||
/* Ambiguous cases arise from CParser where there is not enough
|
||||
* information to determine if we have a function call or a cast.
|
||||
* ( type-name ) ( identifier ) ;
|
||||
* ( identifier ) ( identifier ) ;
|
||||
* If exp.e1 is a type-name, then this is a cast.
|
||||
*/
|
||||
Expression arg;
|
||||
foreach (a; (*exp.arguments)[])
|
||||
{
|
||||
arg = arg ? new CommaExp(a.loc, arg, a) : a;
|
||||
}
|
||||
auto t = exp.e1.isTypeExp().type;
|
||||
auto e = new CastExp(exp.loc, arg, t);
|
||||
result = e.expressionSemantic(sc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ambiguous cases arise from CParser where there is not enough
|
||||
* information to determine if we have a function call or declaration.
|
||||
* type-name ( identifier ) ;
|
||||
|
@ -4426,7 +4451,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
* If exp.e1 is a type-name, then this is a declaration. C11 does not
|
||||
* have type construction syntax, so don't convert this to a cast().
|
||||
*/
|
||||
if (exp.arguments && exp.arguments.dim == 1)
|
||||
if (numArgs == 1)
|
||||
{
|
||||
Expression arg = (*exp.arguments)[0];
|
||||
if (auto ie = (*exp.arguments)[0].isIdentifierExp())
|
||||
|
@ -4638,15 +4663,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
{
|
||||
UnaExp ue = cast(UnaExp)exp.e1;
|
||||
|
||||
Expression ue1 = ue.e1;
|
||||
Expression ue1old = ue1; // need for 'right this' check
|
||||
VarDeclaration v;
|
||||
if (ue1.op == TOK.variable && (v = (cast(VarExp)ue1).var.isVarDeclaration()) !is null && v.needThis())
|
||||
{
|
||||
ue.e1 = new TypeExp(ue1.loc, ue1.type);
|
||||
ue1 = null;
|
||||
}
|
||||
|
||||
Expression ue1old = ue.e1; // need for 'right this' check
|
||||
DotVarExp dve;
|
||||
DotTemplateExp dte;
|
||||
Dsymbol s;
|
||||
|
@ -4665,7 +4682,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
}
|
||||
|
||||
// Do overload resolution
|
||||
exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue1 ? ue1.type : null, exp.arguments, FuncResolveFlag.standard);
|
||||
exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue.e1.type, exp.arguments, FuncResolveFlag.standard);
|
||||
if (!exp.f || exp.f.errors || exp.f.type.ty == Terror)
|
||||
return setError();
|
||||
|
||||
|
@ -4677,7 +4694,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
auto ad2 = b.sym;
|
||||
ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod));
|
||||
ue.e1 = ue.e1.expressionSemantic(sc);
|
||||
ue1 = ue.e1;
|
||||
auto vi = exp.f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.dim);
|
||||
assert(vi >= 0);
|
||||
exp.f = ad2.vtbl[vi].isFuncDeclaration();
|
||||
|
@ -6036,17 +6052,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
}
|
||||
|
||||
{
|
||||
auto readResult = File.read(name);
|
||||
if (!readResult.success)
|
||||
auto fileName = FileName(name.toDString);
|
||||
if (auto fmResult = FileManager.fileManager.lookup(fileName))
|
||||
{
|
||||
e.error("cannot read file `%s`", name);
|
||||
return setError();
|
||||
se = new StringExp(e.loc, fmResult.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// take ownership of buffer (probably leaking)
|
||||
auto data = readResult.extractSlice();
|
||||
se = new StringExp(e.loc, data);
|
||||
auto readResult = File.read(name);
|
||||
if (!readResult.success)
|
||||
{
|
||||
e.error("cannot read file `%s`", name);
|
||||
return setError();
|
||||
}
|
||||
else
|
||||
{
|
||||
// take ownership of buffer (probably leaking)
|
||||
auto data = readResult.extractSlice();
|
||||
se = new StringExp(e.loc, data);
|
||||
|
||||
FileBuffer* fileBuffer = FileBuffer.create();
|
||||
fileBuffer.data = data;
|
||||
FileManager.fileManager.add(fileName, fileBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
result = se.expressionSemantic(sc);
|
||||
|
@ -6357,7 +6385,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
result = e;
|
||||
return;
|
||||
}
|
||||
exp.type = Type.tnoreturn;
|
||||
|
||||
// Only override the type when it isn't already some flavour of noreturn,
|
||||
// e.g. when this assert was generated by defaultInitLiteral
|
||||
if (!exp.type || !exp.type.isTypeNoreturn())
|
||||
exp.type = Type.tnoreturn;
|
||||
}
|
||||
else
|
||||
exp.type = Type.tvoid;
|
||||
|
@ -6374,12 +6406,53 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars());
|
||||
//printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op));
|
||||
}
|
||||
if (exp.arrow) // ImportC only
|
||||
exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
|
||||
|
||||
if (sc.flags & SCOPE.Cfile)
|
||||
{
|
||||
if (exp.ident == Id.__xalignof && exp.e1.isTypeExp())
|
||||
{
|
||||
// C11 6.5.3 says _Alignof only applies to types
|
||||
Expression e;
|
||||
Type t;
|
||||
Dsymbol s;
|
||||
dmd.typesem.resolve(exp.e1.type, exp.e1.loc, sc, e, t, s, true);
|
||||
if (e)
|
||||
{
|
||||
exp.e1.error("argument to `_Alignof` must be a type");
|
||||
return setError();
|
||||
}
|
||||
else if (t)
|
||||
{
|
||||
result = new IntegerExp(exp.loc, t.alignsize, Type.tsize_t);
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
exp.e1.error("argument to `_Alignof` must be a type");
|
||||
return setError();
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sc.flags & SCOPE.Cfile && exp.ident != Id.__sizeof)
|
||||
{
|
||||
result = fieldLookup(exp.e1, sc, exp.ident);
|
||||
return;
|
||||
}
|
||||
|
||||
Expression e = exp.semanticY(sc, 1);
|
||||
|
||||
if (e && isDotOpDispatch(e))
|
||||
{
|
||||
auto ode = e;
|
||||
uint errors = global.startGagging();
|
||||
e = resolvePropertiesX(sc, e);
|
||||
if (global.endGagging(errors))
|
||||
// Any error or if 'e' is not resolved, go to UFCS
|
||||
if (global.endGagging(errors) || e is ode)
|
||||
e = null; /* fall down to UFCS */
|
||||
else
|
||||
{
|
||||
|
@ -6580,10 +6653,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
{
|
||||
printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars());
|
||||
}
|
||||
if (exp.type)
|
||||
{
|
||||
result = exp;
|
||||
return;
|
||||
}
|
||||
// Indicate we need to resolve by UFCS.
|
||||
Expression e = exp.semanticY(sc, 1);
|
||||
if (!e)
|
||||
e = resolveUFCSProperties(sc, exp);
|
||||
if (e is exp)
|
||||
e.type = Type.tvoid; // Unresolved type, because it needs inference
|
||||
result = e;
|
||||
}
|
||||
|
||||
|
@ -7908,6 +7988,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
dinteger_t length = el.toInteger();
|
||||
auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length));
|
||||
exp.upperIsInBounds = bounds.contains(uprRange);
|
||||
if (exp.lwr.op == TOK.int64 && exp.upr.op == TOK.int64 && exp.lwr.toInteger() > exp.upr.toInteger())
|
||||
{
|
||||
exp.error("in slice `%s[%llu .. %llu]`, lower bound is greater than upper bound", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger());
|
||||
return setError();
|
||||
}
|
||||
if (exp.upr.op == TOK.int64 && exp.upr.toInteger() > length)
|
||||
{
|
||||
exp.error("in slice `%s[%llu .. %llu]`, upper bound is greater than array length `%llu`", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger(), length);
|
||||
return setError();
|
||||
}
|
||||
}
|
||||
else if (exp.upr.op == TOK.int64 && exp.upr.toInteger() == 0)
|
||||
{
|
||||
|
@ -7965,6 +8055,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
printf("ArrayExp::semantic('%s')\n", exp.toChars());
|
||||
}
|
||||
assert(!exp.type);
|
||||
|
||||
result = exp.carraySemantic(sc); // C semantics
|
||||
if (result)
|
||||
return;
|
||||
|
||||
Expression e = exp.op_overload(sc);
|
||||
if (e)
|
||||
{
|
||||
|
@ -8019,6 +8114,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
|
||||
override void visit(CommaExp e)
|
||||
{
|
||||
//printf("Semantic.CommaExp() %s\n", e.toChars());
|
||||
if (e.type)
|
||||
{
|
||||
result = e;
|
||||
|
@ -8042,10 +8138,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
if (checkNonAssignmentArrayOp(e.e1))
|
||||
return setError();
|
||||
|
||||
// Comma expressions trigger this conversion
|
||||
e.e2 = e.e2.arrayFuncConv(sc);
|
||||
|
||||
e.type = e.e2.type;
|
||||
if (e.type is Type.tvoid)
|
||||
discardValue(e.e1);
|
||||
else if (!e.allowCommaExp && !e.isGenerated)
|
||||
else if (!e.allowCommaExp && !e.isGenerated && !(sc.flags & SCOPE.Cfile))
|
||||
e.error("Using the result of a comma expression is not allowed");
|
||||
result = e;
|
||||
}
|
||||
|
@ -8143,7 +8242,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
|
||||
// operator overloading should be handled in ArrayExp already.
|
||||
if (!exp.e1.type)
|
||||
exp.e1 = exp.e1.expressionSemantic(sc);
|
||||
exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
|
||||
assert(exp.e1.type); // semantic() should already be run on it
|
||||
if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple)
|
||||
{
|
||||
|
@ -8191,7 +8290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
}
|
||||
if (t1b.ty == Ttuple)
|
||||
sc = sc.startCTFE();
|
||||
exp.e2 = exp.e2.expressionSemantic(sc);
|
||||
exp.e2 = exp.e2.expressionSemantic(sc).arrayFuncConv(sc);
|
||||
exp.e2 = resolveProperties(sc, exp.e2);
|
||||
if (t1b.ty == Ttuple)
|
||||
sc = sc.endCTFE();
|
||||
|
@ -8515,7 +8614,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
|
||||
if (auto e2comma = exp.e2.isCommaExp())
|
||||
{
|
||||
if (!e2comma.isGenerated)
|
||||
if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile))
|
||||
exp.error("Using the result of a comma expression is not allowed");
|
||||
|
||||
/* Rewrite to get rid of the comma from rvalue
|
||||
|
@ -8659,6 +8758,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
|
||||
e1x = e;
|
||||
}
|
||||
else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp())
|
||||
{
|
||||
auto die = e1x.isDotIdExp();
|
||||
e1x = fieldLookup(die.e1, sc, die.ident);
|
||||
}
|
||||
else if (auto die = e1x.isDotIdExp())
|
||||
{
|
||||
Expression e = die.semanticY(sc, 1);
|
||||
|
@ -8672,10 +8776,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
* In order to make sure that UFCS is tried with correct parameters, e2
|
||||
* needs to have semantic ran on it.
|
||||
*/
|
||||
auto ode = e;
|
||||
exp.e2 = exp.e2.expressionSemantic(sc);
|
||||
uint errors = global.startGagging();
|
||||
e = resolvePropertiesX(sc, e, exp.e2);
|
||||
if (global.endGagging(errors))
|
||||
// Any error or if 'e' is not resolved, go to UFCS
|
||||
if (global.endGagging(errors) || e is ode)
|
||||
e = null; /* fall down to UFCS */
|
||||
else
|
||||
return setResult(e);
|
||||
|
@ -8717,12 +8823,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
{
|
||||
Expression e2x = inferType(exp.e2, t1.baseElemOf());
|
||||
e2x = e2x.expressionSemantic(sc);
|
||||
if (!t1.isTypeSArray())
|
||||
e2x = e2x.arrayFuncConv(sc);
|
||||
e2x = resolveProperties(sc, e2x);
|
||||
if (e2x.op == TOK.type)
|
||||
e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684
|
||||
if (e2x.op == TOK.error)
|
||||
return setResult(e2x);
|
||||
// We skip checking the value for structs/classes as these might have
|
||||
// We delay checking the value for structs/classes as these might have
|
||||
// an opAssign defined.
|
||||
if ((t1.ty != Tstruct && t1.ty != Tclass && e2x.checkValue()) ||
|
||||
e2x.checkSharedAccess(sc))
|
||||
|
@ -9208,6 +9316,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
else
|
||||
assert(exp.op == TOK.blit);
|
||||
|
||||
if (e2x.checkValue())
|
||||
return setError();
|
||||
|
||||
exp.e1 = e1x;
|
||||
exp.e2 = e2x;
|
||||
}
|
||||
|
@ -9223,6 +9334,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (exp.e2.checkValue())
|
||||
return setError();
|
||||
}
|
||||
else if (t1.ty == Tsarray)
|
||||
{
|
||||
|
@ -9672,7 +9785,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
exp.type = exp.e1.type;
|
||||
assert(exp.type);
|
||||
auto res = exp.op == TOK.assign ? exp.reorderSettingAAElem(sc) : exp;
|
||||
checkAssignEscape(sc, res, false);
|
||||
Expression tmp;
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=22366
|
||||
*
|
||||
* `reorderSettingAAElem` creates a tree of comma expressions, however,
|
||||
* `checkAssignExp` expects only AssignExps.
|
||||
*/
|
||||
checkAssignEscape(sc, Expression.extractLast(res, tmp), false);
|
||||
return setResult(res);
|
||||
}
|
||||
|
||||
|
@ -9944,6 +10063,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
return;
|
||||
}
|
||||
|
||||
/* ImportC: convert arrays to pointers, functions to pointers to functions
|
||||
*/
|
||||
exp.e1 = exp.e1.arrayFuncConv(sc);
|
||||
exp.e2 = exp.e2.arrayFuncConv(sc);
|
||||
|
||||
Type tb1 = exp.e1.type.toBasetype();
|
||||
Type tb2 = exp.e2.type.toBasetype();
|
||||
|
||||
|
@ -10045,6 +10169,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
return;
|
||||
}
|
||||
|
||||
/* ImportC: convert arrays to pointers, functions to pointers to functions
|
||||
*/
|
||||
exp.e1 = exp.e1.arrayFuncConv(sc);
|
||||
exp.e2 = exp.e2.arrayFuncConv(sc);
|
||||
|
||||
Type t1 = exp.e1.type.toBasetype();
|
||||
Type t2 = exp.e2.type.toBasetype();
|
||||
|
||||
|
@ -11539,12 +11668,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
|
|||
ec = ec.toBoolean(sc);
|
||||
|
||||
CtorFlow ctorflow_root = sc.ctorflow.clone();
|
||||
Expression e1x = exp.e1.expressionSemantic(sc);
|
||||
Expression e1x = exp.e1.expressionSemantic(sc).arrayFuncConv(sc);
|
||||
e1x = resolveProperties(sc, e1x);
|
||||
|
||||
CtorFlow ctorflow1 = sc.ctorflow;
|
||||
sc.ctorflow = ctorflow_root;
|
||||
Expression e2x = exp.e2.expressionSemantic(sc);
|
||||
Expression e2x = exp.e2.expressionSemantic(sc).arrayFuncConv(sc);
|
||||
e2x = resolveProperties(sc, e2x);
|
||||
|
||||
sc.merge(exp.loc, ctorflow1);
|
||||
|
@ -11894,95 +12023,84 @@ Expression semanticX(DotIdExp exp, Scope* sc)
|
|||
if (exp.ident == Id._mangleof)
|
||||
{
|
||||
// symbol.mangleof
|
||||
|
||||
// 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
301
gcc/d/dmd/file_manager.d
Normal 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();
|
||||
}
|
||||
}
|
|
@ -4,17 +4,16 @@
|
|||
* http://www.digitalmars.com
|
||||
* Distributed under the Boost Software License, Version 1.0.
|
||||
* http://www.boost.org/LICENSE_1_0.txt
|
||||
* https://github.com/dlang/dmd/blob/master/src/dmd/root/root.h
|
||||
* https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "object.h"
|
||||
#include "root/file.h"
|
||||
|
||||
#include "filename.h"
|
||||
|
||||
#include "file.h"
|
||||
|
||||
#include "outbuffer.h"
|
||||
|
||||
#include "array.h"
|
||||
struct FileManager
|
||||
{
|
||||
static void _init();
|
||||
FileBuffer* lookup(const char* filename);
|
||||
FileBuffer* add(const char* filename, FileBuffer* filebuffer);
|
||||
};
|
|
@ -45,7 +45,8 @@ import dmd.identifier;
|
|||
import dmd.init;
|
||||
import dmd.mtype;
|
||||
import dmd.objc;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.root.aav;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.root.stringtable;
|
||||
|
@ -261,6 +262,8 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
VarDeclaration vresult; /// result variable for out contracts
|
||||
LabelDsymbol returnLabel; /// where the return goes
|
||||
|
||||
bool[size_t] isTypeIsolatedCache; /// cache for the potentially very expensive isTypeIsolated check
|
||||
|
||||
// used to prevent symbols in different
|
||||
// scopes from having the same name
|
||||
DsymbolTable localsymtab;
|
||||
|
@ -740,7 +743,7 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
*/
|
||||
final BaseClass* overrideInterface()
|
||||
{
|
||||
if (ClassDeclaration cd = toParent2().isClassDeclaration())
|
||||
for (ClassDeclaration cd = toParent2().isClassDeclaration(); cd; cd = cd.baseClass)
|
||||
{
|
||||
foreach (b; cd.interfaces)
|
||||
{
|
||||
|
@ -1529,8 +1532,23 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
extern (D) final bool isTypeIsolated(Type t)
|
||||
{
|
||||
StringTable!Type parentTypes;
|
||||
parentTypes._init();
|
||||
return isTypeIsolated(t, parentTypes);
|
||||
const uniqueTypeID = t.getUniqueID();
|
||||
if (uniqueTypeID)
|
||||
{
|
||||
const cacheResultPtr = uniqueTypeID in isTypeIsolatedCache;
|
||||
if (cacheResultPtr !is null)
|
||||
return *cacheResultPtr;
|
||||
|
||||
parentTypes._init();
|
||||
const isIsolated = isTypeIsolated(t, parentTypes);
|
||||
isTypeIsolatedCache[uniqueTypeID] = isIsolated;
|
||||
return isIsolated;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentTypes._init();
|
||||
return isTypeIsolated(t, parentTypes);
|
||||
}
|
||||
}
|
||||
|
||||
///ditto
|
||||
|
@ -2593,9 +2611,10 @@ extern (C++) class FuncDeclaration : Declaration
|
|||
}
|
||||
|
||||
if (!tf.nextOf())
|
||||
error("must return `int` or `void`");
|
||||
else if (tf.nextOf().ty != Tint32 && tf.nextOf().ty != Tvoid)
|
||||
error("must return `int` or `void`, not `%s`", tf.nextOf().toChars());
|
||||
// auto main(), check after semantic
|
||||
assert(this.inferRetType);
|
||||
else if (tf.nextOf().ty != Tint32 && tf.nextOf().ty != Tvoid && tf.nextOf().ty != Tnoreturn)
|
||||
error("must return `int`, `void` or `noreturn`, not `%s`", tf.nextOf().toChars());
|
||||
else if (tf.parameterList.varargs || nparams >= 2 || argerr)
|
||||
error("parameters must be `main()` or `main(string[] args)`");
|
||||
}
|
||||
|
@ -3054,7 +3073,11 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s,
|
|||
return null;
|
||||
|
||||
bool hasOverloads = fd.overnext !is null;
|
||||
auto tf = fd.type.toTypeFunction();
|
||||
auto tf = fd.type.isTypeFunction();
|
||||
// if type is an error, the original type should be there for better diagnostics
|
||||
if (!tf)
|
||||
tf = fd.originalType.toTypeFunction();
|
||||
|
||||
if (tthis && !MODimplicitConv(tthis.mod, tf.mod)) // modifier mismatch
|
||||
{
|
||||
OutBuffer thisBuf, funcBuf;
|
||||
|
@ -3253,17 +3276,7 @@ private bool traverseIndirections(Type ta, Type tb)
|
|||
{
|
||||
//printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars());
|
||||
|
||||
/* Threaded list of aggregate types already examined,
|
||||
* used to break cycles.
|
||||
* Cycles in type graphs can only occur with aggregates.
|
||||
*/
|
||||
static struct Ctxt
|
||||
{
|
||||
Ctxt* prev;
|
||||
Type type; // an aggregate type
|
||||
}
|
||||
|
||||
static bool traverse(Type ta, Type tb, Ctxt* ctxt, bool reversePass)
|
||||
static bool traverse(Type ta, Type tb, ref scope AssocArray!(const(char)*, bool) table, bool reversePass)
|
||||
{
|
||||
//printf("traverse(%s, %s)\n", ta.toChars(), tb.toChars());
|
||||
ta = ta.baseElemOf();
|
||||
|
@ -3293,28 +3306,27 @@ private bool traverseIndirections(Type ta, Type tb)
|
|||
|
||||
if (tb.ty == Tclass || tb.ty == Tstruct)
|
||||
{
|
||||
for (Ctxt* c = ctxt; c; c = c.prev)
|
||||
if (tb == c.type)
|
||||
return true;
|
||||
Ctxt c;
|
||||
c.prev = ctxt;
|
||||
c.type = tb;
|
||||
|
||||
/* Traverse the type of each field of the aggregate
|
||||
*/
|
||||
bool* found = table.getLvalue(tb.deco);
|
||||
if (*found == true)
|
||||
return true; // We have already seen this symbol, break the cycle
|
||||
else
|
||||
*found = true;
|
||||
|
||||
AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration();
|
||||
foreach (v; sym.fields)
|
||||
{
|
||||
Type tprmi = v.type.addMod(tb.mod);
|
||||
//printf("\ttb = %s, tprmi = %s\n", tb.toChars(), tprmi.toChars());
|
||||
if (!traverse(ta, tprmi, &c, reversePass))
|
||||
if (!traverse(ta, tprmi, table, reversePass))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer)
|
||||
{
|
||||
Type tind = tb.nextOf();
|
||||
if (!traverse(ta, tind, ctxt, reversePass))
|
||||
if (!traverse(ta, tind, table, reversePass))
|
||||
return false;
|
||||
}
|
||||
else if (tb.hasPointers())
|
||||
|
@ -3325,7 +3337,10 @@ private bool traverseIndirections(Type ta, Type tb)
|
|||
|
||||
// Still no match, so try breaking up ta if we have not done so yet.
|
||||
if (!reversePass)
|
||||
return traverse(tb, ta, ctxt, true);
|
||||
{
|
||||
scope newTable = AssocArray!(const(char)*, bool)();
|
||||
return traverse(tb, ta, newTable, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3333,7 +3348,8 @@ private bool traverseIndirections(Type ta, Type tb)
|
|||
// To handle arbitrary levels of indirections in both parameters, we
|
||||
// recursively descend into aggregate members/levels of indirection in both
|
||||
// `ta` and `tb` while avoiding cycles. Start with the original types.
|
||||
const result = traverse(ta, tb, null, false);
|
||||
scope table = AssocArray!(const(char)*, bool)();
|
||||
const result = traverse(ta, tb, table, false);
|
||||
//printf(" returns %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ module dmd.globals;
|
|||
import core.stdc.stdint;
|
||||
import dmd.root.array;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.identifier;
|
||||
|
||||
/// Defines a setting for how compiler warnings and deprecations are handled
|
||||
|
@ -118,6 +118,7 @@ extern (C++) struct Param
|
|||
bool vgc; // identify gc usage
|
||||
bool vfield; // identify non-mutable field variables
|
||||
bool vcomplex = true; // identify complex/imaginary type usage
|
||||
bool vin; // identify 'in' parameters
|
||||
ubyte symdebug; // insert debug symbolic information
|
||||
bool symdebugref; // insert debug information for all referenced types, too
|
||||
bool optimize; // run optimizer
|
||||
|
@ -261,11 +262,29 @@ extern (C++) struct Param
|
|||
const(char)[] mapfile;
|
||||
}
|
||||
|
||||
alias structalign_t = uint;
|
||||
extern (C++) struct structalign_t
|
||||
{
|
||||
private:
|
||||
ushort value = 0; // unknown
|
||||
enum STRUCTALIGN_DEFAULT = 1234; // default = match whatever the corresponding C compiler does
|
||||
bool pack; // use #pragma pack semantics
|
||||
|
||||
public:
|
||||
pure @safe @nogc nothrow:
|
||||
bool isDefault() const { return value == STRUCTALIGN_DEFAULT; }
|
||||
void setDefault() { value = STRUCTALIGN_DEFAULT; }
|
||||
bool isUnknown() const { return value == 0; } // value is not set
|
||||
void setUnknown() { value = 0; }
|
||||
void set(uint value) { this.value = cast(ushort)value; }
|
||||
uint get() const { return value; }
|
||||
bool isPack() const { return pack; }
|
||||
void setPack(bool pack) { this.pack = pack; }
|
||||
}
|
||||
//alias structalign_t = uint;
|
||||
|
||||
// magic value means "match whatever the underlying C compiler does"
|
||||
// other values are all powers of 2
|
||||
enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
|
||||
//enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
|
||||
|
||||
enum mars_ext = "d"; // for D source files
|
||||
enum doc_ext = "html"; // for Ddoc generated files
|
||||
|
@ -307,6 +326,8 @@ extern (C++) struct Global
|
|||
Array!Identifier* versionids; /// command line versions and predefined versions
|
||||
Array!Identifier* debugids; /// command line debug versions and predefined versions
|
||||
|
||||
bool hasMainFunction; /// Whether a main function has already been compiled in (for -main switch)
|
||||
|
||||
enum recursionLimit = 500; /// number of recursive template expansions before abort
|
||||
|
||||
nothrow:
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include "root/dcompat.h"
|
||||
#include "root/ctfloat.h"
|
||||
#include "root/outbuffer.h"
|
||||
#include "common/outbuffer.h"
|
||||
#include "root/filename.h"
|
||||
#include "compiler.h"
|
||||
|
||||
|
@ -107,6 +107,7 @@ struct Param
|
|||
bool vgc; // identify gc usage
|
||||
bool vfield; // identify non-mutable field variables
|
||||
bool vcomplex; // identify complex/imaginary type usage
|
||||
bool vin; // identify 'in' parameters
|
||||
unsigned char symdebug; // insert debug symbolic information
|
||||
bool symdebugref; // insert debug information for all referenced types, too
|
||||
bool optimize; // run optimizer
|
||||
|
@ -238,10 +239,24 @@ struct Param
|
|||
DString mapfile;
|
||||
};
|
||||
|
||||
typedef unsigned structalign_t;
|
||||
struct structalign_t
|
||||
{
|
||||
unsigned short value;
|
||||
bool pack;
|
||||
|
||||
bool isDefault() const;
|
||||
void setDefault();
|
||||
bool isUnknown() const;
|
||||
void setUnknown();
|
||||
void set(unsigned value);
|
||||
unsigned get() const;
|
||||
bool isPack() const;
|
||||
void setPack(bool pack);
|
||||
};
|
||||
|
||||
// magic value means "match whatever the underlying C compiler does"
|
||||
// other values are all powers of 2
|
||||
#define STRUCTALIGN_DEFAULT ((structalign_t) ~0)
|
||||
//#define STRUCTALIGN_DEFAULT ((structalign_t) ~0)
|
||||
|
||||
const DString mars_ext = "d";
|
||||
const DString doc_ext = "html"; // for Ddoc generated files
|
||||
|
@ -274,6 +289,8 @@ struct Global
|
|||
Array<class Identifier*>* versionids; // command line versions and predefined versions
|
||||
Array<class Identifier*>* debugids; // command line debug versions and predefined versions
|
||||
|
||||
bool hasMainFunction;
|
||||
|
||||
/* Start gagging. Return the current number of gagged errors
|
||||
*/
|
||||
unsigned startGagging();
|
||||
|
|
|
@ -78,6 +78,7 @@ else version (IN_GCC)
|
|||
extern (C++)
|
||||
{
|
||||
Statement asmSemantic(AsmStatement s, Scope* sc);
|
||||
void toObjFile(Dsymbol ds, bool multiobj);
|
||||
}
|
||||
|
||||
// stubs
|
||||
|
|
|
@ -44,7 +44,7 @@ import dmd.mtype;
|
|||
import dmd.nspace;
|
||||
import dmd.parse;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.statement;
|
||||
|
@ -909,6 +909,12 @@ public:
|
|||
|
||||
override void visit(AttribDeclaration d)
|
||||
{
|
||||
bool hasSTC;
|
||||
if (auto stcd = d.isStorageClassDeclaration)
|
||||
{
|
||||
hasSTC = stcToBuffer(buf, stcd.stc);
|
||||
}
|
||||
|
||||
if (!d.decl)
|
||||
{
|
||||
buf.writeByte(';');
|
||||
|
@ -918,10 +924,12 @@ public:
|
|||
if (d.decl.dim == 0 || (hgs.hdrgen && d.decl.dim == 1 && (*d.decl)[0].isUnitTestDeclaration()))
|
||||
{
|
||||
// hack for bugzilla 8081
|
||||
if (hasSTC) buf.writeByte(' ');
|
||||
buf.writestring("{}");
|
||||
}
|
||||
else if (d.decl.dim == 1)
|
||||
{
|
||||
if (hasSTC) buf.writeByte(' ');
|
||||
(*d.decl)[0].accept(this);
|
||||
return;
|
||||
}
|
||||
|
@ -941,8 +949,6 @@ public:
|
|||
|
||||
override void visit(StorageClassDeclaration d)
|
||||
{
|
||||
if (stcToBuffer(buf, d.stc))
|
||||
buf.writeByte(' ');
|
||||
visit(cast(AttribDeclaration)d);
|
||||
}
|
||||
|
||||
|
@ -1324,11 +1330,10 @@ public:
|
|||
if (d.ident)
|
||||
{
|
||||
buf.writestring(d.ident.toString());
|
||||
buf.writeByte(' ');
|
||||
}
|
||||
if (d.memtype)
|
||||
{
|
||||
buf.writestring(": ");
|
||||
buf.writestring(" : ");
|
||||
typeToBuffer(d.memtype, null, buf, hgs);
|
||||
}
|
||||
if (!d.members)
|
||||
|
@ -2362,7 +2367,10 @@ public:
|
|||
override void visit(DotIdExp e)
|
||||
{
|
||||
expToBuffer(e.e1, PREC.primary, buf, hgs);
|
||||
buf.writeByte('.');
|
||||
if (e.arrow)
|
||||
buf.writestring("->");
|
||||
else
|
||||
buf.writeByte('.');
|
||||
buf.writestring(e.ident.toString());
|
||||
}
|
||||
|
||||
|
|
|
@ -300,7 +300,7 @@ Ldone:
|
|||
* Returns:
|
||||
* the completed gcc asm statement, or null if errors occurred
|
||||
*/
|
||||
public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
|
||||
extern (C++) public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc)
|
||||
{
|
||||
//printf("GccAsmStatement.semantic()\n");
|
||||
scope p = new Parser!ASTCodegen(sc._module, ";", false);
|
||||
|
|
|
@ -500,6 +500,17 @@ immutable Msgtable[] msgtable =
|
|||
{ "vector_size" },
|
||||
{ "__func__" },
|
||||
{ "noreturn" },
|
||||
{ "__pragma", "pragma" },
|
||||
{ "builtin_va_list", "__builtin_va_list" },
|
||||
{ "builtin_va_start", "__builtin_va_start" },
|
||||
{ "builtin_va_arg", "__builtin_va_arg" },
|
||||
{ "builtin_va_copy", "__builtin_va_copy" },
|
||||
{ "builtin_va_end", "__builtin_va_end" },
|
||||
{ "va_list_tag", "__va_list_tag" },
|
||||
{ "pack" },
|
||||
{ "show" },
|
||||
{ "push" },
|
||||
{ "pop" },
|
||||
];
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import core.stdc.stdio;
|
|||
import core.stdc.string;
|
||||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.root.stringtable;
|
||||
|
|
171
gcc/d/dmd/importc.d
Normal file
171
gcc/d/dmd/importc.d
Normal 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);
|
||||
}
|
|
@ -23,7 +23,7 @@ import dmd.globals;
|
|||
import dmd.hdrgen;
|
||||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.tokens;
|
||||
import dmd.visitor;
|
||||
|
|
|
@ -31,6 +31,7 @@ import dmd.func;
|
|||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.importc;
|
||||
import dmd.init;
|
||||
import dmd.mtype;
|
||||
import dmd.opover;
|
||||
|
@ -176,30 +177,35 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (fieldi >= nfields)
|
||||
if (j >= nfields)
|
||||
{
|
||||
error(i.loc, "too many initializers for `%s`", sd.toChars());
|
||||
error(i.value[j].loc, "too many initializers for `%s`", sd.toChars());
|
||||
return err();
|
||||
}
|
||||
|
||||
VarDeclaration vd = sd.fields[fieldi];
|
||||
if (elems[fieldi])
|
||||
{
|
||||
error(i.loc, "duplicate initializer for field `%s`", vd.toChars());
|
||||
error(i.value[j].loc, "duplicate initializer for field `%s`", vd.toChars());
|
||||
errors = true;
|
||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||
++fieldi;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for @safe violations
|
||||
if (vd.type.hasPointers)
|
||||
{
|
||||
if ((t.alignment() < target.ptrsize ||
|
||||
if ((!t.alignment.isDefault() && t.alignment.get() < target.ptrsize ||
|
||||
(vd.offset & (target.ptrsize - 1))) &&
|
||||
sc.func && sc.func.setUnsafe())
|
||||
{
|
||||
error(i.loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
|
||||
error(i.value[j].loc, "field `%s.%s` cannot assign to misaligned pointers in `@safe` code",
|
||||
sd.toChars(), vd.toChars());
|
||||
errors = true;
|
||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||
++fieldi;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,7 +214,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
{
|
||||
if (vd.isOverlappedWith(v2) && elems[k])
|
||||
{
|
||||
error(i.loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
|
||||
error(elems[k].loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
|
||||
errors = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -222,6 +228,8 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
if (ex.op == TOK.error)
|
||||
{
|
||||
errors = true;
|
||||
elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors
|
||||
++fieldi;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -363,10 +371,10 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
if (length > i.dim)
|
||||
i.dim = length;
|
||||
}
|
||||
if (t.ty == Tsarray)
|
||||
if (auto tsa = t.isTypeSArray())
|
||||
{
|
||||
uinteger_t edim = (cast(TypeSArray)t).dim.toInteger();
|
||||
if (i.dim > edim)
|
||||
uinteger_t edim = tsa.dim.toInteger();
|
||||
if (i.dim > edim && !(tsa.isIncomplete() && (sc.flags & SCOPE.Cfile)))
|
||||
{
|
||||
error(i.loc, "array initializer has %u elements, but array length is %llu", i.dim, edim);
|
||||
return err();
|
||||
|
@ -398,6 +406,13 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
if (i.exp.op == TOK.error)
|
||||
return err();
|
||||
uint olderrors = global.errors;
|
||||
|
||||
/* ImportC: convert arrays to pointers, functions to pointers to functions
|
||||
*/
|
||||
Type tb = t.toBasetype();
|
||||
if (tb.isTypePointer())
|
||||
i.exp = i.exp.arrayFuncConv(sc);
|
||||
|
||||
/* Save the expression before ctfe
|
||||
* Otherwise the error message would contain for example "&[0][0]" instead of "new int"
|
||||
* Regression: https://issues.dlang.org/show_bug.cgi?id=21687
|
||||
|
@ -408,7 +423,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
// If the result will be implicitly cast, move the cast into CTFE
|
||||
// to avoid premature truncation of polysemous types.
|
||||
// eg real [] x = [1.1, 2.2]; should use real precision.
|
||||
if (i.exp.implicitConvTo(t))
|
||||
if (i.exp.implicitConvTo(t) && !(sc.flags & SCOPE.Cfile))
|
||||
{
|
||||
i.exp = i.exp.implicitCastTo(sc, t);
|
||||
}
|
||||
|
@ -416,6 +431,11 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
{
|
||||
return i;
|
||||
}
|
||||
if (sc.flags & SCOPE.Cfile)
|
||||
/* the interpreter turns (char*)"string" into &"string"[0] which then
|
||||
* it cannot interpret. Resolve that case by doing optimize() first
|
||||
*/
|
||||
i.exp = i.exp.optimize(WANTvalue);
|
||||
i.exp = i.exp.ctfeInterpret();
|
||||
if (i.exp.op == TOK.voidExpression)
|
||||
error(i.loc, "variables cannot be initialized with an expression of type `void`. Use `void` initialization instead.");
|
||||
|
@ -424,6 +444,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
{
|
||||
i.exp = i.exp.optimize(WANTvalue);
|
||||
}
|
||||
|
||||
if (!global.gag && olderrors != global.errors)
|
||||
{
|
||||
return i; // Failed, suppress duplicate error messages
|
||||
|
@ -445,7 +466,6 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
i.exp.error("cannot use non-constant CTFE pointer in an initializer `%s`", currExp.toChars());
|
||||
return err();
|
||||
}
|
||||
Type tb = t.toBasetype();
|
||||
Type ti = i.exp.type.toBasetype();
|
||||
if (i.exp.op == TOK.tuple && i.expandTuples && !i.exp.implicitConvTo(t))
|
||||
{
|
||||
|
@ -470,13 +490,12 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
goto L1;
|
||||
}
|
||||
}
|
||||
|
||||
/* C11 6.7.9-14..15
|
||||
* Initialize an array of unknown size with a string.
|
||||
* ImportC regards Tarray as an array of unknown size.
|
||||
* Change to static array of known size
|
||||
*/
|
||||
if (sc.flags & SCOPE.Cfile && i.exp.op == TOK.string_ && tb.ty == Tarray)
|
||||
if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() &&
|
||||
tb.isTypeSArray() && tb.isTypeSArray().isIncomplete())
|
||||
{
|
||||
StringExp se = i.exp.isStringExp();
|
||||
auto ts = new TypeSArray(tb.nextOf(), new IntegerExp(Loc.initial, se.len + 1, Type.tsize_t));
|
||||
|
@ -683,8 +702,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
}
|
||||
|
||||
auto tsa = t.isTypeSArray();
|
||||
auto ta = t.isTypeDArray();
|
||||
if (!(tsa || ta))
|
||||
if (!tsa)
|
||||
{
|
||||
/* Not an array. See if it is `{ exp }` which can be
|
||||
* converted to an ExpInitializer
|
||||
|
@ -722,19 +740,32 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
{
|
||||
//printf(" type %s i %d dim %d dil.length = %d\n", t.toChars(), cast(int)i, cast(int)dim, cast(int)dil.length);
|
||||
auto tn = t.nextOf().toBasetype();
|
||||
if (auto tna = tn.isTypeDArray())
|
||||
auto tnsa = tn.isTypeSArray();
|
||||
if (tnsa && tnsa.isIncomplete())
|
||||
{
|
||||
// C11 6.2.5-20 "element type shall be complete whenever the array type is specified"
|
||||
error(ci.loc, "incomplete element type `%s` not allowed", tna.toChars());
|
||||
error(ci.loc, "incomplete element type `%s` not allowed", tnsa.toChars());
|
||||
errors = true;
|
||||
return 1;
|
||||
}
|
||||
if (i == dil.length)
|
||||
return 0;
|
||||
size_t n;
|
||||
auto tnsa = tn.isTypeSArray();
|
||||
const nelems = tnsa ? cast(size_t)tnsa.dim.toInteger() : 0;
|
||||
|
||||
/* Run initializerSemantic on a single element.
|
||||
*/
|
||||
Initializer elem(Initializer ie)
|
||||
{
|
||||
++i;
|
||||
auto tnx = tn; // in case initializerSemantic tries to change it
|
||||
ie = ie.initializerSemantic(sc, tnx, needInterpret);
|
||||
if (ie.isErrorInitializer())
|
||||
errors = true;
|
||||
assert(tnx == tn); // sub-types should not be modified
|
||||
return ie;
|
||||
}
|
||||
|
||||
foreach (j; 0 .. dim)
|
||||
{
|
||||
auto di = dil[i];
|
||||
|
@ -751,16 +782,17 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
}
|
||||
else if (auto tns = tn.isTypeStruct())
|
||||
{
|
||||
dil[n].initializer = structs(tns);
|
||||
if (di.initializer.isExpInitializer())
|
||||
{
|
||||
// no braces enclosing struct initializer
|
||||
dil[n].initializer = structs(tns);
|
||||
}
|
||||
else
|
||||
dil[n].initializer = elem(di.initializer);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
auto tnx = tn; // in case initializerSemantic tries to change it
|
||||
di.initializer = di.initializer.initializerSemantic(sc, tnx, needInterpret);
|
||||
if (di.initializer.isErrorInitializer())
|
||||
errors = true;
|
||||
assert(tnx == tn); // sub-types should not be modified
|
||||
di.initializer = elem(di.initializer);
|
||||
}
|
||||
++n;
|
||||
if (i == dil.length)
|
||||
|
@ -770,16 +802,16 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
return n;
|
||||
}
|
||||
|
||||
size_t dim = ta ? dil.length : cast(size_t)tsa.dim.toInteger();
|
||||
auto n = array(t, dim);
|
||||
size_t dim = tsa.isIncomplete() ? dil.length : cast(size_t)tsa.dim.toInteger();
|
||||
auto newdim = array(t, dim);
|
||||
|
||||
if (errors)
|
||||
return err();
|
||||
|
||||
if (ta) // array of unknown length
|
||||
if (tsa.isIncomplete()) // array of unknown length
|
||||
{
|
||||
// Change to array of known length
|
||||
tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, n, Type.tsize_t));
|
||||
tsa = new TypeSArray(tn, new IntegerExp(Loc.initial, newdim, Type.tsize_t));
|
||||
tx = tsa; // rewrite caller's type
|
||||
ci.type = tsa; // remember for later passes
|
||||
}
|
||||
|
@ -799,6 +831,39 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ
|
|||
return err();
|
||||
}
|
||||
|
||||
/* If an array of simple elements, replace with an ArrayInitializer
|
||||
*/
|
||||
auto tnb = tn.toBasetype();
|
||||
if (!(tnb.isTypeSArray() || tnb.isTypeStruct()))
|
||||
{
|
||||
auto ai = new ArrayInitializer(ci.loc);
|
||||
ai.dim = cast(uint) dil.length;
|
||||
ai.index.setDim(dil.length);
|
||||
ai.value.setDim(dil.length);
|
||||
foreach (const j; 0 .. dil.length)
|
||||
{
|
||||
ai.index[j] = null;
|
||||
ai.value[j] = dil[j].initializer;
|
||||
}
|
||||
auto ty = tx;
|
||||
return ai.initializerSemantic(sc, ty, needInterpret);
|
||||
}
|
||||
|
||||
if (newdim < ci.initializerList.length && tnb.isTypeStruct())
|
||||
{
|
||||
// https://issues.dlang.org/show_bug.cgi?id=22375
|
||||
// initializerList can be bigger than the number of actual elements
|
||||
// to initialize for array of structs because it is not required
|
||||
// for values to have proper bracing.
|
||||
// i.e: These are all valid initializers for `struct{int a,b;}[3]`:
|
||||
// {1,2,3,4}, {{1,2},3,4}, {1,2,{3,4}}, {{1,2},{3,4}}
|
||||
// In all examples above, the new length of the initializer list
|
||||
// has been shortened from four elements to two. This is important,
|
||||
// because `dil` is written back to directly, making the lowered
|
||||
// initializer `{{1,2},{3,4}}` and not `{{1,2},{3,4},3,4}`.
|
||||
ci.initializerList.length = newdim;
|
||||
}
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
|
@ -1263,6 +1328,3 @@ private bool hasNonConstPointers(Expression e)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -33,7 +33,7 @@ import dmd.hdrgen;
|
|||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
import dmd.target;
|
||||
|
@ -794,8 +794,8 @@ public:
|
|||
property("init", d._init.toString());
|
||||
if (d.isField())
|
||||
property("offset", d.offset);
|
||||
if (d.alignment && d.alignment != STRUCTALIGN_DEFAULT)
|
||||
property("align", d.alignment);
|
||||
if (!d.alignment.isUnknown() && !d.alignment.isDefault())
|
||||
property("align", d.alignment.get());
|
||||
objectEnd();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import dmd.expression;
|
|||
import dmd.func;
|
||||
import dmd.dmangle;
|
||||
import dmd.mtype;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.stringtable;
|
||||
import dmd.dscope;
|
||||
|
|
|
@ -26,8 +26,9 @@ import dmd.errors;
|
|||
import dmd.globals;
|
||||
import dmd.id;
|
||||
import dmd.identifier;
|
||||
import dmd.root.array;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.port;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.string;
|
||||
|
@ -229,6 +230,8 @@ class Lexer
|
|||
ubyte long_doublesize; /// size of C long double, 8 or D real.sizeof
|
||||
ubyte wchar_tsize; /// size of C wchar_t, 2 or 4
|
||||
|
||||
structalign_t packalign; /// current state of #pragma pack alignment (ImportC)
|
||||
|
||||
private
|
||||
{
|
||||
const(char)* base; // pointer to start of buffer
|
||||
|
@ -242,6 +245,10 @@ class Lexer
|
|||
int lastDocLine; // last line of previous doc comment
|
||||
|
||||
Token* tokenFreelist;
|
||||
|
||||
// ImportC #pragma pack stack
|
||||
Array!Identifier* records; // identifers (or null)
|
||||
Array!structalign_t* packs; // parallel alignment values
|
||||
}
|
||||
|
||||
nothrow:
|
||||
|
@ -273,6 +280,7 @@ class Lexer
|
|||
this.commentToken = commentToken;
|
||||
this.inTokenStringConstant = 0;
|
||||
this.lastDocLine = 0;
|
||||
this.packalign.setDefault();
|
||||
//initKeywords();
|
||||
/* If first line starts with '#!', ignore the line
|
||||
*/
|
||||
|
@ -1146,6 +1154,11 @@ class Lexer
|
|||
poundLine(n, false);
|
||||
continue;
|
||||
}
|
||||
else if (n.ident == Id.__pragma && Ccompile)
|
||||
{
|
||||
pragmaDirective(scanloc);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const locx = loc();
|
||||
|
@ -2162,7 +2175,7 @@ class Lexer
|
|||
case '.':
|
||||
if (p[1] == '.')
|
||||
goto Ldone; // if ".."
|
||||
if (base == 10 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
|
||||
if (base <= 10 && n > 0 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80))
|
||||
goto Ldone; // if ".identifier" or ".unicode"
|
||||
if (base == 16 && (!ishex(p[1]) || p[1] == '_' || p[1] & 0x80))
|
||||
goto Ldone; // if ".identifier" or ".unicode"
|
||||
|
@ -2911,6 +2924,220 @@ class Lexer
|
|||
error(loc, "#line integer [\"filespec\"]\\n expected");
|
||||
}
|
||||
|
||||
/*********************************************
|
||||
* C11 6.10.6 Pragma directive
|
||||
* # pragma pp-tokens(opt) new-line
|
||||
* The C preprocessor sometimes leaves pragma directives in
|
||||
* the preprocessed output. Ignore them.
|
||||
* Upon return, p is at start of next line.
|
||||
*/
|
||||
private void pragmaDirective(const ref Loc loc)
|
||||
{
|
||||
Token n;
|
||||
scan(&n);
|
||||
if (n.value == TOK.identifier && n.ident == Id.pack)
|
||||
return pragmaPack(loc);
|
||||
skipToNextLine();
|
||||
}
|
||||
|
||||
/*********
|
||||
* ImportC
|
||||
* # pragma pack
|
||||
* https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html
|
||||
* https://docs.microsoft.com/en-us/cpp/preprocessor/pack
|
||||
* Scanner is on the `pack`
|
||||
* Params:
|
||||
* startloc = location to use for error messages
|
||||
*/
|
||||
private void pragmaPack(const ref Loc startloc)
|
||||
{
|
||||
const loc = startloc;
|
||||
Token n;
|
||||
scan(&n);
|
||||
if (n.value != TOK.leftParenthesis)
|
||||
{
|
||||
error(loc, "left parenthesis expected to follow `#pragma pack`");
|
||||
skipToNextLine();
|
||||
return;
|
||||
}
|
||||
|
||||
void closingParen()
|
||||
{
|
||||
if (n.value != TOK.rightParenthesis)
|
||||
{
|
||||
error(loc, "right parenthesis expected to close `#pragma pack(`");
|
||||
}
|
||||
skipToNextLine();
|
||||
}
|
||||
|
||||
void setPackAlign(ref const Token t)
|
||||
{
|
||||
const n = t.unsvalue;
|
||||
if (n < 1 || n & (n - 1) || ushort.max < n)
|
||||
error(loc, "pack must be an integer positive power of 2, not 0x%llx", cast(ulong)n);
|
||||
packalign.set(cast(uint)n);
|
||||
packalign.setPack(true);
|
||||
}
|
||||
|
||||
scan(&n);
|
||||
|
||||
if (!records)
|
||||
{
|
||||
records = new Array!Identifier;
|
||||
packs = new Array!structalign_t;
|
||||
}
|
||||
|
||||
/* # pragma pack ( show )
|
||||
*/
|
||||
if (n.value == TOK.identifier && n.ident == Id.show)
|
||||
{
|
||||
if (packalign.isDefault())
|
||||
warning(startloc, "current pack attribute is default");
|
||||
else
|
||||
warning(startloc, "current pack attribute is %d", packalign.get());
|
||||
scan(&n);
|
||||
return closingParen();
|
||||
}
|
||||
/* # pragma pack ( push )
|
||||
* # pragma pack ( push , identifier )
|
||||
* # pragma pack ( push , integer )
|
||||
* # pragma pack ( push , identifier , integer )
|
||||
*/
|
||||
if (n.value == TOK.identifier && n.ident == Id.push)
|
||||
{
|
||||
scan(&n);
|
||||
Identifier record = null;
|
||||
if (n.value == TOK.comma)
|
||||
{
|
||||
scan(&n);
|
||||
if (n.value == TOK.identifier)
|
||||
{
|
||||
record = n.ident;
|
||||
scan(&n);
|
||||
if (n.value == TOK.comma)
|
||||
{
|
||||
scan(&n);
|
||||
if (n.value == TOK.int32Literal)
|
||||
{
|
||||
setPackAlign(n);
|
||||
scan(&n);
|
||||
}
|
||||
else
|
||||
error(loc, "alignment value expected, not `%s`", n.toChars());
|
||||
}
|
||||
}
|
||||
else if (n.value == TOK.int32Literal)
|
||||
{
|
||||
setPackAlign(n);
|
||||
scan(&n);
|
||||
}
|
||||
else
|
||||
error(loc, "alignment value expected, not `%s`", n.toChars());
|
||||
}
|
||||
this.records.push(record);
|
||||
this.packs.push(packalign);
|
||||
return closingParen();
|
||||
}
|
||||
/* # pragma pack ( pop )
|
||||
* # pragma pack ( pop PopList )
|
||||
* PopList :
|
||||
* , IdentifierOrInteger
|
||||
* , IdentifierOrInteger PopList
|
||||
* IdentifierOrInteger:
|
||||
* identifier
|
||||
* integer
|
||||
*/
|
||||
if (n.value == TOK.identifier && n.ident == Id.pop)
|
||||
{
|
||||
scan(&n);
|
||||
while (n.value == TOK.comma)
|
||||
{
|
||||
scan(&n);
|
||||
if (n.value == TOK.identifier)
|
||||
{
|
||||
for (size_t len = this.records.length; len; --len)
|
||||
{
|
||||
if ((*this.records)[len - 1] == n.ident)
|
||||
{
|
||||
packalign = (*this.packs)[len - 1];
|
||||
this.records.setDim(len - 1);
|
||||
this.packs.setDim(len - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
scan(&n);
|
||||
}
|
||||
else if (n.value == TOK.int32Literal)
|
||||
{
|
||||
setPackAlign(n);
|
||||
this.records.push(null);
|
||||
this.packs.push(packalign);
|
||||
scan(&n);
|
||||
}
|
||||
}
|
||||
return closingParen();
|
||||
}
|
||||
/* # pragma pack ( integer )
|
||||
*/
|
||||
if (n.value == TOK.int32Literal)
|
||||
{
|
||||
setPackAlign(n);
|
||||
scan(&n);
|
||||
return closingParen();
|
||||
}
|
||||
/* # pragma pack ( )
|
||||
*/
|
||||
if (n.value == TOK.rightParenthesis)
|
||||
{
|
||||
packalign.setDefault();
|
||||
return closingParen();
|
||||
}
|
||||
|
||||
error(loc, "unrecognized `#pragma pack(%s)`", n.toChars());
|
||||
skipToNextLine();
|
||||
}
|
||||
|
||||
/***************************************
|
||||
* Scan forward to start of next line.
|
||||
*/
|
||||
private void skipToNextLine()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case 0:
|
||||
case 0x1A:
|
||||
return; // do not advance p
|
||||
|
||||
case '\n':
|
||||
++p;
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
++p;
|
||||
if (p[0] == '\n')
|
||||
++p;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (*p & 0x80)
|
||||
{
|
||||
const u = decodeUTF();
|
||||
if (u == PS || u == LS)
|
||||
{
|
||||
++p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
endOfLine();
|
||||
}
|
||||
|
||||
/********************************************
|
||||
* Decode UTF character.
|
||||
* Issue error messages for invalid sequences.
|
||||
|
@ -3106,8 +3333,10 @@ class Lexer
|
|||
return p;
|
||||
}
|
||||
|
||||
private:
|
||||
void endOfLine() pure @nogc @safe
|
||||
/**************************
|
||||
* `p` should be at start of next line
|
||||
*/
|
||||
private void endOfLine() pure @nogc @safe
|
||||
{
|
||||
scanloc.linnum++;
|
||||
line = p;
|
||||
|
|
|
@ -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();
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
|
@ -119,7 +119,7 @@ public:
|
|||
|
||||
static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen);
|
||||
|
||||
static Module *load(Loc loc, Identifiers *packages, Identifier *ident);
|
||||
static Module *load(const Loc &loc, Identifiers *packages, Identifier *ident);
|
||||
|
||||
const char *kind() const;
|
||||
bool read(const Loc &loc); // read file, returns 'true' if succeed, 'false' otherwise.
|
||||
|
|
|
@ -43,7 +43,7 @@ import dmd.identifier;
|
|||
import dmd.init;
|
||||
import dmd.opover;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.stringtable;
|
||||
|
@ -237,6 +237,7 @@ enum DotExpFlag
|
|||
{
|
||||
gag = 1, // don't report "not a property" error and just return null
|
||||
noDeref = 2, // the use of the expression will not attempt a dereference
|
||||
noAliasThis = 4, // don't do 'alias this' resolution
|
||||
}
|
||||
|
||||
/// Result of a check whether two types are covariant
|
||||
|
@ -426,6 +427,13 @@ extern (C++) abstract class Type : ASTNode
|
|||
return DYNCAST.type;
|
||||
}
|
||||
|
||||
/// Returns a non-zero unique ID for this Type, or returns 0 if the Type does not (yet) have a unique ID.
|
||||
/// If `semantic()` has not been run, 0 is returned.
|
||||
final size_t getUniqueID() const
|
||||
{
|
||||
return cast(size_t) deco;
|
||||
}
|
||||
|
||||
extern (D)
|
||||
final Mcache* getMcache()
|
||||
{
|
||||
|
@ -2298,7 +2306,9 @@ extern (C++) abstract class Type : ASTNode
|
|||
*/
|
||||
structalign_t alignment()
|
||||
{
|
||||
return STRUCTALIGN_DEFAULT;
|
||||
structalign_t s;
|
||||
s.setDefault();
|
||||
return s;
|
||||
}
|
||||
|
||||
/***************************************
|
||||
|
@ -3532,6 +3542,13 @@ extern (C++) final class TypeSArray : TypeArray
|
|||
this.dim = dim;
|
||||
}
|
||||
|
||||
extern (D) this(Type t) // for incomplete type
|
||||
{
|
||||
super(Tsarray, t);
|
||||
//printf("TypeSArray()\n");
|
||||
this.dim = new IntegerExp(0);
|
||||
}
|
||||
|
||||
override const(char)* kind() const
|
||||
{
|
||||
return "sarray";
|
||||
|
@ -3546,6 +3563,15 @@ extern (C++) final class TypeSArray : TypeArray
|
|||
return result;
|
||||
}
|
||||
|
||||
/***
|
||||
* C11 6.7.6.2-4 incomplete array type
|
||||
* Returns: true if incomplete type
|
||||
*/
|
||||
bool isIncomplete()
|
||||
{
|
||||
return dim.isIntegerExp() && dim.isIntegerExp().getInteger() == 0;
|
||||
}
|
||||
|
||||
override d_uns64 size(const ref Loc loc)
|
||||
{
|
||||
//printf("TypeSArray::size()\n");
|
||||
|
@ -3952,65 +3978,37 @@ extern (C++) final class TypePointer : TypeNext
|
|||
if (equals(to))
|
||||
return MATCH.exact;
|
||||
|
||||
if (next.ty == Tfunction)
|
||||
{
|
||||
if (auto tp = to.isTypePointer())
|
||||
{
|
||||
if (tp.next.ty == Tfunction)
|
||||
{
|
||||
if (next.equals(tp.next))
|
||||
return MATCH.constant;
|
||||
|
||||
if (next.covariant(tp.next) == Covariant.yes)
|
||||
{
|
||||
Type tret = this.next.nextOf();
|
||||
Type toret = tp.next.nextOf();
|
||||
if (tret.ty == Tclass && toret.ty == Tclass)
|
||||
{
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=10219
|
||||
* Check covariant interface return with offset tweaking.
|
||||
* interface I {}
|
||||
* class C : Object, I {}
|
||||
* I function() dg = function C() {} // should be error
|
||||
*/
|
||||
int offset = 0;
|
||||
if (toret.isBaseOf(tret, &offset) && offset != 0)
|
||||
return MATCH.nomatch;
|
||||
}
|
||||
return MATCH.convert;
|
||||
}
|
||||
}
|
||||
else if (tp.next.ty == Tvoid)
|
||||
{
|
||||
// Allow conversions to void*
|
||||
return MATCH.convert;
|
||||
}
|
||||
}
|
||||
// Only convert between pointers
|
||||
auto tp = to.isTypePointer();
|
||||
if (!tp)
|
||||
return MATCH.nomatch;
|
||||
}
|
||||
else if (auto tp = to.isTypePointer())
|
||||
|
||||
assert(this.next);
|
||||
assert(tp.next);
|
||||
|
||||
// Conversion to void*
|
||||
if (tp.next.ty == Tvoid)
|
||||
{
|
||||
assert(tp.next);
|
||||
// Function pointer conversion doesn't check constness?
|
||||
if (this.next.ty == Tfunction)
|
||||
return MATCH.convert;
|
||||
|
||||
if (!MODimplicitConv(next.mod, tp.next.mod))
|
||||
return MATCH.nomatch; // not const-compatible
|
||||
|
||||
/* Alloc conversion to void*
|
||||
*/
|
||||
if (next.ty != Tvoid && tp.next.ty == Tvoid)
|
||||
{
|
||||
return MATCH.convert;
|
||||
}
|
||||
|
||||
MATCH m = next.constConv(tp.next);
|
||||
if (m > MATCH.nomatch)
|
||||
{
|
||||
if (m == MATCH.exact && mod != to.mod)
|
||||
m = MATCH.constant;
|
||||
return m;
|
||||
}
|
||||
return this.next.ty == Tvoid ? MATCH.constant : MATCH.convert;
|
||||
}
|
||||
return MATCH.nomatch;
|
||||
|
||||
// Conversion between function pointers
|
||||
if (auto thisTf = this.next.isTypeFunction())
|
||||
return thisTf.implicitPointerConv(tp.next);
|
||||
|
||||
// Default, no implicit conversion between the pointer targets
|
||||
MATCH m = next.constConv(tp.next);
|
||||
|
||||
if (m == MATCH.exact && mod != to.mod)
|
||||
m = MATCH.constant;
|
||||
return m;
|
||||
}
|
||||
|
||||
override MATCH constConv(Type to)
|
||||
|
@ -4760,7 +4758,10 @@ extern (C++) final class TypeFunction : TypeNext
|
|||
}
|
||||
}
|
||||
else
|
||||
m = arg.implicitConvTo(tprm);
|
||||
{
|
||||
import dmd.dcast : cimplicitConvTo;
|
||||
m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm);
|
||||
}
|
||||
}
|
||||
//printf("match %d\n", m);
|
||||
}
|
||||
|
@ -4971,6 +4972,47 @@ extern (C++) final class TypeFunction : TypeNext
|
|||
return MATCH.nomatch;
|
||||
}
|
||||
|
||||
/+
|
||||
+ Checks whether this function type is convertible to ` to`
|
||||
+ when used in a function pointer / delegate.
|
||||
+
|
||||
+ Params:
|
||||
+ to = target type
|
||||
+
|
||||
+ Returns:
|
||||
+ MATCH.nomatch: `to` is not a covaraint function
|
||||
+ MATCH.convert: `to` is a covaraint function
|
||||
+ MATCH.exact: `to` is identical to this function
|
||||
+/
|
||||
private MATCH implicitPointerConv(Type to)
|
||||
{
|
||||
assert(to);
|
||||
|
||||
if (this == to)
|
||||
return MATCH.constant;
|
||||
|
||||
if (this.covariant(to) == Covariant.yes)
|
||||
{
|
||||
Type tret = this.nextOf();
|
||||
Type toret = to.nextOf();
|
||||
if (tret.ty == Tclass && toret.ty == Tclass)
|
||||
{
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=10219
|
||||
* Check covariant interface return with offset tweaking.
|
||||
* interface I {}
|
||||
* class C : Object, I {}
|
||||
* I function() dg = function C() {} // should be error
|
||||
*/
|
||||
int offset = 0;
|
||||
if (toret.isBaseOf(tret, &offset) && offset != 0)
|
||||
return MATCH.nomatch;
|
||||
}
|
||||
return MATCH.convert;
|
||||
}
|
||||
|
||||
return MATCH.nomatch;
|
||||
}
|
||||
|
||||
/** Extends TypeNext.constConv by also checking for matching attributes **/
|
||||
override MATCH constConv(Type to)
|
||||
{
|
||||
|
@ -5262,27 +5304,16 @@ extern (C++) final class TypeDelegate : TypeNext
|
|||
if (this == to)
|
||||
return MATCH.exact;
|
||||
|
||||
version (all)
|
||||
if (auto toDg = to.isTypeDelegate())
|
||||
{
|
||||
// not allowing covariant conversions because it interferes with overriding
|
||||
if (to.ty == Tdelegate && this.nextOf().covariant(to.nextOf()) == Covariant.yes)
|
||||
{
|
||||
Type tret = this.next.nextOf();
|
||||
Type toret = (cast(TypeDelegate)to).next.nextOf();
|
||||
if (tret.ty == Tclass && toret.ty == Tclass)
|
||||
{
|
||||
/* https://issues.dlang.org/show_bug.cgi?id=10219
|
||||
* Check covariant interface return with offset tweaking.
|
||||
* interface I {}
|
||||
* class C : Object, I {}
|
||||
* I delegate() dg = delegate C() {} // should be error
|
||||
*/
|
||||
int offset = 0;
|
||||
if (toret.isBaseOf(tret, &offset) && offset != 0)
|
||||
return MATCH.nomatch;
|
||||
}
|
||||
return MATCH.convert;
|
||||
}
|
||||
MATCH m = this.next.isTypeFunction().implicitPointerConv(toDg.next);
|
||||
|
||||
// Retain the old behaviour for this refactoring
|
||||
// Should probably be changed to constant to match function pointers
|
||||
if (m > MATCH.convert)
|
||||
m = MATCH.convert;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
return MATCH.nomatch;
|
||||
|
@ -5516,6 +5547,11 @@ extern (C++) final class TypeIdentifier : TypeQualified
|
|||
this.ident = ident;
|
||||
}
|
||||
|
||||
static TypeIdentifier create(const ref Loc loc, Identifier ident)
|
||||
{
|
||||
return new TypeIdentifier(loc, ident);
|
||||
}
|
||||
|
||||
override const(char)* kind() const
|
||||
{
|
||||
return "identifier";
|
||||
|
@ -5737,7 +5773,7 @@ extern (C++) final class TypeStruct : Type
|
|||
|
||||
override structalign_t alignment()
|
||||
{
|
||||
if (sym.alignment == 0)
|
||||
if (sym.alignment.isUnknown())
|
||||
sym.size(sym.loc);
|
||||
return sym.alignment;
|
||||
}
|
||||
|
@ -6519,6 +6555,29 @@ extern (C++) final class TypeTuple : Type
|
|||
return false;
|
||||
}
|
||||
|
||||
override MATCH implicitConvTo(Type to)
|
||||
{
|
||||
if (this == to)
|
||||
return MATCH.exact;
|
||||
if (auto tt = to.isTypeTuple())
|
||||
{
|
||||
if (arguments.dim == tt.arguments.dim)
|
||||
{
|
||||
MATCH m = MATCH.exact;
|
||||
for (size_t i = 0; i < tt.arguments.dim; i++)
|
||||
{
|
||||
Parameter arg1 = (*arguments)[i];
|
||||
Parameter arg2 = (*tt.arguments)[i];
|
||||
MATCH mi = arg1.type.implicitConvTo(arg2.type);
|
||||
if (mi < m)
|
||||
m = mi;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
return MATCH.nomatch;
|
||||
}
|
||||
|
||||
override void accept(Visitor v)
|
||||
{
|
||||
v.visit(this);
|
||||
|
|
|
@ -224,6 +224,7 @@ public:
|
|||
bool equivalent(Type *t);
|
||||
// kludge for template.isType()
|
||||
DYNCAST dyncast() const { return DYNCAST_TYPE; }
|
||||
size_t getUniqueID() const;
|
||||
Covariant covariant(Type *t, StorageClass *pstc = NULL);
|
||||
const char *toChars() const;
|
||||
char *toPrettyChars(bool QualifyTypes = false);
|
||||
|
@ -446,6 +447,7 @@ public:
|
|||
|
||||
const char *kind();
|
||||
TypeSArray *syntaxCopy();
|
||||
bool isIncomplete();
|
||||
d_uns64 size(const Loc &loc);
|
||||
unsigned alignsize();
|
||||
bool isString();
|
||||
|
@ -582,6 +584,7 @@ struct ParameterList
|
|||
Parameters* parameters;
|
||||
StorageClass stc;
|
||||
VarArg varargs;
|
||||
bool hasIdentifierList; // true if C identifier-list style
|
||||
|
||||
size_t length();
|
||||
Parameter *operator[](size_t i) { return Parameter::getNth(parameters, i); }
|
||||
|
@ -711,6 +714,7 @@ public:
|
|||
Identifier *ident;
|
||||
Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution
|
||||
|
||||
static TypeIdentifier *create(const Loc &loc, Identifier *ident);
|
||||
const char *kind();
|
||||
TypeIdentifier *syntaxCopy();
|
||||
Dsymbol *toDsymbol(Scope *sc);
|
||||
|
|
|
@ -43,7 +43,7 @@ import dmd.tokens;
|
|||
import dmd.visitor;
|
||||
|
||||
import dmd.root.bitarray;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
|
||||
/**********************************
|
||||
* Perform ownership/borrowing checks for funcdecl.
|
||||
|
|
|
@ -38,7 +38,7 @@ import dmd.id;
|
|||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.root.array;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.stringtable;
|
||||
import dmd.target;
|
||||
import dmd.tokens;
|
||||
|
|
|
@ -213,9 +213,13 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE
|
|||
if (isRecursiveAliasThis(e.att1, e.e1.type))
|
||||
return null;
|
||||
//printf("att %s e1 = %s\n", Token::toChars(e.op), e.e1.type.toChars());
|
||||
Expression e1 = new DotIdExp(e.loc, e.e1, ad.aliasthis.ident);
|
||||
BinExp be = cast(BinExp)e.copy();
|
||||
be.e1 = e1;
|
||||
// Resolve 'alias this' but in case of assigment don't resolve properties yet
|
||||
// because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2'
|
||||
bool findOnly = (e.op == TOK.assign);
|
||||
be.e1 = resolveAliasThis(sc, e.e1, true, findOnly);
|
||||
if (!be.e1)
|
||||
return null;
|
||||
|
||||
Expression result;
|
||||
if (be.op == TOK.concatenateAssign)
|
||||
|
@ -237,9 +241,10 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE
|
|||
if (isRecursiveAliasThis(e.att2, e.e2.type))
|
||||
return null;
|
||||
//printf("att %s e2 = %s\n", Token::toChars(e.op), e.e2.type.toChars());
|
||||
Expression e2 = new DotIdExp(e.loc, e.e2, ad.aliasthis.ident);
|
||||
BinExp be = cast(BinExp)e.copy();
|
||||
be.e2 = e2;
|
||||
be.e2 = resolveAliasThis(sc, e.e2, true);
|
||||
if (!be.e2)
|
||||
return null;
|
||||
|
||||
Expression result;
|
||||
if (be.op == TOK.concatenateAssign)
|
||||
|
@ -1744,11 +1749,31 @@ private FuncDeclaration findBestOpApplyMatch(Expression ethis, FuncDeclaration f
|
|||
else if (m == match && m > MATCH.nomatch)
|
||||
{
|
||||
assert(fd_best);
|
||||
/* Ignore covariant matches, as later on it can be redone
|
||||
* after the opApply delegate has its attributes inferred.
|
||||
*/
|
||||
if (tf.covariant(fd_best.type) != Covariant.yes &&
|
||||
fd_best.type.covariant(tf) != Covariant.yes)
|
||||
auto bestTf = fd_best.type.isTypeFunction();
|
||||
assert(bestTf);
|
||||
|
||||
// Found another overload with different attributes?
|
||||
// e.g. @system vs. @safe opApply
|
||||
bool ambig = tf.attributesEqual(bestTf);
|
||||
|
||||
// opApplies with identical attributes could still accept
|
||||
// different function bodies as delegate
|
||||
// => different parameters or attributes
|
||||
if (ambig)
|
||||
{
|
||||
// Fetch the delegates that receive the function body
|
||||
auto tfBody = tf.parameterList[0].type.isTypeDelegate().next;
|
||||
assert(tfBody);
|
||||
|
||||
auto bestBody = bestTf.parameterList[0].type.isTypeDelegate().next;
|
||||
assert(bestBody);
|
||||
|
||||
// Ignore covariant matches, as later on it can be redone
|
||||
// after the opApply delegate has its attributes inferred.
|
||||
ambig = !(tfBody.covariant(bestBody) == Covariant.yes || bestBody.covariant(tfBody) == Covariant.yes);
|
||||
}
|
||||
|
||||
if (ambig)
|
||||
fd_ambig = f; // not covariant, so ambiguous
|
||||
}
|
||||
return 0; // continue
|
||||
|
|
|
@ -697,6 +697,8 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue)
|
|||
// See if we can remove an unnecessary cast
|
||||
ClassDeclaration cdfrom = e.e1.type.isClassHandle();
|
||||
ClassDeclaration cdto = e.type.isClassHandle();
|
||||
if (cdfrom.errors || cdto.errors)
|
||||
return error();
|
||||
if (cdto == ClassDeclaration.object && !cdfrom.isInterfaceDeclaration())
|
||||
goto L1; // can always convert a class to Object
|
||||
// Need to determine correct offset before optimizing away the cast.
|
||||
|
|
|
@ -22,7 +22,7 @@ import dmd.identifier;
|
|||
import dmd.lexer;
|
||||
import dmd.errors;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.root.string;
|
||||
|
@ -556,6 +556,9 @@ class Parser(AST) : Lexer
|
|||
{
|
||||
case TOK.leftParenthesis:
|
||||
{
|
||||
// MixinType
|
||||
if (isDeclaration(&token, NeedDeclaratorId.mustIfDstyle, TOK.reserved, null))
|
||||
goto Ldeclaration;
|
||||
// mixin(string)
|
||||
nextToken();
|
||||
auto exps = parseArguments();
|
||||
|
@ -2954,6 +2957,8 @@ class Parser(AST) : Lexer
|
|||
// Don't call nextToken again.
|
||||
}
|
||||
case TOK.in_:
|
||||
if (global.params.vin)
|
||||
message(scanloc, "Usage of 'in' on parameter");
|
||||
stc = STC.in_;
|
||||
goto L2;
|
||||
|
||||
|
@ -5408,6 +5413,11 @@ class Parser(AST) : Lexer
|
|||
stc = STC.scope_;
|
||||
goto Lagain;
|
||||
|
||||
case TOK.out_:
|
||||
error("cannot declare `out` loop variable, use `ref` instead");
|
||||
stc = STC.out_;
|
||||
goto Lagain;
|
||||
|
||||
case TOK.enum_:
|
||||
stc = STC.manifest;
|
||||
goto Lagain;
|
||||
|
|
|
@ -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();
|
|
@ -59,7 +59,7 @@ extern (C++) final class PrintASTVisitor : Visitor
|
|||
printIndent(indent);
|
||||
|
||||
import dmd.hdrgen : floatToBuffer;
|
||||
import dmd.root.outbuffer : OutBuffer;
|
||||
import dmd.common.outbuffer : OutBuffer;
|
||||
OutBuffer buf;
|
||||
floatToBuffer(e.type, e.value, &buf, false);
|
||||
printf("Real %s %s\n", buf.peekChars(), e.type ? e.type.toChars() : "");
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
| [hash.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d) | Calculate a hash for a byte array |
|
||||
| [longdouble.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/longdouble.d) | 80-bit floating point number implementation in case they are not natively supported |
|
||||
| [man.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/man.d) | Opens an online manual page |
|
||||
| [outbuffer.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d) | An expandable buffer in which you can write text or binary data. |
|
||||
| [port.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d) | Portable routines for functions that have different implementations on different platforms |
|
||||
| [region.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d) | A region allocator |
|
||||
| [response.d](https://github.com/dlang/dmd/blob/master/src/dmd/root/response.d) | Parse command line arguments from response files |
|
||||
|
|
|
@ -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);
|
||||
|
|
@ -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);
|
|
@ -23,410 +23,8 @@ import dmd.root.filename;
|
|||
import dmd.root.rmem;
|
||||
import dmd.root.string;
|
||||
|
||||
/**
|
||||
Encapsulated management of a memory-mapped file.
|
||||
|
||||
Params:
|
||||
Datum = the mapped data type: Use a POD of size 1 for read/write mapping
|
||||
and a `const` version thereof for read-only mapping. Other primitive types
|
||||
should work, but have not been yet tested.
|
||||
*/
|
||||
struct FileMapping(Datum)
|
||||
{
|
||||
static assert(__traits(isPOD, Datum) && Datum.sizeof == 1,
|
||||
"Not tested with other data types yet. Add new types with care.");
|
||||
|
||||
version(Posix) enum invalidHandle = -1;
|
||||
else version(Windows) enum invalidHandle = INVALID_HANDLE_VALUE;
|
||||
|
||||
// state {
|
||||
/// Handle of underlying file
|
||||
private auto handle = invalidHandle;
|
||||
/// File mapping object needed on Windows
|
||||
version(Windows) private HANDLE fileMappingObject = invalidHandle;
|
||||
/// Memory-mapped array
|
||||
private Datum[] data;
|
||||
/// Name of underlying file, zero-terminated
|
||||
private const(char)* name;
|
||||
// state }
|
||||
|
||||
/**
|
||||
Open `filename` and map it in memory. If `Datum` is `const`, opens for
|
||||
read-only and maps the content in memory; no error is issued if the file
|
||||
does not exist. This makes it easy to treat a non-existing file as empty.
|
||||
|
||||
If `Datum` is mutable, opens for read/write (creates file if it does not
|
||||
exist) and fails fatally on any error.
|
||||
|
||||
Due to quirks in `mmap`, if the file is empty, `handle` is valid but `data`
|
||||
is `null`. This state is valid and accounted for.
|
||||
|
||||
Params:
|
||||
filename = the name of the file to be mapped in memory
|
||||
*/
|
||||
this(const char* filename)
|
||||
{
|
||||
version (Posix)
|
||||
{
|
||||
import core.sys.posix.sys.mman;
|
||||
import core.sys.posix.fcntl;
|
||||
|
||||
handle = .open(filename, is(Datum == const) ? O_RDONLY : (O_CREAT | O_RDWR),
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
if (handle == invalidHandle)
|
||||
{
|
||||
static if (is(Datum == const))
|
||||
{
|
||||
// No error, nonexisting file in read mode behaves like an empty file.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "open(\"%s\") failed: %s\n", filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const size = File.size(handle);
|
||||
|
||||
if (size > 0 && size != ulong.max && size <= size_t.max)
|
||||
{
|
||||
auto p = mmap(null, cast(size_t) size, is(Datum == const) ? PROT_READ : PROT_WRITE, MAP_SHARED, handle, 0);
|
||||
if (p == MAP_FAILED)
|
||||
{
|
||||
fprintf(stderr, "mmap(null, %zu) for \"%s\" failed: %s\n", cast(size_t) size, filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
// The cast below will always work because it's gated by the `size <= size_t.max` condition.
|
||||
data = cast(Datum[]) p[0 .. cast(size_t) size];
|
||||
}
|
||||
}
|
||||
else version(Windows)
|
||||
{
|
||||
static if (is(Datum == const))
|
||||
{
|
||||
enum createFileMode = GENERIC_READ;
|
||||
enum openFlags = OPEN_EXISTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum createFileMode = GENERIC_READ | GENERIC_WRITE;
|
||||
enum openFlags = CREATE_ALWAYS;
|
||||
}
|
||||
|
||||
handle = CreateFileA(filename, createFileMode, 0, null, openFlags, FILE_ATTRIBUTE_NORMAL, null);
|
||||
if (handle == invalidHandle)
|
||||
{
|
||||
static if (is(Datum == const))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "CreateFileA() failed for \"%s\": %d\n", filename, GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
createMapping(filename, File.size(handle));
|
||||
}
|
||||
else static assert(0);
|
||||
|
||||
// Save the name for later. Technically there's no need: on Linux one can use readlink on /proc/self/fd/NNN.
|
||||
// On BSD and OSX one can use fcntl with F_GETPATH. On Windows one can use GetFileInformationByHandleEx.
|
||||
// But just saving the name is simplest, fastest, and most portable...
|
||||
import core.stdc.string : strlen;
|
||||
name = filename[0 .. filename.strlen() + 1].idup.ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
Common code factored opportunistically. Windows only. Assumes `handle` is
|
||||
already pointing to an opened file. Initializes the `fileMappingObject`
|
||||
and `data` members.
|
||||
|
||||
Params:
|
||||
filename = the file to be mapped
|
||||
size = the size of the file in bytes
|
||||
*/
|
||||
version(Windows) private void createMapping(const char* filename, ulong size)
|
||||
{
|
||||
assert(size <= size_t.max || size == ulong.max);
|
||||
assert(handle != invalidHandle);
|
||||
assert(data is null);
|
||||
assert(fileMappingObject == invalidHandle);
|
||||
|
||||
if (size == 0 || size == ulong.max)
|
||||
return;
|
||||
|
||||
static if (is(Datum == const))
|
||||
{
|
||||
enum fileMappingFlags = PAGE_READONLY;
|
||||
enum mapViewFlags = FILE_MAP_READ;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum fileMappingFlags = PAGE_READWRITE;
|
||||
enum mapViewFlags = FILE_MAP_WRITE;
|
||||
}
|
||||
|
||||
fileMappingObject = CreateFileMappingA(handle, null, fileMappingFlags, 0, 0, null);
|
||||
if (!fileMappingObject)
|
||||
{
|
||||
fprintf(stderr, "CreateFileMappingA(%p) failed for %llu bytes of \"%s\": %d\n",
|
||||
handle, size, filename, GetLastError());
|
||||
fileMappingObject = invalidHandle; // by convention always use invalidHandle, not null
|
||||
exit(1);
|
||||
}
|
||||
auto p = MapViewOfFile(fileMappingObject, mapViewFlags, 0, 0, 0);
|
||||
if (!p)
|
||||
{
|
||||
fprintf(stderr, "MapViewOfFile() failed for \"%s\": %d\n", filename, GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
data = cast(Datum[]) p[0 .. cast(size_t) size];
|
||||
}
|
||||
|
||||
// Not copyable or assignable (for now).
|
||||
@disable this(const FileMapping!Datum rhs);
|
||||
@disable void opAssign(const ref FileMapping!Datum rhs);
|
||||
|
||||
/**
|
||||
Frees resources associated with this mapping. However, it does not deallocate the name.
|
||||
*/
|
||||
~this() pure nothrow
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
fakePure({
|
||||
version (Posix)
|
||||
{
|
||||
import core.sys.posix.sys.mman : munmap;
|
||||
|
||||
// Cannot call fprintf from inside a destructor, so exiting silently.
|
||||
|
||||
if (data.ptr && munmap(cast(void*) data.ptr, data.length) != 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
data = null;
|
||||
if (handle != invalidHandle && .close(handle) != 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
handle = invalidHandle;
|
||||
}
|
||||
else version(Windows)
|
||||
{
|
||||
if (data.ptr !is null && UnmapViewOfFile(cast(void*) data.ptr) == 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
data = null;
|
||||
if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
fileMappingObject = invalidHandle;
|
||||
if (handle != invalidHandle && CloseHandle(handle) == 0)
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
handle = invalidHandle;
|
||||
}
|
||||
else static assert(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the zero-terminated file name associated with the mapping. Can
|
||||
be saved beyond the lifetime of `this`.
|
||||
*/
|
||||
const(char)* filename() const pure @nogc @safe nothrow { return name; }
|
||||
|
||||
/**
|
||||
Frees resources associated with this mapping. However, it does not deallocate the name.
|
||||
Reinitializes `this` as a fresh object that can be reused.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
__dtor();
|
||||
handle = invalidHandle;
|
||||
version(Windows) fileMappingObject = invalidHandle;
|
||||
data = null;
|
||||
name = null;
|
||||
}
|
||||
|
||||
/**
|
||||
Deletes the underlying file and frees all resources associated.
|
||||
Reinitializes `this` as a fresh object that can be reused.
|
||||
|
||||
This function does not abort if the file cannot be deleted, but does print
|
||||
a message on `stderr` and returns `false` to the caller. The underlying
|
||||
rationale is to give the caller the option to continue execution if
|
||||
deleting the file is not important.
|
||||
|
||||
Returns: `true` iff the file was successfully deleted. If the file was not
|
||||
deleted, prints a message to `stderr` and returns `false`.
|
||||
*/
|
||||
static if (!is(Datum == const))
|
||||
bool discard()
|
||||
{
|
||||
// Truncate file to zero so unflushed buffers are not flushed unnecessarily.
|
||||
resize(0);
|
||||
auto deleteme = name;
|
||||
close();
|
||||
// In-memory resource freed, now get rid of the underlying temp file.
|
||||
version(Posix)
|
||||
{
|
||||
import core.sys.posix.unistd;
|
||||
if (unlink(deleteme) != 0)
|
||||
{
|
||||
fprintf(stderr, "unlink(\"%s\") failed: %s\n", filename, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else version(Windows)
|
||||
{
|
||||
import core.sys.windows.winbase;
|
||||
if (DeleteFileA(deleteme) == 0)
|
||||
{
|
||||
fprintf(stderr, "DeleteFileA error %d\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else static assert(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
Queries whether `this` is currently associated with a file.
|
||||
|
||||
Returns: `true` iff there is an active mapping.
|
||||
*/
|
||||
bool active() const pure @nogc nothrow
|
||||
{
|
||||
return handle !is invalidHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
Queries the length of the file associated with this mapping. If not
|
||||
active, returns 0.
|
||||
|
||||
Returns: the length of the file, or 0 if no file associated.
|
||||
*/
|
||||
size_t length() const pure @nogc @safe nothrow { return data.length; }
|
||||
|
||||
/**
|
||||
Get a slice to the contents of the entire file.
|
||||
|
||||
Returns: the contents of the file. If not active, returns the `null` slice.
|
||||
*/
|
||||
auto opSlice() pure @nogc @safe nothrow { return data; }
|
||||
|
||||
/**
|
||||
Resizes the file and mapping to the specified `size`.
|
||||
|
||||
Params:
|
||||
size = new length requested
|
||||
*/
|
||||
static if (!is(Datum == const))
|
||||
void resize(size_t size) pure
|
||||
{
|
||||
assert(handle != invalidHandle);
|
||||
fakePure({
|
||||
version(Posix)
|
||||
{
|
||||
import core.sys.posix.unistd : ftruncate;
|
||||
import core.sys.posix.sys.mman;
|
||||
|
||||
if (data.length)
|
||||
{
|
||||
assert(data.ptr, "Corrupt memory mapping");
|
||||
// assert(0) here because it would indicate an internal error
|
||||
munmap(cast(void*) data.ptr, data.length) == 0 || assert(0);
|
||||
data = null;
|
||||
}
|
||||
if (ftruncate(handle, size) != 0)
|
||||
{
|
||||
fprintf(stderr, "ftruncate() failed for \"%s\": %s\n", filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
if (size > 0)
|
||||
{
|
||||
auto p = mmap(null, size, PROT_WRITE, MAP_SHARED, handle, 0);
|
||||
if (cast(ssize_t) p == -1)
|
||||
{
|
||||
fprintf(stderr, "mmap() failed for \"%s\": %s\n", filename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
data = cast(Datum[]) p[0 .. size];
|
||||
}
|
||||
}
|
||||
else version(Windows)
|
||||
{
|
||||
// Per documentation, must unmap first.
|
||||
if (data.length > 0 && UnmapViewOfFile(cast(void*) data.ptr) == 0)
|
||||
{
|
||||
fprintf(stderr, "UnmapViewOfFile(%p) failed for memory mapping of \"%s\": %d\n",
|
||||
data.ptr, filename, GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
data = null;
|
||||
if (fileMappingObject != invalidHandle && CloseHandle(fileMappingObject) == 0)
|
||||
{
|
||||
fprintf(stderr, "CloseHandle() failed for memory mapping of \"%s\": %d\n", filename, GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
fileMappingObject = invalidHandle;
|
||||
LARGE_INTEGER biggie;
|
||||
biggie.QuadPart = size;
|
||||
if (SetFilePointerEx(handle, biggie, null, FILE_BEGIN) == 0 || SetEndOfFile(handle) == 0)
|
||||
{
|
||||
fprintf(stderr, "SetFilePointer() failed for \"%s\": %d\n", filename, GetLastError());
|
||||
exit(1);
|
||||
}
|
||||
createMapping(name, size);
|
||||
}
|
||||
else static assert(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
Unconditionally and destructively moves the underlying file to `filename`.
|
||||
If the operation succeds, returns true. Upon failure, prints a message to
|
||||
`stderr` and returns `false`.
|
||||
|
||||
Params: filename = zero-terminated name of the file to move to.
|
||||
|
||||
Returns: `true` iff the operation was successful.
|
||||
*/
|
||||
bool moveToFile(const char* filename)
|
||||
{
|
||||
auto oldname = name;
|
||||
|
||||
close();
|
||||
// Rename the underlying file to the target, no copy necessary.
|
||||
version(Posix)
|
||||
{
|
||||
if (.rename(oldname, filename) != 0)
|
||||
{
|
||||
fprintf(stderr, "rename(\"%s\", \"%s\") failed: %s\n", oldname, filename, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else version(Windows)
|
||||
{
|
||||
import core.sys.windows.winbase;
|
||||
if (MoveFileExA(oldname, filename, MOVEFILE_REPLACE_EXISTING) == 0)
|
||||
{
|
||||
fprintf(stderr, "MoveFileExA(\"%s\", \"%s\") failed: %d\n", oldname, filename, GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else static assert(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
import dmd.common.file;
|
||||
import dmd.common.string;
|
||||
|
||||
/// Owns a (rmem-managed) file buffer.
|
||||
struct FileBuffer
|
||||
|
@ -585,58 +183,8 @@ nothrow:
|
|||
/// Write a file, returning `true` on success.
|
||||
extern (D) static bool write(const(char)* name, const void[] data)
|
||||
{
|
||||
version (Posix)
|
||||
{
|
||||
ssize_t numwritten;
|
||||
int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, (6 << 6) | (4 << 3) | 4);
|
||||
if (fd == -1)
|
||||
goto err;
|
||||
numwritten = .write(fd, data.ptr, data.length);
|
||||
if (numwritten != data.length)
|
||||
goto err2;
|
||||
if (close(fd) == -1)
|
||||
goto err;
|
||||
return true;
|
||||
err2:
|
||||
close(fd);
|
||||
.remove(name);
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
else version (Windows)
|
||||
{
|
||||
DWORD numwritten; // here because of the gotos
|
||||
const nameStr = name.toDString;
|
||||
// work around Windows file path length limitation
|
||||
// (see documentation for extendedPathThen).
|
||||
HANDLE h = nameStr.extendedPathThen!
|
||||
(p => CreateFileW(p.ptr,
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
null,
|
||||
CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
|
||||
null));
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
goto err;
|
||||
|
||||
if (WriteFile(h, data.ptr, cast(DWORD)data.length, &numwritten, null) != TRUE)
|
||||
goto err2;
|
||||
if (numwritten != data.length)
|
||||
goto err2;
|
||||
if (!CloseHandle(h))
|
||||
goto err;
|
||||
return true;
|
||||
err2:
|
||||
CloseHandle(h);
|
||||
nameStr.extendedPathThen!(p => DeleteFileW(p.ptr));
|
||||
err:
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
static assert(0);
|
||||
}
|
||||
import dmd.common.file : writeFile;
|
||||
return writeFile(name, data);
|
||||
}
|
||||
|
||||
///ditto
|
||||
|
@ -717,42 +265,6 @@ nothrow:
|
|||
return update(name, data[0 .. size]);
|
||||
}
|
||||
|
||||
/// Touch a file to current date
|
||||
static bool touch(const char* namez)
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
FILETIME ft = void;
|
||||
SYSTEMTIME st = void;
|
||||
GetSystemTime(&st);
|
||||
SystemTimeToFileTime(&st, &ft);
|
||||
|
||||
import core.stdc.string : strlen;
|
||||
|
||||
// get handle to file
|
||||
HANDLE h = namez[0 .. namez.strlen()].extendedPathThen!(p => CreateFile(p.ptr,
|
||||
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
null, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, null));
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
const f = SetFileTime(h, null, null, &ft); // set last write time
|
||||
|
||||
if (!CloseHandle(h))
|
||||
return false;
|
||||
|
||||
return f != 0;
|
||||
}
|
||||
else version (Posix)
|
||||
{
|
||||
import core.sys.posix.utime;
|
||||
return utime(namez, null) == 0;
|
||||
}
|
||||
else
|
||||
static assert(0);
|
||||
}
|
||||
|
||||
/// Size of a file in bytes.
|
||||
/// Params: namez = null-terminated filename
|
||||
/// Returns: `ulong.max` on any error, the length otherwise.
|
||||
|
@ -777,38 +289,5 @@ nothrow:
|
|||
// Error cases go here.
|
||||
return ulong.max;
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
version (Posix)
|
||||
static ulong size(int fd)
|
||||
{
|
||||
stat_t buf;
|
||||
if (fstat(fd, &buf) == 0)
|
||||
return buf.st_size;
|
||||
return ulong.max;
|
||||
}
|
||||
|
||||
/// Ditto
|
||||
version (Windows)
|
||||
static ulong size(HANDLE fd)
|
||||
{
|
||||
ulong result;
|
||||
if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0)
|
||||
return result;
|
||||
return ulong.max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Runs a non-pure function or delegate as pure code. Use with caution.
|
||||
|
||||
Params:
|
||||
fun = the delegate to run, usually inlined: `fakePure({ ... });`
|
||||
|
||||
Returns: whatever `fun` returns.
|
||||
*/
|
||||
private auto ref fakePure(F)(scope F fun) pure
|
||||
{
|
||||
mixin("alias PureFun = " ~ F.stringof ~ " pure;");
|
||||
return (cast(PureFun) fun)();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ import core.stdc.errno;
|
|||
import core.stdc.string;
|
||||
import dmd.root.array;
|
||||
import dmd.root.file;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.common.file;
|
||||
import dmd.root.port;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
|
@ -1123,78 +1124,13 @@ version(Windows)
|
|||
*/
|
||||
private int _mkdir(const(char)[] path) nothrow
|
||||
{
|
||||
import dmd.common.string : extendedPathThen;
|
||||
const createRet = path.extendedPathThen!(
|
||||
p => CreateDirectoryW(&p[0], null /*securityAttributes*/));
|
||||
// different conventions for CreateDirectory and mkdir
|
||||
return createRet == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
/**************************************
|
||||
* Converts a path to one suitable to be passed to Win32 API
|
||||
* functions that can deal with paths longer than 248
|
||||
* characters then calls the supplied function on it.
|
||||
*
|
||||
* Params:
|
||||
* path = The Path to call F on.
|
||||
*
|
||||
* Returns:
|
||||
* The result of calling F on path.
|
||||
*
|
||||
* References:
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
*/
|
||||
package auto extendedPathThen(alias F)(const(char)[] path)
|
||||
{
|
||||
if (!path.length)
|
||||
return F((wchar[]).init);
|
||||
return path.toWStringzThen!((wpath)
|
||||
{
|
||||
// GetFullPathNameW expects a sized buffer to store the result in. Since we don't
|
||||
// know how large it has to be, we pass in null and get the needed buffer length
|
||||
// as the return code.
|
||||
const pathLength = GetFullPathNameW(&wpath[0],
|
||||
0 /*length8*/,
|
||||
null /*output buffer*/,
|
||||
null /*filePartBuffer*/);
|
||||
if (pathLength == 0)
|
||||
{
|
||||
return F((wchar[]).init);
|
||||
}
|
||||
|
||||
// wpath is the UTF16 version of path, but to be able to use
|
||||
// extended paths, we need to prefix with `\\?\` and the absolute
|
||||
// path.
|
||||
static immutable prefix = `\\?\`w;
|
||||
|
||||
// prefix only needed for long names and non-UNC names
|
||||
const needsPrefix = pathLength >= MAX_PATH && (wpath[0] != '\\' || wpath[1] != '\\');
|
||||
const prefixLength = needsPrefix ? prefix.length : 0;
|
||||
|
||||
// +1 for the null terminator
|
||||
const bufferLength = pathLength + prefixLength + 1;
|
||||
|
||||
wchar[1024] absBuf = void;
|
||||
wchar[] absPath = bufferLength > absBuf.length
|
||||
? new wchar[bufferLength] : absBuf[0 .. bufferLength];
|
||||
|
||||
absPath[0 .. prefixLength] = prefix[0 .. prefixLength];
|
||||
|
||||
const absPathRet = GetFullPathNameW(&wpath[0],
|
||||
cast(uint)(absPath.length - prefixLength - 1),
|
||||
&absPath[prefixLength],
|
||||
null /*filePartBuffer*/);
|
||||
|
||||
if (absPathRet == 0 || absPathRet > absPath.length - prefixLength)
|
||||
{
|
||||
return F((wchar[]).init);
|
||||
}
|
||||
|
||||
absPath[$ - 1] = '\0';
|
||||
// Strip null terminator from the slice
|
||||
return F(absPath[0 .. $ - 1]);
|
||||
});
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* Converts a UTF-16 string to a (null-terminated) narrow string.
|
||||
* Returns:
|
||||
|
@ -1222,33 +1158,6 @@ version(Windows)
|
|||
return newBuffer[0 .. length];
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* Converts a narrow string to a (null-terminated) UTF-16 string.
|
||||
* Returns:
|
||||
* If `buffer` is specified and the result fits, a slice of that buffer,
|
||||
* otherwise a new buffer which can be released via `mem.xfree()`.
|
||||
* Nulls are propagated, i.e., if `narrow` is null, the returned slice is
|
||||
* null too.
|
||||
*/
|
||||
wchar[] toWStringz(const(char)[] narrow, wchar[] buffer = null) nothrow
|
||||
{
|
||||
if (narrow is null)
|
||||
return null;
|
||||
|
||||
const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length);
|
||||
if (requiredLength < buffer.length)
|
||||
{
|
||||
buffer[requiredLength] = 0;
|
||||
return buffer[0 .. requiredLength];
|
||||
}
|
||||
|
||||
wchar* newBuffer = cast(wchar*) mem.xmalloc_noscan((requiredLength + 1) * wchar.sizeof);
|
||||
const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, newBuffer, requiredLength);
|
||||
assert(length == requiredLength);
|
||||
newBuffer[length] = 0;
|
||||
return newBuffer[0 .. length];
|
||||
}
|
||||
|
||||
/**********************************
|
||||
* Converts a slice of UTF-8 characters to an array of wchar that's null
|
||||
* terminated so it can be passed to Win32 APIs then calls the supplied
|
||||
|
@ -1262,9 +1171,12 @@ version(Windows)
|
|||
*/
|
||||
private auto toWStringzThen(alias F)(const(char)[] str) nothrow
|
||||
{
|
||||
import dmd.common.string : SmallBuffer, toWStringz;
|
||||
|
||||
if (!str.length) return F(""w.ptr);
|
||||
|
||||
wchar[1024] buf = void;
|
||||
wchar[1024] support = void;
|
||||
auto buf = SmallBuffer!wchar(support.length, support);
|
||||
wchar[] wide = toWStringz(str, buf);
|
||||
scope(exit) wide.ptr != buf.ptr && mem.xfree(wide.ptr);
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -13,7 +13,7 @@ module dmd.root.rootobject;
|
|||
|
||||
import core.stdc.stdio;
|
||||
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
|
||||
/***********************************************************
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
@ -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();
|
||||
};
|
|
@ -89,7 +89,7 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg)
|
|||
|
||||
if (hasPointers && v.type.toBasetype().ty != Tstruct)
|
||||
{
|
||||
if ((ad.type.alignment() < target.ptrsize ||
|
||||
if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize ||
|
||||
(v.offset & (target.ptrsize - 1))) &&
|
||||
sc.func.setUnsafe())
|
||||
{
|
||||
|
|
|
@ -53,7 +53,7 @@ import dmd.objc;
|
|||
import dmd.opover;
|
||||
import dmd.parse;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.sideeffect;
|
||||
|
@ -363,7 +363,7 @@ private extern(C++) final class Semantic2Visitor : Visitor
|
|||
assert(fd.semanticRun <= PASS.semantic2);
|
||||
fd.semanticRun = PASS.semantic2;
|
||||
|
||||
//printf("FuncDeclaration::semantic2 [%s] fd0 = %s %s\n", loc.toChars(), toChars(), type.toChars());
|
||||
//printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr);
|
||||
|
||||
// Only check valid functions which have a body to avoid errors
|
||||
// for multiple declarations, e.g.
|
||||
|
|
|
@ -55,7 +55,7 @@ import dmd.objc;
|
|||
import dmd.opover;
|
||||
import dmd.parse;
|
||||
import dmd.root.filename;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.sideeffect;
|
||||
|
@ -407,7 +407,8 @@ private extern(C++) final class Semantic3Visitor : Visitor
|
|||
sc2.insert(_arguments);
|
||||
_arguments.parent = funcdecl;
|
||||
}
|
||||
if (f.linkage == LINK.d || f.parameterList.length)
|
||||
if ((f.linkage == LINK.d || f.parameterList.length) &&
|
||||
!(sc.flags & SCOPE.Cfile)) // don't want to require importing stdarg for C files
|
||||
{
|
||||
// Declare _argptr
|
||||
Type t = target.va_listType(funcdecl.loc, sc);
|
||||
|
@ -598,7 +599,10 @@ private extern(C++) final class Semantic3Visitor : Visitor
|
|||
f.next = Type.tvoid;
|
||||
if (f.checkRetType(funcdecl.loc))
|
||||
funcdecl.fbody = new ErrorStatement();
|
||||
else if (funcdecl.isMain())
|
||||
funcdecl.checkDmain(); // Check main() parameters and return type
|
||||
}
|
||||
|
||||
if (global.params.vcomplex && f.next !is null)
|
||||
f.next.checkComplexTransition(funcdecl.loc, sc);
|
||||
|
||||
|
@ -777,8 +781,14 @@ private extern(C++) final class Semantic3Visitor : Visitor
|
|||
}
|
||||
assert(!funcdecl.returnLabel);
|
||||
}
|
||||
else if (f.next.ty == Tnoreturn)
|
||||
else if (f.next.toBasetype().ty == Tnoreturn)
|
||||
{
|
||||
// Fallthrough despite being declared as noreturn? return is already rejected when evaluating the ReturnStatement
|
||||
if (blockexit & BE.fallthru)
|
||||
{
|
||||
funcdecl.error("is typed as `%s` but does return", f.next.toChars());
|
||||
funcdecl.loc.errorSupplemental("`noreturn` functions must either throw, abort or loop indefinitely");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1571,7 +1581,7 @@ private struct FuncDeclSem3
|
|||
}
|
||||
}
|
||||
|
||||
private void semanticTypeInfoMembers(StructDeclaration sd)
|
||||
extern (C++) void semanticTypeInfoMembers(StructDeclaration sd)
|
||||
{
|
||||
if (sd.xeq &&
|
||||
sd.xeq._scope &&
|
||||
|
|
|
@ -39,7 +39,7 @@ import dmd.id;
|
|||
import dmd.identifier;
|
||||
import dmd.dinterpret;
|
||||
import dmd.mtype;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rootobject;
|
||||
import dmd.sapply;
|
||||
import dmd.sideeffect;
|
||||
|
@ -463,7 +463,7 @@ extern (C++) class ExpStatement : Statement
|
|||
this.exp = new DeclarationExp(loc, declaration);
|
||||
}
|
||||
|
||||
static ExpStatement create(Loc loc, Expression exp)
|
||||
static ExpStatement create(const ref Loc loc, Expression exp)
|
||||
{
|
||||
return new ExpStatement(loc, exp);
|
||||
}
|
||||
|
@ -577,7 +577,7 @@ extern (C++) class CompoundStatement : Statement
|
|||
statements.push(s);
|
||||
}
|
||||
|
||||
static CompoundStatement create(Loc loc, Statement s1, Statement s2)
|
||||
static CompoundStatement create(const ref Loc loc, Statement s1, Statement s2)
|
||||
{
|
||||
return new CompoundStatement(loc, s1, s2);
|
||||
}
|
||||
|
@ -1635,7 +1635,7 @@ extern (C++) final class TryFinallyStatement : Statement
|
|||
this.bodyFallsThru = true; // assume true until statementSemantic()
|
||||
}
|
||||
|
||||
static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody)
|
||||
static TryFinallyStatement create(const ref Loc loc, Statement _body, Statement finalbody)
|
||||
{
|
||||
return new TryFinallyStatement(loc, _body, finalbody);
|
||||
}
|
||||
|
|
|
@ -186,7 +186,7 @@ class ExpStatement : public Statement
|
|||
public:
|
||||
Expression *exp;
|
||||
|
||||
static ExpStatement *create(Loc loc, Expression *exp);
|
||||
static ExpStatement *create(const Loc &loc, Expression *exp);
|
||||
ExpStatement *syntaxCopy();
|
||||
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
@ -218,7 +218,7 @@ class CompoundStatement : public Statement
|
|||
public:
|
||||
Statements *statements;
|
||||
|
||||
static CompoundStatement *create(Loc loc, Statement *s1, Statement *s2);
|
||||
static CompoundStatement *create(const Loc &loc, Statement *s1, Statement *s2);
|
||||
CompoundStatement *syntaxCopy();
|
||||
ReturnStatement *endsWithReturnStatement();
|
||||
Statement *last();
|
||||
|
@ -615,7 +615,7 @@ public:
|
|||
Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion
|
||||
bool bodyFallsThru; // true if _body falls through to finally
|
||||
|
||||
static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody);
|
||||
static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody);
|
||||
TryFinallyStatement *syntaxCopy();
|
||||
bool hasBreak() const;
|
||||
bool hasContinue() const;
|
||||
|
|
|
@ -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 *) { }
|
||||
};
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@ import dmd.globals;
|
|||
import dmd.identifier;
|
||||
import dmd.mtype;
|
||||
import dmd.root.array;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.tokens;
|
||||
|
||||
|
||||
|
|
|
@ -316,7 +316,8 @@ struct TargetC
|
|||
enum BitFieldStyle : ubyte
|
||||
{
|
||||
Unspecified,
|
||||
Dm_Ms, /// Digital Mars and Microsoft C compilers
|
||||
DM, /// Digital Mars 32 bit C compiler
|
||||
MS, /// Microsoft 32 and 64 bit C compilers
|
||||
/// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160
|
||||
/// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160
|
||||
Gcc_Clang, /// gcc and clang
|
||||
|
|
|
@ -63,7 +63,8 @@ struct TargetC
|
|||
enum class BitFieldStyle : unsigned char
|
||||
{
|
||||
Unspecified,
|
||||
Dm_Ms, // Digital Mars and Microsoft C compilers
|
||||
DM, // Digital Mars 32 bit C compiler
|
||||
MS, // Microsoft 32 and 64 bit C compilers
|
||||
// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160
|
||||
// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160
|
||||
Gcc_Clang, // gcc and clang
|
||||
|
|
|
@ -131,7 +131,7 @@ public:
|
|||
virtual bool declareParameter(Scope *sc) = 0;
|
||||
virtual void print(RootObject *oarg, RootObject *oded) = 0;
|
||||
virtual RootObject *specialization() = 0;
|
||||
virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0;
|
||||
virtual RootObject *defaultArg(const Loc &instLoc, Scope *sc) = 0;
|
||||
virtual bool hasDefaultArg() = 0;
|
||||
|
||||
/* Create dummy argument based on parameter.
|
||||
|
@ -154,7 +154,7 @@ public:
|
|||
bool declareParameter(Scope *sc);
|
||||
void print(RootObject *oarg, RootObject *oded);
|
||||
RootObject *specialization();
|
||||
RootObject *defaultArg(Loc instLoc, Scope *sc);
|
||||
RootObject *defaultArg(const Loc &instLoc, Scope *sc);
|
||||
bool hasDefaultArg();
|
||||
RootObject *dummyArg();
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
@ -186,7 +186,7 @@ public:
|
|||
bool declareParameter(Scope *sc);
|
||||
void print(RootObject *oarg, RootObject *oded);
|
||||
RootObject *specialization();
|
||||
RootObject *defaultArg(Loc instLoc, Scope *sc);
|
||||
RootObject *defaultArg(const Loc &instLoc, Scope *sc);
|
||||
bool hasDefaultArg();
|
||||
RootObject *dummyArg();
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
@ -207,7 +207,7 @@ public:
|
|||
bool declareParameter(Scope *sc);
|
||||
void print(RootObject *oarg, RootObject *oded);
|
||||
RootObject *specialization();
|
||||
RootObject *defaultArg(Loc instLoc, Scope *sc);
|
||||
RootObject *defaultArg(const Loc &instLoc, Scope *sc);
|
||||
bool hasDefaultArg();
|
||||
RootObject *dummyArg();
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
@ -224,7 +224,7 @@ public:
|
|||
bool declareParameter(Scope *sc);
|
||||
void print(RootObject *oarg, RootObject *oded);
|
||||
RootObject *specialization();
|
||||
RootObject *defaultArg(Loc instLoc, Scope *sc);
|
||||
RootObject *defaultArg(const Loc &instLoc, Scope *sc);
|
||||
bool hasDefaultArg();
|
||||
RootObject *dummyArg();
|
||||
void accept(Visitor *v) { v->visit(this); }
|
||||
|
|
|
@ -19,7 +19,7 @@ import core.stdc.string;
|
|||
import dmd.globals;
|
||||
import dmd.identifier;
|
||||
import dmd.root.ctfloat;
|
||||
import dmd.root.outbuffer;
|
||||
import dmd.common.outbuffer;
|
||||
import dmd.root.rmem;
|
||||
import dmd.utf;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue