diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2021-12-05 17:11:12 +0100 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2021-12-09 00:58:58 +0100 |
commit | 0fb57034770aa20adced4d176f34ca611c2945bf (patch) | |
tree | 1f5735c8b4f25aa4a290e5ae8124713c24f98359 /gcc | |
parent | c15aa46cca0649b68613d3292cf71c7cc57ef78f (diff) | |
download | gcc-0fb57034770aa20adced4d176f34ca611c2945bf.zip gcc-0fb57034770aa20adced4d176f34ca611c2945bf.tar.gz gcc-0fb57034770aa20adced4d176f34ca611c2945bf.tar.bz2 |
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.
Diffstat (limited to 'gcc')
191 files changed, 7386 insertions, 3141 deletions
diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 4ce11e3..d7f7147 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -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) diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc index ab3a950..ea8e1ed 100644 --- a/gcc/d/d-builtins.cc +++ b/gcc/d/d-builtins.cc @@ -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++) diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index 403e3c7..c082ac5 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -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 diff --git a/gcc/d/d-frontend.cc b/gcc/d/d-frontend.cc index 522095f..b2e52c0 100644 --- a/gcc/d/d-frontend.cc +++ b/gcc/d/d-frontend.cc @@ -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); +} diff --git a/gcc/d/d-gimplify.cc b/gcc/d/d-gimplify.cc index 0fa7442..e366881 100644 --- a/gcc/d/d-gimplify.cc +++ b/gcc/d/d-gimplify.cc @@ -120,52 +120,47 @@ d_gimplify_addr_expr (tree *expr_p) static gimplify_status d_gimplify_call_expr (tree *expr_p, gimple_seq *pre_p) { - if (CALL_EXPR_ARGS_ORDERED (*expr_p)) - { - /* Strictly evaluate all arguments from left to right. */ - int nargs = call_expr_nargs (*expr_p); - location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location); + /* Strictly evaluate all arguments from left to right. */ + int nargs = call_expr_nargs (*expr_p); + location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location); - /* No need to enforce evaluation order if only one argument. */ - if (nargs < 2) - return GS_UNHANDLED; + /* No need to enforce evaluation order if only one argument. */ + if (nargs < 2) + return GS_UNHANDLED; - /* Or if all arguments are already free of side-effects. */ - bool has_side_effects = false; - for (int i = 0; i < nargs; i++) + /* Or if all arguments are already free of side-effects. */ + bool has_side_effects = false; + for (int i = 0; i < nargs; i++) + { + if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i))) { - if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i))) - { - has_side_effects = true; - break; - } + has_side_effects = true; + break; } + } - if (!has_side_effects) - return GS_UNHANDLED; - - /* Leave the last argument for gimplify_call_expr. */ - for (int i = 0; i < nargs - 1; i++) - { - tree new_arg = CALL_EXPR_ARG (*expr_p, i); + if (!has_side_effects) + return GS_UNHANDLED; - /* If argument has a side-effect, gimplify_arg will handle it. */ - if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR) - return GS_ERROR; + /* Leave the last argument for gimplify_call_expr. */ + for (int i = 0; i < nargs - 1; i++) + { + tree new_arg = CALL_EXPR_ARG (*expr_p, i); - /* Even if an argument itself doesn't have any side-effects, it - might be altered by another argument in the list. */ - if (new_arg == CALL_EXPR_ARG (*expr_p, i) - && !really_constant_p (new_arg)) - new_arg = get_formal_tmp_var (new_arg, pre_p); + /* If argument has a side-effect, gimplify_arg will handle it. */ + if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR) + return GS_ERROR; - CALL_EXPR_ARG (*expr_p, i) = new_arg; - } + /* Even if an argument itself doesn't have any side-effects, it + might be altered by another argument in the list. */ + if (new_arg == CALL_EXPR_ARG (*expr_p, i) + && !really_constant_p (new_arg)) + new_arg = get_formal_tmp_var (new_arg, pre_p); - return GS_OK; + CALL_EXPR_ARG (*expr_p, i) = new_arg; } - return GS_UNHANDLED; + return GS_OK; } /* Gimplify an UNSIGNED_RSHIFT_EXPR node. */ diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index dbf7a8b..576eefc 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -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; diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h index 328b6b8..a5970d8 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -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) diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index e28a581..a4976b6 100644 --- a/gcc/d/decl.cc +++ b/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; } diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 129050b..d23e1fe 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -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. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index 720d256..3cb7e12 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -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 | |---------------------------------------------------------------------------------|---------------------------------------------------------------| diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index 64ce79a..fa5940e 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.097.2 +v2.098.0 diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d index cff4b9f..1fe8e80 100644 --- a/gcc/d/dmd/aggregate.d +++ b/gcc/d/dmd/aggregate.d @@ -21,6 +21,7 @@ import dmd.aliasthis; import dmd.apply; import dmd.arraytypes; import dmd.astenums; +import dmd.attrib; import dmd.declaration; import dmd.dscope; import dmd.dstruct; @@ -115,11 +116,12 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol AliasThis aliasthis; /// forward unresolved lookups to aliasthis - DtorDeclarations dtors; /// Array of destructors - DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors - DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D + DtorDeclarations userDtors; /// user-defined destructors (`~this()`) - mixins can yield multiple ones + DtorDeclaration aggrDtor; /// aggregate destructor calling userDtors and fieldDtor (and base class aggregate dtor for C++ classes) + DtorDeclaration dtor; /// the aggregate destructor exposed as `__xdtor` alias + /// (same as aggrDtor, except for C++ classes with virtual dtor on Windows) DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI) - FuncDeclaration fieldDtor; /// aggregate destructor for just the fields + DtorDeclaration fieldDtor; /// function destructing (non-inherited) fields Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this) @@ -177,7 +179,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol * Returns: * false if failed to determine the size. */ - final bool determineSize(Loc loc) + final bool determineSize(const ref Loc loc) { //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); @@ -331,7 +333,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol * false if any errors occur. * Otherwise, returns true and the missing arguments will be pushed in elements[]. */ - final bool fill(Loc loc, Expressions* elements, bool ctorinit) + final bool fill(const ref Loc loc, Expressions* elements, bool ctorinit) { //printf("AggregateDeclaration::fill() %s\n", toChars()); assert(sizeok == Sizeok.done); @@ -482,45 +484,52 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol * Align sizes of 0, as we may not know array sizes yet. * Params: * alignment = struct alignment that is in effect - * size = alignment requirement of field + * memalignsize = natural alignment of field * poffset = pointer to offset to be aligned */ - extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe + extern (D) static void alignmember(structalign_t alignment, uint memalignsize, uint* poffset) pure nothrow @safe { - //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset); - switch (alignment) - { - case cast(structalign_t)1: - // No alignment - break; + //debug printf("alignment = %u %d, size = %u, offset = %u\n", alignment.get(), alignment.isPack(), memalignsize, *poffset); + uint alignvalue; - case cast(structalign_t)STRUCTALIGN_DEFAULT: + if (alignment.isDefault()) + { // Alignment in Target::fieldalignsize must match what the // corresponding C compiler's default alignment behavior is. - assert(size > 0 && !(size & (size - 1))); - *poffset = (*poffset + size - 1) & ~(size - 1); - break; - - default: + alignvalue = memalignsize; + } + else if (alignment.isPack()) // #pragma pack semantics + { + alignvalue = alignment.get(); + if (memalignsize < alignvalue) + alignvalue = memalignsize; // align to min(memalignsize, alignment) + } + else if (alignment.get() > 1) + { // Align on alignment boundary, which must be a positive power of 2 - assert(alignment > 0 && !(alignment & (alignment - 1))); - *poffset = (*poffset + alignment - 1) & ~(alignment - 1); - break; + alignvalue = alignment.get(); } + else + return; + + assert(alignvalue > 0 && !(alignvalue & (alignvalue - 1))); + *poffset = (*poffset + alignvalue - 1) & ~(alignvalue - 1); } /**************************************** - * Place a member (mem) into an aggregate (agg), which can be a struct, union or class + * Place a field (mem) into an aggregate (agg), which can be a struct, union or class + * Params: + * nextoffset = location just past the end of the previous field in the aggregate. + * Updated to be just past the end of this field to be placed, i.e. the future nextoffset + * memsize = size of field + * memalignsize = natural alignment of field + * alignment = alignment in effect for this field + * paggsize = size of aggregate (updated) + * paggalignsize = alignment of aggregate (updated) + * isunion = the aggregate is a union * Returns: - * offset to place field at + * aligned offset to place field at * - * nextoffset: next location in aggregate - * memsize: size of member - * memalignsize: natural alignment of member - * alignment: alignment in effect for this member - * paggsize: size of aggregate (updated) - * paggalignsize: alignment of aggregate (updated) - * isunion: the aggregate is a union */ extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize, structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion) @@ -528,7 +537,8 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol uint ofs = *nextoffset; const uint actualAlignment = - alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment; + alignment.isDefault() || alignment.isPack() && memalignsize < alignment.get() + ? memalignsize : alignment.get(); // Ensure no overflow bool overflow; @@ -536,7 +546,10 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol addu(ofs, sz, overflow); if (overflow) assert(0); - alignmember(alignment, memalignsize, &ofs); + // Skip no-op for noreturn without custom aligment + if (memsize != 0 || !alignment.isDefault()) + alignmember(alignment, memalignsize, &ofs); + uint memoffset = ofs; ofs += memsize; if (ofs > *paggsize) diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h index f8d2f45..48e5f4a 100644 --- a/gcc/d/dmd/aggregate.h +++ b/gcc/d/dmd/aggregate.h @@ -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); diff --git a/gcc/d/dmd/aliasthis.d b/gcc/d/dmd/aliasthis.d index 81e0d7e..e048cdc 100644 --- a/gcc/d/dmd/aliasthis.d +++ b/gcc/d/dmd/aliasthis.d @@ -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) diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index 66be73e..e2b3319 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -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; diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index ae8f65b..0bf40ef 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -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. diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d index 1fd9005..0ecd635 100644 --- a/gcc/d/dmd/blockexit.d +++ b/gcc/d/dmd/blockexit.d @@ -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); diff --git a/gcc/d/dmd/builtin.d b/gcc/d/dmd/builtin.d index b99f690..c4f84b1 100644 --- a/gcc/d/dmd/builtin.d +++ b/gcc/d/dmd/builtin.d @@ -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); diff --git a/gcc/d/dmd/chkformat.d b/gcc/d/dmd/chkformat.d index 97adc5a..b168b4f 100644 --- a/gcc/d/dmd/chkformat.d +++ b/gcc/d/dmd/chkformat.d @@ -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(); } } diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index d300617..da66812 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -794,6 +794,19 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) if (!needToHash(sd)) return null; + /* The trouble is that the following code relies on .tupleof, but .tupleof + * is not allowed for C files. If we allow it for C files, then that turns on + * the other D properties, too, such as .dup which will then conflict with allowed + * field names. + * One way to fix it is to replace the following foreach and .tupleof with C + * statements and expressions. + * But, it's debatable whether C structs should even need toHash(). + * Note that it would only be necessary if it has floating point fields. + * For now, we'll just not generate a toHash() for C files. + */ + if (sc.flags & SCOPE.Cfile) + return null; + //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); Loc declLoc; // loc is unnecessary so __xtoHash is never called directly Loc loc; // internal code should have no loc to prevent coverage @@ -831,31 +844,31 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) } /***************************************** - * Create inclusive destructor for struct/class by aggregating - * all the destructors in dtors[] with the destructors for + * Create aggregate destructor for struct/class by aggregating + * all the destructors in userDtors[] with the destructors for * all the members. + * Sets ad's fieldDtor, aggrDtor, dtor and tidtor fields. * Params: * ad = struct or class to build destructor for * sc = context - * Returns: - * generated function, null if none needed * Note: * Close similarity with StructDeclaration::buildPostBlit(), * and the ordering changes (runs backward instead of forwards). */ -DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) +void buildDtors(AggregateDeclaration ad, Scope* sc) { //printf("AggregateDeclaration::buildDtor() %s\n", ad.toChars()); if (ad.isUnionDeclaration()) - return null; // unions don't have destructors + return; // unions don't have destructors StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; - Loc declLoc = ad.dtors.dim ? ad.dtors[0].loc : ad.loc; + Loc declLoc = ad.userDtors.dim ? ad.userDtors[0].loc : ad.loc; Loc loc; // internal code should have no loc to prevent coverage FuncDeclaration xdtor_fwd = null; - // if the dtor is an extern(C++) prototype, then we expect it performs a full-destruction; we don't need to build a full-dtor - const bool dtorIsCppPrototype = ad.dtors.dim == 1 && ad.dtors[0].linkage == LINK.cpp && !ad.dtors[0].fbody; + // Build the field destructor (`ad.fieldDtor`), if needed. + // If the user dtor is an extern(C++) prototype, then we expect it performs a full-destruction and skip building. + const bool dtorIsCppPrototype = ad.userDtors.dim && ad.userDtors[0].linkage == LINK.cpp && !ad.userDtors[0].fbody; if (!dtorIsCppPrototype) { Expression e = null; @@ -936,36 +949,6 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) e = Expression.combine(ex, e); // combine in reverse order } - /* extern(C++) destructors call into super to destruct the full hierarchy - */ - ClassDeclaration cldec = ad.isClassDeclaration(); - if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.primaryDtor) - { - // WAIT BUT: do I need to run `cldec.baseClass.dtor` semantic? would it have been run before? - cldec.baseClass.dtor.functionSemantic(); - - stc = mergeFuncAttrs(stc, cldec.baseClass.primaryDtor); - if (!(stc & STC.disable)) - { - // super.__xdtor() - - Expression ex = new SuperExp(loc); - - // This is a hack so we can call destructors on const/immutable objects. - // Do it as a type 'paint'. - ex = new CastExp(loc, ex, cldec.baseClass.type.mutableOf()); - if (stc & STC.safe) - stc = (stc & ~STC.safe) | STC.trusted; - - ex = new DotVarExp(loc, ex, cldec.baseClass.primaryDtor, false); - ex = new CallExp(loc, ex); - - e = Expression.combine(e, ex); // super dtor last - } - } - - /* Build our own "destructor" which executes e - */ if (e || (stc & STC.disable)) { //printf("Building __fieldDtor(), %s\n", e.toChars()); @@ -973,29 +956,45 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) dd.generated = true; dd.storage_class |= STC.inference; dd.fbody = new ExpStatement(loc, e); - ad.dtors.shift(dd); ad.members.push(dd); dd.dsymbolSemantic(sc); ad.fieldDtor = dd; } } - DtorDeclaration xdtor = null; - switch (ad.dtors.dim) + // Generate list of dtors to call in that order + DtorDeclarations dtors; + foreach_reverse (userDtor; ad.userDtors[]) + dtors.push(userDtor); + if (ad.fieldDtor) + dtors.push(ad.fieldDtor); + if (!dtorIsCppPrototype) + { + // extern(C++) destructors call into super to destruct the full hierarchy + ClassDeclaration cldec = ad.isClassDeclaration(); + if (cldec && cldec.classKind == ClassKind.cpp && cldec.baseClass && cldec.baseClass.aggrDtor) + dtors.push(cldec.baseClass.aggrDtor); + } + + // Set/build `ad.aggrDtor` + switch (dtors.dim) { case 0: break; case 1: - xdtor = ad.dtors[0]; + // Use the single existing dtor directly as aggregate dtor. + // Note that this might be `cldec.baseClass.aggrDtor`. + ad.aggrDtor = dtors[0]; break; default: + // Build the aggregate destructor, calling all dtors in order. assert(!dtorIsCppPrototype); Expression e = null; e = null; stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; - foreach (FuncDeclaration fd; ad.dtors) + foreach (FuncDeclaration fd; dtors) { stc = mergeFuncAttrs(stc, fd); if (stc & STC.disable) @@ -1005,8 +1004,9 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) } Expression ex = new ThisExp(loc); ex = new DotVarExp(loc, ex, fd, false); - ex = new CallExp(loc, ex); - e = Expression.combine(ex, e); + CallExp ce = new CallExp(loc, ex); + ce.directcall = true; + e = Expression.combine(e, ce); } auto dd = new DtorDeclaration(declLoc, Loc.initial, stc, Id.__aggrDtor); dd.generated = true; @@ -1014,19 +1014,20 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) dd.fbody = new ExpStatement(loc, e); ad.members.push(dd); dd.dsymbolSemantic(sc); - xdtor = dd; + ad.aggrDtor = dd; break; } - ad.primaryDtor = xdtor; - - if (xdtor && xdtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable) - xdtor = buildWindowsCppDtor(ad, xdtor, sc); + // Set/build `ad.dtor`. + // On Windows, the dtor in the vtable is a shim with different signature. + ad.dtor = (ad.aggrDtor && ad.aggrDtor.linkage == LINK.cpp && !target.cpp.twoDtorInVtable) + ? buildWindowsCppDtor(ad, ad.aggrDtor, sc) + : ad.aggrDtor; - // Add an __xdtor alias to make the inclusive dtor accessible - if (xdtor) + // Add an __xdtor alias to make `ad.dtor` accessible + if (ad.dtor) { - auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, xdtor); + auto _alias = new AliasDeclaration(Loc.initial, Id.__xdtor, ad.dtor); _alias.dsymbolSemantic(sc); ad.members.push(_alias); if (xdtor_fwd) @@ -1035,7 +1036,8 @@ DtorDeclaration buildDtor(AggregateDeclaration ad, Scope* sc) _alias.addMember(sc, ad); // add to symbol table } - return xdtor; + // Set/build `ad.tidtor` + ad.tidtor = buildExternDDtor(ad, sc); } /** @@ -1069,17 +1071,16 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara auto ftype = new TypeFunction(ParameterList(params), Type.tvoidptr, LINK.cpp, dtor.storage_class); auto func = new DtorDeclaration(dtor.loc, dtor.loc, dtor.storage_class, Id.cppdtor); func.type = ftype; - if (dtor.fbody) - { - const loc = dtor.loc; - auto stmts = new Statements; - auto call = new CallExp(loc, dtor, null); - call.directcall = true; - stmts.push(new ExpStatement(loc, call)); - stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr))); - func.fbody = new CompoundStatement(loc, stmts); - func.generated = true; - } + + // Always generate the function with body, because it is not exported from DLLs. + const loc = dtor.loc; + auto stmts = new Statements; + auto call = new CallExp(loc, dtor, null); + call.directcall = true; + stmts.push(new ExpStatement(loc, call)); + stmts.push(new ReturnStatement(loc, new CastExp(loc, new ThisExp(loc), Type.tvoidptr))); + func.fbody = new CompoundStatement(loc, stmts); + func.generated = true; auto sc2 = sc.push(); sc2.stc &= ~STC.static_; // not a static destructor @@ -1094,7 +1095,7 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara } /** - * build a shim function around the compound dtor that translates + * build a shim function around the aggregate dtor that translates * a C++ destructor to a destructor with extern(D) calling convention * * Params: @@ -1104,9 +1105,9 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara * Returns: * the shim destructor, semantically analyzed and added to the class as a member */ -DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) +private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) { - auto dtor = ad.primaryDtor; + auto dtor = ad.aggrDtor; if (!dtor) return null; diff --git a/gcc/d/dmd/common/README.md b/gcc/d/dmd/common/README.md new file mode 100644 index 0000000..a9b65c3 --- /dev/null +++ b/gcc/d/dmd/common/README.md @@ -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 | diff --git a/gcc/d/dmd/common/file.d b/gcc/d/dmd/common/file.d new file mode 100644 index 0000000..b8cde37 --- /dev/null +++ b/gcc/d/dmd/common/file.d @@ -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)(); +} diff --git a/gcc/d/dmd/root/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index e756917..c5a8437 100644 --- a/gcc/d/dmd/root/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -9,14 +9,21 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/outbuffer.d */ -module dmd.root.outbuffer; +module dmd.common.outbuffer; import core.stdc.stdarg; import core.stdc.stdio; import core.stdc.string; -import dmd.root.rmem; -import dmd.root.rootobject; -import dmd.root.string; +import core.stdc.stdlib; + +// In theory these functions should also restore errno, but we don't care because +// we abort application on error anyway. +extern (C) private pure @system @nogc nothrow +{ + pragma(mangle, "malloc") void* pureMalloc(size_t); + pragma(mangle, "realloc") void* pureRealloc(void* ptr, size_t size); + pragma(mangle, "free") void pureFree(void* ptr); +} debug { @@ -29,7 +36,7 @@ a contiguous array or a memory-mapped file. */ struct OutBuffer { - import dmd.root.file : FileMapping; + import dmd.common.file : FileMapping, touchFile, writeFile; // IMPORTANT: PLEASE KEEP STATE AND DESTRUCTOR IN SYNC WITH DEFINITION IN ./outbuffer.h. // state { @@ -48,6 +55,14 @@ struct OutBuffer // state } /** + Construct given size. + */ + this(size_t initialSize) nothrow + { + reserve(initialSize); + } + + /** Construct from filename. Will map the file into memory (or create it anew if necessary) and start writing at the beginning of it. @@ -56,14 +71,36 @@ struct OutBuffer */ @trusted this(const(char)* filename) { - fileMapping = new FileMapping!ubyte(filename); + FileMapping!ubyte model; + fileMapping = cast(FileMapping!ubyte*) malloc(model.sizeof); + memcpy(fileMapping, &model, model.sizeof); + fileMapping.__ctor(filename); + //fileMapping = new FileMapping!ubyte(filename); data = (*fileMapping)[]; } /** + Frees resources associated. + */ + extern (C++) void dtor() nothrow @trusted + { + if (fileMapping) + { + if (fileMapping.active) + fileMapping.close(); + fileMapping = null; + } + else + { + debug (stomp) memset(data.ptr, 0xFF, data.length); + free(data.ptr); + } + } + + /** Frees resources associated automatically. */ - extern (C++) ~this() pure nothrow + extern (C++) ~this() pure nothrow @trusted { if (fileMapping) { @@ -74,10 +111,23 @@ struct OutBuffer else { debug (stomp) memset(data.ptr, 0xFF, data.length); - mem.xfree(data.ptr); + pureFree(data.ptr); } } + /// For porting with ease from dmd.backend.outbuf.Outbuffer + ubyte* buf() nothrow { + return data.ptr; + } + + /// For porting with ease from dmd.backend.outbuf.Outbuffer + ubyte** bufptr() nothrow { + static struct Array { size_t length; ubyte* ptr; } + auto a = cast(Array*) &data; + assert(a.length == data.length && a.ptr == data.ptr); + return &a.ptr; + } + extern (C++) size_t length() const pure @nogc @safe nothrow { return offset; } /********************** @@ -109,7 +159,7 @@ struct OutBuffer else { debug (stomp) memset(data.ptr, 0xFF, data.length); - mem.xfree(extractData()); + pureFree(extractData()); } } @@ -141,17 +191,18 @@ struct OutBuffer { debug (stomp) { - auto p = cast(ubyte*)mem.xmalloc(size); + auto p = cast(ubyte*) pureMalloc(size); + p || assert(0, "OutBuffer: out of memory."); memcpy(p, data.ptr, offset); memset(data.ptr, 0xFF, data.length); // stomp old location - mem.xfree(data.ptr); + pureFree(data.ptr); memset(p + offset, 0xff, size - offset); // stomp unused data } else { - auto p = cast(ubyte*)mem.xrealloc(data.ptr, size); - if (mem.isGCEnabled) // clear currently unused data to avoid false pointers - memset(p + offset + nbytes, 0xff, size - offset - nbytes); + auto p = cast(ubyte*) pureRealloc(data.ptr, size); + p || assert(0, "OutBuffer: out of memory."); + memset(p + offset + nbytes, 0xff, size - offset - nbytes); } data = p[0 .. size]; } @@ -164,7 +215,7 @@ struct OutBuffer */ extern (C++) void setsize(size_t size) pure nothrow @nogc @safe { - assert(size <= offset); + assert(size <= data.length); offset = size; } @@ -185,6 +236,14 @@ struct OutBuffer notlinehead = true; } + // Write an array to the buffer, no reserve check + @trusted nothrow + void writen(const void *b, size_t len) + { + memcpy(data.ptr + offset, b, len); + offset += len; + } + extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow { write(data[0 .. nbytes]); @@ -199,27 +258,90 @@ struct OutBuffer offset += buf.length; } - extern (C++) void writestring(const(char)* string) pure nothrow + /** + * Writes a 16 bit value, no reserve check. + */ + @trusted nothrow + void write16n(int v) { - write(string.toDString); + auto x = cast(ushort) v; + data[offset] = x & 0x00FF; + data[offset + 1] = x >> 8u; + offset += 2; } + /** + * Writes a 16 bit value. + */ + void write16(int v) nothrow + { + auto u = cast(ushort) v; + write(&u, u.sizeof); + } + + /** + * Writes a 32 bit int. + */ + void write32(int v) nothrow @trusted + { + write(&v, v.sizeof); + } + + /** + * Writes a 64 bit int. + */ + @trusted void write64(long v) nothrow + { + write(&v, v.sizeof); + } + + /// NOT zero-terminated + extern (C++) void writestring(const(char)* s) pure nothrow + { + if (!s) + return; + import core.stdc.string : strlen; + write(s[0 .. strlen(s)]); + } + + /// ditto void writestring(const(char)[] s) pure nothrow { write(s); } + /// ditto void writestring(string s) pure nothrow { write(s); } + /// NOT zero-terminated, followed by newline void writestringln(const(char)[] s) pure nothrow { writestring(s); writenl(); } + // Zero-terminated + void writeString(const(char)* s) pure nothrow @trusted + { + write(s[0 .. strlen(s)+1]); + } + + /// ditto + void writeString(const(char)[] s) pure nothrow + { + write(s); + writeByte(0); + } + + /// ditto + void writeString(string s) pure nothrow + { + writeString(cast(const(char)[])(s)); + } + extern (C++) void prependstring(const(char)* string) pure nothrow { size_t len = strlen(string); @@ -244,6 +366,38 @@ struct OutBuffer notlinehead = false; } + // Write n zeros; return pointer to start of zeros + @trusted + void *writezeros(size_t n) nothrow + { + reserve(n); + auto result = memset(data.ptr + offset, 0, n); + offset += n; + return result; + } + + // Position buffer to accept the specified number of bytes at offset + @trusted + void position(size_t where, size_t nbytes) nothrow + { + if (where + nbytes > data.length) + { + reserve(where + nbytes - offset); + } + offset = where; + + debug assert(offset + nbytes <= data.length); + } + + /** + * Writes an 8 bit byte, no reserve check. + */ + extern (C++) @trusted nothrow + void writeByten(int b) + { + this.data[offset++] = cast(ubyte) b; + } + extern (C++) void writeByte(uint b) pure nothrow { if (doindent && !notlinehead && b != '\n') @@ -369,14 +523,6 @@ struct OutBuffer } } - extern (C++) void write(RootObject obj) /*nothrow*/ - { - if (obj) - { - writestring(obj.toChars()); - } - } - extern (C++) void fill0(size_t nbytes) pure nothrow { reserve(nbytes); @@ -428,8 +574,8 @@ struct OutBuffer break; } offset += count; - if (mem.isGCEnabled) - memset(data.ptr + offset, 0xff, psize - count); + // if (mem.isGCEnabled) + memset(data.ptr + offset, 0xff, psize - count); } static if (__VERSION__ < 2092) @@ -460,7 +606,6 @@ struct OutBuffer */ extern (C++) void print(ulong u) pure nothrow { - //import core.internal.string; // not available UnsignedStringBuf buf = void; writestring(unsignedToTempString(u, buf)); } @@ -558,6 +703,11 @@ struct OutBuffer return extractData()[0 .. length]; } + extern (D) byte[] extractUbyteSlice(bool nullTerminate = false) pure nothrow + { + return cast(byte[]) extractSlice(nullTerminate); + } + // Append terminating null if necessary and get view of internal buffer extern (C++) char* peekChars() pure nothrow { @@ -577,6 +727,36 @@ struct OutBuffer return extractData(); } + void writesLEB128(int value) pure nothrow + { + while (1) + { + ubyte b = value & 0x7F; + + value >>= 7; // arithmetic right shift + if ((value == 0 && !(b & 0x40)) || + (value == -1 && (b & 0x40))) + { + writeByte(b); + break; + } + writeByte(b | 0x80); + } + } + + void writeuLEB128(uint value) pure nothrow + { + do + { + ubyte b = value & 0x7F; + + value >>= 7; + if (value) + b |= 0x80; + writeByte(b); + } while (value); + } + /** Destructively saves the contents of `this` to `filename`. As an optimization, if the file already has identical contents with the buffer, @@ -591,7 +771,6 @@ struct OutBuffer */ extern(D) bool moveToFile(const char* filename) { - import dmd.root.file; bool result = true; const bool identical = this[] == FileMapping!(const ubyte)(filename)[]; @@ -615,12 +794,12 @@ struct OutBuffer else { if (!identical) - File.write(filename, this[]); + writeFile(filename, this[]); destroy(); } return identical - ? result && File.touch(filename) + ? result && touchFile(filename) : result; } } @@ -645,7 +824,7 @@ char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe pure else { ubyte x = cast(ubyte)(value % radix); - value = value / radix; + value /= radix; buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a'); } } while (value); diff --git a/gcc/d/dmd/root/outbuffer.h b/gcc/d/dmd/common/outbuffer.h index b635373..a5e3f9c 100644 --- a/gcc/d/dmd/root/outbuffer.h +++ b/gcc/d/dmd/common/outbuffer.h @@ -4,14 +4,14 @@ * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt - * https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.h + * https://github.com/dlang/dmd/blob/master/src/dmd/common/outbuffer.h */ #pragma once -#include "dsystem.h" -#include "dcompat.h" -#include "rmem.h" +#include "../root/dsystem.h" +#include "../root/dcompat.h" +#include "../root/rmem.h" class RootObject; @@ -22,7 +22,7 @@ private: DArray<unsigned char> data; d_size_t offset; bool notlinehead; - void* fileMapping; // pointer to a file mapping object not used on the C++ side + void *fileMapping; // pointer to a file mapping object not used on the C++ side public: bool doindent; bool spaces; diff --git a/gcc/d/dmd/common/string.d b/gcc/d/dmd/common/string.d new file mode 100644 index 0000000..026374a --- /dev/null +++ b/gcc/d/dmd/common/string.d @@ -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]); +} diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index d4a8b13..05bd4bd 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -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) diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index bb40649..7d8ab67 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -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); + } + //} } diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/cppmangle.d index 0381f9a..df742c0 100644 --- a/gcc/d/dmd/cppmangle.d +++ b/gcc/d/dmd/cppmangle.d @@ -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); diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index 22633a8..7f76d75 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -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 || diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 4c70565..87c3ada 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -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)) diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index b065251..34a236b 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -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; diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 0f40c11..e3f135a 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -16,6 +16,7 @@ import core.stdc.stdio; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; +import dmd.attrib; import dmd.ctorflow; import dmd.dclass; import dmd.delegatize; @@ -34,7 +35,7 @@ import dmd.init; import dmd.initsem; import dmd.intrange; import dmd.mtype; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.rootobject; import dmd.target; import dmd.tokens; @@ -705,7 +706,7 @@ extern (C++) final class AliasDeclaration : Declaration assert(s); } - static AliasDeclaration create(Loc loc, Identifier id, Type type) + static AliasDeclaration create(const ref Loc loc, Identifier id, Type type) { return new AliasDeclaration(loc, id, type); } @@ -1192,14 +1193,7 @@ extern (C++) class VarDeclaration : Declaration /* If coming after a bit field in progress, * advance past the field */ - if (fieldState.inFlight) - { - fieldState.inFlight = false; - if (0 && target.os & Target.OS.Posix) - fieldState.offset += (fieldState.bitOffset + 7) / 8; - else if (0 &&target.os == Target.OS.Windows) - fieldState.offset += fieldState.fieldSize; - } + fieldState.inFlight = false; const sz = t.size(loc); assert(sz != SIZE_INVALID && sz < uint.max); @@ -1743,13 +1737,23 @@ extern (C++) class BitFieldDeclaration : VarDeclaration override final void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) { - //printf("BitFieldDeclaration::setFieldOffset(ad = %s) %s\n", ad.toChars(), toChars()); + //printf("BitFieldDeclaration::setFieldOffset(ad: %s, field: %s)\n", ad.toChars(), toChars()); + static void print(const ref FieldState fieldState) + { + printf("FieldState.offset = %d bytes\n", fieldState.offset); + printf(" .fieldOffset = %d bytes\n", fieldState.fieldOffset); + printf(" .bitOffset = %d bits\n", fieldState.bitOffset); + printf(" .fieldSize = %d bytes\n", fieldState.fieldSize); + printf(" .inFlight = %d\n\n", fieldState.inFlight); + } + //print(fieldState); Type t = type.toBasetype(); + const bool anon = isAnonymous(); // List in ad.fields. Even if the type is error, it's necessary to avoid // pointless error diagnostic "more initializers than fields" on struct literal. - if (!isAnonymous()) + if (!anon) ad.fields.push(this); if (t.ty == Terror) @@ -1760,17 +1764,36 @@ extern (C++) class BitFieldDeclaration : VarDeclaration uint memsize = cast(uint)sz; // size of member uint memalignsize = target.fieldalign(t); // size of member for alignment purposes - if (fieldWidth == 0 && !isAnonymous()) + if (fieldWidth == 0 && !anon) error(loc, "named bit fields cannot have 0 width"); if (fieldWidth > memsize * 8) error(loc, "bit field width %d is larger than type", fieldWidth); + const style = target.c.bitFieldStyle; + void startNewField() { + uint alignsize; + if (style == TargetC.BitFieldStyle.Gcc_Clang) + { + if (fieldWidth > 32) + alignsize = memalignsize; + else if (fieldWidth > 16) + alignsize = 4; + else if (fieldWidth > 8) + alignsize = 2; + else + alignsize = 1; + } + else + alignsize = memsize; // not memalignsize + + uint dummy; offset = AggregateDeclaration.placeField( &fieldState.offset, - memsize, memalignsize, alignment, - &ad.structsize, &ad.alignsize, + memsize, alignsize, alignment, + &ad.structsize, + (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? &dummy : &ad.alignsize, isunion); fieldState.inFlight = true; @@ -1779,19 +1802,92 @@ extern (C++) class BitFieldDeclaration : VarDeclaration fieldState.fieldSize = memsize; } - if (!fieldState.inFlight || fieldWidth == 0) + if (style == TargetC.BitFieldStyle.Gcc_Clang) { - startNewField(); + if (fieldWidth == 0) + { + if (!isunion) + { + // Use type of zero width field to align to next field + fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); + ad.structsize = fieldState.offset; + } + + fieldState.inFlight = false; + return; + } + + if (ad.alignsize == 0) + ad.alignsize = 1; + if (!anon && + ad.alignsize < memalignsize) + ad.alignsize = memalignsize; + } + else if (style == TargetC.BitFieldStyle.MS) + { + if (ad.alignsize == 0) + ad.alignsize = 1; + if (fieldWidth == 0) + { + if (fieldState.inFlight && !isunion) + { + // documentation says align to next int + //const alsz = cast(uint)Type.tint32.size(); + const alsz = memsize; // but it really does this + fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); + ad.structsize = fieldState.offset; + } + + fieldState.inFlight = false; + return; + } + } + else if (style == TargetC.BitFieldStyle.DM) + { + if (anon && fieldWidth && (!fieldState.inFlight || fieldState.bitOffset == 0)) + return; // this probably should be a bug in DMC + if (ad.alignsize == 0) + ad.alignsize = 1; + if (fieldWidth == 0) + { + if (fieldState.inFlight && !isunion) + { + const alsz = memsize; + fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); + ad.structsize = fieldState.offset; + } + + fieldState.inFlight = false; + return; + } } - if (0 && target.os & Target.OS.Posix) + if (!fieldState.inFlight) + { + startNewField(); + } + else if (style == TargetC.BitFieldStyle.Gcc_Clang) { - if ((fieldState.offset%4 * 8) + fieldState.bitOffset + fieldWidth > int.sizeof * 8) + if (fieldState.bitOffset + fieldWidth > memsize * 8) { + //printf("start1 fieldState.bitOffset:%u fieldWidth:%u memsize:%u\n", fieldState.bitOffset, fieldWidth, memsize); startNewField(); } + else + { + // if alignment boundary is crossed + uint start = fieldState.fieldOffset * 8 + fieldState.bitOffset; + uint end = start + fieldWidth; + //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); + if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) + { + //printf("alignment is crossed\n"); + startNewField(); + } + } } - else if (1 || target.os == Target.OS.Windows) + else if (style == TargetC.BitFieldStyle.DM || + style == TargetC.BitFieldStyle.MS) { if (memsize != fieldState.fieldSize || fieldState.bitOffset + fieldWidth > fieldState.fieldSize * 8) @@ -1799,22 +1895,29 @@ extern (C++) class BitFieldDeclaration : VarDeclaration startNewField(); } } + else + assert(0); offset = fieldState.fieldOffset; bitOffset = fieldState.bitOffset; - if (0 && target.os & Target.OS.Posix) + + const pastField = bitOffset + fieldWidth; + if (style == TargetC.BitFieldStyle.Gcc_Clang) { - while (bitOffset > memsize * 8) - { - bitOffset -= 8; - offset += 1; - } + auto size = (pastField + 7) / 8; + fieldState.fieldSize = size; + //printf(" offset: %d, size: %d\n", offset, size); + ad.structsize = offset + size; } + else + fieldState.fieldSize = memsize; + //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize); + //print(fieldState); - //fieldState.fieldSize = memsize; if (!isunion) { - fieldState.bitOffset += fieldWidth; + fieldState.offset = offset + fieldState.fieldSize; + fieldState.bitOffset = pastField; } //printf("\t%s: memalignsize = %d\n", toChars(), memalignsize); @@ -1861,7 +1964,7 @@ extern (C++) class TypeInfoDeclaration : VarDeclaration storage_class = STC.static_ | STC.gshared; visibility = Visibility(Visibility.Kind.public_); linkage = LINK.c; - alignment = target.ptrsize; + alignment.set(target.ptrsize); } static TypeInfoDeclaration create(Type tinfo) diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 1c56def..4a4c353 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -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; diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 541fac7..a1f36c0 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -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; diff --git a/gcc/d/dmd/dmacro.d b/gcc/d/dmd/dmacro.d index ddfee2c..357f7bd 100644 --- a/gcc/d/dmd/dmacro.d +++ b/gcc/d/dmd/dmacro.d @@ -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 diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/dmangle.d index 71b8c7a..c417f93 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/dmangle.d @@ -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); } } diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 768eaa0..4e00713 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -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 diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index 9b4329b..f9b765c 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -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); diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index 638fc7e..42c0d18 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -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; + } } /********************************** diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index 80ecd36..0925e7c 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -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; diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index 3a6dff2..9aa435d 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -187,7 +187,7 @@ struct Visibility } } -enum PASS : int +enum PASS : ubyte { init, // initial state semantic, // semantic() started @@ -225,11 +225,13 @@ enum : int */ struct FieldState { - uint offset; /// offset for next field + uint offset; /// byte offset for next field - uint fieldOffset; /// offset for the start of the bit field + uint fieldOffset; /// byte offset for the start of the bit field + uint fieldSize; /// byte size of field + uint fieldAlign; /// byte alignment of field uint bitOffset; /// bit offset for field - uint fieldSize; /// size of field in bytes + bool inFlight; /// bit field is in flight } @@ -793,10 +795,18 @@ extern (C++) class Dsymbol : ASTNode Dsymbol s2 = sds.symtabLookup(this,ident); // If using C tag/prototype/forward declaration rules - if (sc.flags & SCOPE.Cfile && - handleTagSymbols(*sc, this, s2, sds)) + if (sc.flags & SCOPE.Cfile) + { + if (handleTagSymbols(*sc, this, s2, sds)) + return; + if (handleSymbolRedeclarations(*sc, this, s2, sds)) return; + sds.multiplyDefined(Loc.initial, this, s2); // ImportC doesn't allow overloading + errors = true; + return; + } + if (!s2.overloadInsert(this)) { sds.multiplyDefined(Loc.initial, this, s2); @@ -2384,3 +2394,91 @@ Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) } +/********************************************** + * ImportC allows redeclarations of C variables, functions and typedefs. + * extern int x; + * int x = 3; + * and: + * extern void f(); + * void f() { } + * Attempt to merge them. + * Params: + * sc = context + * s = symbol to add to symbol table + * s2 = existing declaration + * sds = symbol table + * Returns: + * if s and s2 are successfully put in symbol table then return the merged symbol, + * null if they conflict + */ +Dsymbol handleSymbolRedeclarations(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) +{ + enum log = false; + if (log) printf("handleSymbolRedeclarations('%s')\n", s.toChars()); + + static Dsymbol collision() + { + if (log) printf(" collision\n"); + return null; + } + + auto vd = s.isVarDeclaration(); // new declaration + auto vd2 = s2.isVarDeclaration(); // existing declaration + if (vd && vd2) + { + // if one is `static` and the other isn't + if ((vd.storage_class ^ vd2.storage_class) & STC.static_) + return collision(); + + const i1 = vd._init && ! vd._init.isVoidInitializer(); + const i2 = vd2._init && !vd2._init.isVoidInitializer(); + + if (i1 && i2) + return collision(); // can't both have initializers + + if (i1) + return vd; + + /* BUG: the types should match, which needs semantic() to be run on it + * extern int x; + * int x; // match + * typedef int INT; + * INT x; // match + * long x; // collision + * We incorrectly ignore these collisions + */ + return vd2; + } + + auto fd = s.isFuncDeclaration(); // new declaration + auto fd2 = s2.isFuncDeclaration(); // existing declaration + if (fd && fd2) + { + // if one is `static` and the other isn't + if ((fd.storage_class ^ fd2.storage_class) & STC.static_) + return collision(); + + if (fd.fbody && fd2.fbody) + return collision(); // can't both have bodies + + if (fd.fbody) + return fd; + + /* BUG: just like with VarDeclaration, the types should match, which needs semantic() to be run on it. + * FuncDeclaration::semantic2() can detect this, but it relies overnext being set. + */ + return fd2; + } + + auto td = s.isAliasDeclaration(); // new declaration + auto td2 = s2.isAliasDeclaration(); // existing declaration + if (td && td2) + { + /* BUG: just like with variables and functions, the types should match, which needs semantic() to be run on it. + * FuncDeclaration::semantic2() can detect this, but it relies overnext being set. + */ + return td2; + } + + return collision(); +} diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index f43bc83..02252fd 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -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; }; diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index eac2095..047c1eb 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -56,7 +56,7 @@ import dmd.objc; import dmd.opover; import dmd.parse; import dmd.root.filename; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.rmem; import dmd.root.rootobject; import dmd.semantic2; @@ -109,17 +109,18 @@ extern(C++) void dsymbolSemantic(Dsymbol dsym, Scope* sc) * ad = AlignmentDeclaration * sc = context * Returns: - * alignment as numerical value that is never 0. - * STRUCTALIGN_DEFAULT is used instead. - * STRUCTALIGN_DEFAULT is returned for errors + * ad with alignment value determined */ -structalign_t getAlignment(AlignDeclaration ad, Scope* sc) +AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc) { - if (ad.salign != ad.UNKNOWN) // UNKNOWN is 0 - return ad.salign; + if (!ad.salign.isUnknown()) // UNKNOWN is 0 + return ad; if (!ad.exps) - return ad.salign = STRUCTALIGN_DEFAULT; + { + ad.salign.setDefault(); + return ad; + } dinteger_t strictest = 0; // strictest alignment bool errors; @@ -140,7 +141,7 @@ structalign_t getAlignment(AlignDeclaration ad, Scope* sc) if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment continue; - if (n < 1 || n & (n - 1) || structalign_t.max < n || !e.type.isintegral()) + if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isintegral()) { error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n); errors = true; @@ -150,10 +151,12 @@ structalign_t getAlignment(AlignDeclaration ad, Scope* sc) } } - ad.salign = (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect - ? STRUCTALIGN_DEFAULT - : cast(structalign_t) strictest; - return ad.salign; + if (errors || strictest == 0) // C11 6.7.5-6 says alignment of 0 means no effect + ad.salign.setDefault(); + else + ad.salign.set(cast(uint) strictest); + + return ad; } const(char)* getMessage(DeprecatedDeclaration dd) @@ -365,16 +368,31 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dsym.semanticRun = PASS.semantic; - /* Pick up storage classes from context, but except synchronized, - * override, abstract, and final. - */ - dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)); + // 'static foreach' variables should not inherit scope properties + // https://issues.dlang.org/show_bug.cgi?id=19482 + if ((dsym.storage_class & (STC.foreach_ | STC.local)) == (STC.foreach_ | STC.local)) + { + dsym.linkage = LINK.d; + dsym.visibility = Visibility(Visibility.Kind.public_); + dsym.overlapped = false; // unset because it is modified early on this function + dsym.userAttribDecl = null; // unset because it is set by Dsymbol.setScope() + } + else + { + /* Pick up storage classes from context, but except synchronized, + * override, abstract, and final. + */ + dsym.storage_class |= (sc.stc & ~(STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)); + dsym.userAttribDecl = sc.userAttribDecl; + dsym.cppnamespace = sc.namespace; + dsym.linkage = sc.linkage; + dsym.visibility = sc.visibility; + dsym.alignment = sc.alignment(); + } + if (dsym.storage_class & STC.extern_ && dsym._init) dsym.error("extern symbols cannot have initializers"); - dsym.userAttribDecl = sc.userAttribDecl; - dsym.cppnamespace = sc.namespace; - AggregateDeclaration ad = dsym.isThis(); if (ad) dsym.storage_class |= ad.storage_class & STC.TYPECTOR; @@ -431,16 +449,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dsym.errors = true; dsym.type.checkDeprecated(dsym.loc, sc); - dsym.linkage = sc.linkage; dsym.parent = sc.parent; //printf("this = %p, parent = %p, '%s'\n", dsym, dsym.parent, dsym.parent.toChars()); - dsym.visibility = sc.visibility; /* If scope's alignment is the default, use the type's alignment, * otherwise the scope overrrides. */ - dsym.alignment = sc.alignment(); - if (dsym.alignment == STRUCTALIGN_DEFAULT) + if (dsym.alignment.isDefault()) dsym.alignment = dsym.type.alignment(); // use type's alignment //printf("sc.stc = %x\n", sc.stc); @@ -935,6 +950,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable); + if (sc.flags & SCOPE.Cfile && + dsym.type.isTypeSArray() && + dsym.type.isTypeSArray().isIncomplete() && + dsym._init.isVoidInitializer() && + !(dsym.storage_class & STC.field)) + { + dsym.error("incomplete array type must have initializer"); + } + ExpInitializer ei = dsym._init.isExpInitializer(); if (ei) // https://issues.dlang.org/show_bug.cgi?id=13424 @@ -971,6 +995,13 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ei = new ExpInitializer(dsym._init.loc, e); dsym._init = ei; } + else if (sc.flags & SCOPE.Cfile && dsym.type.isTypeSArray() && + dsym.type.isTypeSArray().isIncomplete()) + { + // C11 6.7.9-22 determine the size of the incomplete array, + // or issue an error that the initializer is invalid. + dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret); + } Expression exp = ei.exp; Expression e1 = new VarExp(dsym.loc, dsym); @@ -1056,18 +1087,29 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // could crash (inf. recursion) on a mod/pkg referencing itself if (ei && (ei.exp.op != TOK.scope_ ? true : !ei.exp.isScopeExp().sds.isPackage())) { - Expression exp = ei.exp.syntaxCopy(); - - bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest); - if (needctfe) - sc = sc.startCTFE(); - exp = exp.expressionSemantic(sc); - exp = resolveProperties(sc, exp); - if (needctfe) - sc = sc.endCTFE(); + if (ei.exp.type) + { + // If exp is already resolved we are done, our original init exp + // could have a type painting that we need to respect + // e.g. ['a'] typed as string, or [['z'], ""] as string[] + // See https://issues.dlang.org/show_bug.cgi?id=15711 + } + else + { + Expression exp = ei.exp.syntaxCopy(); + + bool needctfe = dsym.isDataseg() || (dsym.storage_class & STC.manifest); + if (needctfe) + sc = sc.startCTFE(); + exp = exp.expressionSemantic(sc); + exp = resolveProperties(sc, exp); + if (needctfe) + sc = sc.endCTFE(); + ei.exp = exp; + } Type tb2 = dsym.type.toBasetype(); - Type ti = exp.type.toBasetype(); + Type ti = ei.exp.type.toBasetype(); /* The problem is the following code: * struct CopyTest { @@ -1091,11 +1133,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (sd.postblit && tb2.toDsymbol(null) == sd) { // The only allowable initializer is a (non-copy) constructor - if (exp.isLvalue()) + if (ei.exp.isLvalue()) dsym.error("of type struct `%s` uses `this(this)`, which is not allowed in static initialization", tb2.toChars()); } } - ei.exp = exp; } dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITinterpret); @@ -1708,6 +1749,36 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { if (pd.args && pd.args.dim != 0) pd.error("takes no argument"); + else + { + immutable isCtor = pd.ident == Id.crt_constructor; + + static uint recurse(Dsymbol s, bool isCtor) + { + if (auto ad = s.isAttribDeclaration()) + { + uint nestedCount; + auto decls = ad.include(null); + if (decls) + { + for (size_t i = 0; i < decls.dim; ++i) + nestedCount += recurse((*decls)[i], isCtor); + } + return nestedCount; + } + else if (auto f = s.isFuncDeclaration()) + { + f.isCrtCtorDtor |= isCtor ? 1 : 2; + return 1; + } + else + return 0; + assert(0); + } + + if (recurse(pd, isCtor) > 1) + pd.error("can only apply to a single declaration"); + } return declarations(); } else if (pd.ident == Id.printf || pd.ident == Id.scanf) @@ -2445,6 +2516,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor tempdecl.parent = sc.parent; tempdecl.visibility = sc.visibility; + tempdecl.userAttribDecl = sc.userAttribDecl; tempdecl.cppnamespace = sc.namespace; tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_); tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_); @@ -2973,6 +3045,16 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (auto pragmadecl = sc.inlining) funcdecl.inlining = pragmadecl.evalPragmaInline(sc); + // check pragma(crt_constructor) + if (funcdecl.isCrtCtorDtor) + { + if (funcdecl.linkage != LINK.c) + { + funcdecl.error("must be `extern(C)` for `pragma(%s)`", + funcdecl.isCrtCtorDtor == 1 ? "crt_constructor".ptr : "crt_destructor".ptr); + } + } + funcdecl.visibility = sc.visibility; funcdecl.userAttribDecl = sc.userAttribDecl; UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl.linkage); @@ -3799,6 +3881,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } + if (funcdecl.fbody && sc._module.isRoot() && + (funcdecl.isMain() || funcdecl.isWinMain() || funcdecl.isDllMain() || funcdecl.isCMain())) + global.hasMainFunction = true; + if (funcdecl.fbody && funcdecl.isMain() && sc._module.isRoot()) { // check if `_d_cmain` is defined @@ -3995,7 +4081,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return; } if (dd.ident == Id.dtor && dd.semanticRun < PASS.semantic) - ad.dtors.push(dd); + ad.userDtors.push(dd); if (!dd.type) { dd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, dd.storage_class); @@ -4417,8 +4503,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Look for the constructor sd.ctor = sd.searchCtor(); - sd.dtor = buildDtor(sd, sc2); - sd.tidtor = buildExternDDtor(sd, sc2); + buildDtors(sd, sc2); + sd.hasCopyCtor = buildCopyCtor(sd, sc2); sd.postblit = buildPostBlit(sd, sc2); @@ -5057,8 +5143,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - cldec.dtor = buildDtor(cldec, sc2); - cldec.tidtor = buildExternDDtor(cldec, sc2); + buildDtors(cldec, sc2); if (cldec.classKind == ClassKind.cpp && cldec.cppDtorVtblIndex != -1) { diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index c3503bb..9d7957a 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -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()); + } +} diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d index 28054d0..e19adfc 100644 --- a/gcc/d/dmd/dtoh.d +++ b/gcc/d/dmd/dtoh.d @@ -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 diff --git a/gcc/d/dmd/dversion.d b/gcc/d/dmd/dversion.d index 0dff754..49ee4b3 100644 --- a/gcc/d/dmd/dversion.d +++ b/gcc/d/dmd/dversion.d @@ -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; /*********************************************************** diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 0d6fa1e..2a62332 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -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); } diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index dec3713..691364c 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -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; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 3b604af..8e152d6 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -43,12 +43,14 @@ import dmd.dtemplate; import dmd.errors; import dmd.escape; import dmd.expression; +import dmd.file_manager; import dmd.func; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.imphint; +import dmd.importc; import dmd.init; import dmd.initsem; import dmd.inline; @@ -62,7 +64,7 @@ import dmd.printast; import dmd.root.ctfloat; import dmd.root.file; import dmd.root.filename; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.rootobject; import dmd.root.string; import dmd.semantic2; @@ -1239,6 +1241,10 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = tthis = dve.e1.type; goto Lfd; } + else if (sc && sc.flags & SCOPE.Cfile && e1.op == TOK.variable && !e2) + { + // ImportC: do not implicitly call function if no ( ) are present + } else if (e1.op == TOK.variable && e1.type && (e1.type.toBasetype().ty == Tfunction || (cast(VarExp)e1).var.isOverDeclaration())) { s = (cast(VarExp)e1).var; @@ -1388,7 +1394,6 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) Type t0 = null; Expression e0 = null; - size_t j0 = ~0; bool foundType; for (size_t i = 0; i < exps.dim; i++) @@ -1441,17 +1446,16 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) { // https://issues.dlang.org/show_bug.cgi?id=21285 // Functions and delegates don't convert correctly with castTo below - exps[j0] = condexp.e1; + exps[i] = condexp.e1; e = condexp.e2; } else { // Convert to common type - exps[j0] = condexp.e1.castTo(sc, condexp.type); + exps[i] = condexp.e1.castTo(sc, condexp.type); e = condexp.e2.castTo(sc, condexp.type); } } - j0 = i; e0 = e; t0 = e.type; if (e.op != TOK.error) @@ -1599,6 +1603,7 @@ private bool preFunctionParameters(Scope* sc, Expressions* exps, const bool repo { Expression arg = (*exps)[i]; arg = resolveProperties(sc, arg); + arg = arg.arrayFuncConv(sc); if (arg.op == TOK.type) { // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 @@ -4297,7 +4302,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * expr.foo!(tiargs)(funcargs) */ Ldotti: - if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type) + if (exp.e1.op == TOK.dotTemplateInstance) { DotTemplateInstanceExp se = cast(DotTemplateInstanceExp)exp.e1; TemplateInstance ti = se.ti; @@ -4352,7 +4357,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * We handle such earlier, so go back. * Note that in the rewrite, we carefully did not run semantic() on e1 */ - if (exp.e1.op == TOK.dotTemplateInstance && !exp.e1.type) + if (exp.e1.op == TOK.dotTemplateInstance) { goto Ldotti; } @@ -4419,6 +4424,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (exp.e1.op == TOK.type && (sc && sc.flags & SCOPE.Cfile)) { + const numArgs = exp.arguments ? exp.arguments.length : 0; + if (e1org.parens && numArgs >= 1) + { + /* Ambiguous cases arise from CParser where there is not enough + * information to determine if we have a function call or a cast. + * ( type-name ) ( identifier ) ; + * ( identifier ) ( identifier ) ; + * If exp.e1 is a type-name, then this is a cast. + */ + Expression arg; + foreach (a; (*exp.arguments)[]) + { + arg = arg ? new CommaExp(a.loc, arg, a) : a; + } + auto t = exp.e1.isTypeExp().type; + auto e = new CastExp(exp.loc, arg, t); + result = e.expressionSemantic(sc); + return; + } + /* Ambiguous cases arise from CParser where there is not enough * information to determine if we have a function call or declaration. * type-name ( identifier ) ; @@ -4426,7 +4451,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * If exp.e1 is a type-name, then this is a declaration. C11 does not * have type construction syntax, so don't convert this to a cast(). */ - if (exp.arguments && exp.arguments.dim == 1) + if (numArgs == 1) { Expression arg = (*exp.arguments)[0]; if (auto ie = (*exp.arguments)[0].isIdentifierExp()) @@ -4638,15 +4663,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { UnaExp ue = cast(UnaExp)exp.e1; - Expression ue1 = ue.e1; - Expression ue1old = ue1; // need for 'right this' check - VarDeclaration v; - if (ue1.op == TOK.variable && (v = (cast(VarExp)ue1).var.isVarDeclaration()) !is null && v.needThis()) - { - ue.e1 = new TypeExp(ue1.loc, ue1.type); - ue1 = null; - } - + Expression ue1old = ue.e1; // need for 'right this' check DotVarExp dve; DotTemplateExp dte; Dsymbol s; @@ -4665,7 +4682,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // Do overload resolution - exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue1 ? ue1.type : null, exp.arguments, FuncResolveFlag.standard); + exp.f = resolveFuncCall(exp.loc, sc, s, tiargs, ue.e1.type, exp.arguments, FuncResolveFlag.standard); if (!exp.f || exp.f.errors || exp.f.type.ty == Terror) return setError(); @@ -4677,7 +4694,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto ad2 = b.sym; ue.e1 = ue.e1.castTo(sc, ad2.type.addMod(ue.e1.type.mod)); ue.e1 = ue.e1.expressionSemantic(sc); - ue1 = ue.e1; auto vi = exp.f.findVtblIndex(&ad2.vtbl, cast(int)ad2.vtbl.dim); assert(vi >= 0); exp.f = ad2.vtbl[vi].isFuncDeclaration(); @@ -6036,17 +6052,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } { - auto readResult = File.read(name); - if (!readResult.success) + auto fileName = FileName(name.toDString); + if (auto fmResult = FileManager.fileManager.lookup(fileName)) { - e.error("cannot read file `%s`", name); - return setError(); + se = new StringExp(e.loc, fmResult.data); } else { - // take ownership of buffer (probably leaking) - auto data = readResult.extractSlice(); - se = new StringExp(e.loc, data); + auto readResult = File.read(name); + if (!readResult.success) + { + e.error("cannot read file `%s`", name); + return setError(); + } + else + { + // take ownership of buffer (probably leaking) + auto data = readResult.extractSlice(); + se = new StringExp(e.loc, data); + + FileBuffer* fileBuffer = FileBuffer.create(); + fileBuffer.data = data; + FileManager.fileManager.add(fileName, fileBuffer); + } } } result = se.expressionSemantic(sc); @@ -6357,7 +6385,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - exp.type = Type.tnoreturn; + + // Only override the type when it isn't already some flavour of noreturn, + // e.g. when this assert was generated by defaultInitLiteral + if (!exp.type || !exp.type.isTypeNoreturn()) + exp.type = Type.tnoreturn; } else exp.type = Type.tvoid; @@ -6374,12 +6406,53 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars()); //printf("e1.op = %d, '%s'\n", e1.op, Token::toChars(e1.op)); } + if (exp.arrow) // ImportC only + exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc); + + if (sc.flags & SCOPE.Cfile) + { + if (exp.ident == Id.__xalignof && exp.e1.isTypeExp()) + { + // C11 6.5.3 says _Alignof only applies to types + Expression e; + Type t; + Dsymbol s; + dmd.typesem.resolve(exp.e1.type, exp.e1.loc, sc, e, t, s, true); + if (e) + { + exp.e1.error("argument to `_Alignof` must be a type"); + return setError(); + } + else if (t) + { + result = new IntegerExp(exp.loc, t.alignsize, Type.tsize_t); + } + else if (s) + { + exp.e1.error("argument to `_Alignof` must be a type"); + return setError(); + } + else + assert(0); + return; + } + } + + if (sc.flags & SCOPE.Cfile && exp.ident != Id.__sizeof) + { + result = fieldLookup(exp.e1, sc, exp.ident); + return; + } + Expression e = exp.semanticY(sc, 1); + if (e && isDotOpDispatch(e)) { + auto ode = e; uint errors = global.startGagging(); e = resolvePropertiesX(sc, e); - if (global.endGagging(errors)) + // Any error or if 'e' is not resolved, go to UFCS + if (global.endGagging(errors) || e is ode) e = null; /* fall down to UFCS */ else { @@ -6580,10 +6653,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars()); } + if (exp.type) + { + result = exp; + return; + } // Indicate we need to resolve by UFCS. Expression e = exp.semanticY(sc, 1); if (!e) e = resolveUFCSProperties(sc, exp); + if (e is exp) + e.type = Type.tvoid; // Unresolved type, because it needs inference result = e; } @@ -7908,6 +7988,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor dinteger_t length = el.toInteger(); auto bounds = IntRange(SignExtendedNumber(0), SignExtendedNumber(length)); exp.upperIsInBounds = bounds.contains(uprRange); + if (exp.lwr.op == TOK.int64 && exp.upr.op == TOK.int64 && exp.lwr.toInteger() > exp.upr.toInteger()) + { + exp.error("in slice `%s[%llu .. %llu]`, lower bound is greater than upper bound", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger()); + return setError(); + } + if (exp.upr.op == TOK.int64 && exp.upr.toInteger() > length) + { + exp.error("in slice `%s[%llu .. %llu]`, upper bound is greater than array length `%llu`", exp.e1.toChars, exp.lwr.toInteger(), exp.upr.toInteger(), length); + return setError(); + } } else if (exp.upr.op == TOK.int64 && exp.upr.toInteger() == 0) { @@ -7965,6 +8055,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("ArrayExp::semantic('%s')\n", exp.toChars()); } assert(!exp.type); + + result = exp.carraySemantic(sc); // C semantics + if (result) + return; + Expression e = exp.op_overload(sc); if (e) { @@ -8019,6 +8114,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(CommaExp e) { + //printf("Semantic.CommaExp() %s\n", e.toChars()); if (e.type) { result = e; @@ -8042,10 +8138,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (checkNonAssignmentArrayOp(e.e1)) return setError(); + // Comma expressions trigger this conversion + e.e2 = e.e2.arrayFuncConv(sc); + e.type = e.e2.type; if (e.type is Type.tvoid) discardValue(e.e1); - else if (!e.allowCommaExp && !e.isGenerated) + else if (!e.allowCommaExp && !e.isGenerated && !(sc.flags & SCOPE.Cfile)) e.error("Using the result of a comma expression is not allowed"); result = e; } @@ -8143,7 +8242,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // operator overloading should be handled in ArrayExp already. if (!exp.e1.type) - exp.e1 = exp.e1.expressionSemantic(sc); + exp.e1 = exp.e1.expressionSemantic(sc).arrayFuncConv(sc); assert(exp.e1.type); // semantic() should already be run on it if (exp.e1.op == TOK.type && exp.e1.type.ty != Ttuple) { @@ -8191,7 +8290,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (t1b.ty == Ttuple) sc = sc.startCTFE(); - exp.e2 = exp.e2.expressionSemantic(sc); + exp.e2 = exp.e2.expressionSemantic(sc).arrayFuncConv(sc); exp.e2 = resolveProperties(sc, exp.e2); if (t1b.ty == Ttuple) sc = sc.endCTFE(); @@ -8515,7 +8614,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (auto e2comma = exp.e2.isCommaExp()) { - if (!e2comma.isGenerated) + if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile)) exp.error("Using the result of a comma expression is not allowed"); /* Rewrite to get rid of the comma from rvalue @@ -8659,6 +8758,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e1x = e; } + else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp()) + { + auto die = e1x.isDotIdExp(); + e1x = fieldLookup(die.e1, sc, die.ident); + } else if (auto die = e1x.isDotIdExp()) { Expression e = die.semanticY(sc, 1); @@ -8672,10 +8776,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * In order to make sure that UFCS is tried with correct parameters, e2 * needs to have semantic ran on it. */ + auto ode = e; exp.e2 = exp.e2.expressionSemantic(sc); uint errors = global.startGagging(); e = resolvePropertiesX(sc, e, exp.e2); - if (global.endGagging(errors)) + // Any error or if 'e' is not resolved, go to UFCS + if (global.endGagging(errors) || e is ode) e = null; /* fall down to UFCS */ else return setResult(e); @@ -8717,12 +8823,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { Expression e2x = inferType(exp.e2, t1.baseElemOf()); e2x = e2x.expressionSemantic(sc); + if (!t1.isTypeSArray()) + e2x = e2x.arrayFuncConv(sc); e2x = resolveProperties(sc, e2x); if (e2x.op == TOK.type) e2x = resolveAliasThis(sc, e2x); //https://issues.dlang.org/show_bug.cgi?id=17684 if (e2x.op == TOK.error) return setResult(e2x); - // We skip checking the value for structs/classes as these might have + // We delay checking the value for structs/classes as these might have // an opAssign defined. if ((t1.ty != Tstruct && t1.ty != Tclass && e2x.checkValue()) || e2x.checkSharedAccess(sc)) @@ -9208,6 +9316,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else assert(exp.op == TOK.blit); + if (e2x.checkValue()) + return setError(); + exp.e1 = e1x; exp.e2 = e2x; } @@ -9223,6 +9334,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } + if (exp.e2.checkValue()) + return setError(); } else if (t1.ty == Tsarray) { @@ -9672,7 +9785,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = exp.e1.type; assert(exp.type); auto res = exp.op == TOK.assign ? exp.reorderSettingAAElem(sc) : exp; - checkAssignEscape(sc, res, false); + Expression tmp; + /* https://issues.dlang.org/show_bug.cgi?id=22366 + * + * `reorderSettingAAElem` creates a tree of comma expressions, however, + * `checkAssignExp` expects only AssignExps. + */ + checkAssignEscape(sc, Expression.extractLast(res, tmp), false); return setResult(res); } @@ -9944,6 +10063,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + /* ImportC: convert arrays to pointers, functions to pointers to functions + */ + exp.e1 = exp.e1.arrayFuncConv(sc); + exp.e2 = exp.e2.arrayFuncConv(sc); + Type tb1 = exp.e1.type.toBasetype(); Type tb2 = exp.e2.type.toBasetype(); @@ -10045,6 +10169,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + /* ImportC: convert arrays to pointers, functions to pointers to functions + */ + exp.e1 = exp.e1.arrayFuncConv(sc); + exp.e2 = exp.e2.arrayFuncConv(sc); + Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); @@ -11539,12 +11668,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ec = ec.toBoolean(sc); CtorFlow ctorflow_root = sc.ctorflow.clone(); - Expression e1x = exp.e1.expressionSemantic(sc); + Expression e1x = exp.e1.expressionSemantic(sc).arrayFuncConv(sc); e1x = resolveProperties(sc, e1x); CtorFlow ctorflow1 = sc.ctorflow; sc.ctorflow = ctorflow_root; - Expression e2x = exp.e2.expressionSemantic(sc); + Expression e2x = exp.e2.expressionSemantic(sc).arrayFuncConv(sc); e2x = resolveProperties(sc, e2x); sc.merge(exp.loc, ctorflow1); @@ -11894,95 +12023,84 @@ Expression semanticX(DotIdExp exp, Scope* sc) if (exp.ident == Id._mangleof) { // symbol.mangleof - Dsymbol ds; - switch (exp.e1.op) + + // return mangleof as an Expression + static Expression dotMangleof(const ref Loc loc, Scope* sc, Dsymbol ds) { - case TOK.scope_: - ds = (cast(ScopeExp)exp.e1).sds; - goto L1; - case TOK.variable: - ds = (cast(VarExp)exp.e1).var; - goto L1; - case TOK.dotVariable: - ds = (cast(DotVarExp)exp.e1).var; - goto L1; - case TOK.overloadSet: - ds = (cast(OverExp)exp.e1).vars; - goto L1; - case TOK.template_: - { - TemplateExp te = cast(TemplateExp)exp.e1; - ds = te.fd ? cast(Dsymbol)te.fd : te.td; - } - L1: + assert(ds); + if (auto f = ds.isFuncDeclaration()) { - assert(ds); - if (auto f = ds.isFuncDeclaration()) + if (f.checkForwardRef(loc)) + return ErrorExp.get(); + + if (f.flags & (FUNCFLAG.purityInprocess | FUNCFLAG.safetyInprocess | + FUNCFLAG.nothrowInprocess | FUNCFLAG.nogcInprocess)) { - if (f.checkForwardRef(exp.loc)) - { - return ErrorExp.get(); - } - if (f.flags & (FUNCFLAG.purityInprocess | FUNCFLAG.safetyInprocess | - FUNCFLAG.nothrowInprocess | FUNCFLAG.nogcInprocess)) - { - f.error(exp.loc, "cannot retrieve its `.mangleof` while inferring attributes"); - return ErrorExp.get(); - } + f.error(loc, "cannot retrieve its `.mangleof` while inferring attributes"); + return ErrorExp.get(); } - OutBuffer buf; - mangleToBuffer(ds, &buf); - Expression e = new StringExp(exp.loc, buf.extractSlice()); - e = e.expressionSemantic(sc); - return e; } - default: - break; + OutBuffer buf; + mangleToBuffer(ds, &buf); + Expression e = new StringExp(loc, buf.extractSlice()); + return e.expressionSemantic(sc); + } + + Dsymbol ds; + switch (exp.e1.op) + { + case TOK.scope_: return dotMangleof(exp.loc, sc, exp.e1.isScopeExp().sds); + case TOK.variable: return dotMangleof(exp.loc, sc, exp.e1.isVarExp().var); + case TOK.dotVariable: return dotMangleof(exp.loc, sc, exp.e1.isDotVarExp().var); + case TOK.overloadSet: return dotMangleof(exp.loc, sc, exp.e1.isOverExp().vars); + case TOK.template_: + { + TemplateExp te = exp.e1.isTemplateExp(); + return dotMangleof(exp.loc, sc, ds = te.fd ? te.fd.isDsymbol() : te.td); + } + + default: + break; } } - if (exp.e1.op == TOK.variable && exp.e1.type.toBasetype().ty == Tsarray && exp.ident == Id.length) + if (exp.e1.isVarExp() && exp.e1.type.toBasetype().isTypeSArray() && exp.ident == Id.length) { // bypass checkPurity return exp.e1.type.dotExp(sc, exp.e1, exp.ident, exp.noderef ? DotExpFlag.noDeref : 0); } - if (exp.e1.op == TOK.dot) - { - } - else + if (!exp.e1.isDotExp()) { exp.e1 = resolvePropertiesX(sc, exp.e1); } - if (exp.e1.op == TOK.tuple && exp.ident == Id.offsetof) + + if (auto te = exp.e1.isTupleExp()) { - /* 'distribute' the .offsetof to each of the tuple elements. - */ - TupleExp te = cast(TupleExp)exp.e1; - auto exps = new Expressions(te.exps.dim); - for (size_t i = 0; i < exps.dim; i++) + if (exp.ident == Id.offsetof) { - Expression e = (*te.exps)[i]; + /* 'distribute' the .offsetof to each of the tuple elements. + */ + auto exps = new Expressions(te.exps.dim); + foreach (i, e; (*te.exps)[]) + { + (*exps)[i] = new DotIdExp(e.loc, e, Id.offsetof); + } + // Don't evaluate te.e0 in runtime + Expression e = new TupleExp(exp.loc, null, exps); e = e.expressionSemantic(sc); - e = new DotIdExp(e.loc, e, Id.offsetof); - (*exps)[i] = e; + return e; + } + if (exp.ident == Id.length) + { + // Don't evaluate te.e0 in runtime + return new IntegerExp(exp.loc, te.exps.dim, Type.tsize_t); } - // Don't evaluate te.e0 in runtime - Expression e = new TupleExp(exp.loc, null, exps); - e = e.expressionSemantic(sc); - return e; - } - if (exp.e1.op == TOK.tuple && exp.ident == Id.length) - { - TupleExp te = cast(TupleExp)exp.e1; - // Don't evaluate te.e0 in runtime - Expression e = new IntegerExp(exp.loc, te.exps.dim, Type.tsize_t); - return e; } // https://issues.dlang.org/show_bug.cgi?id=14416 // Template has no built-in properties except for 'stringof'. - if ((exp.e1.op == TOK.dotTemplateDeclaration || exp.e1.op == TOK.template_) && exp.ident != Id.stringof) + if ((exp.e1.isDotTemplateExp() || exp.e1.isTemplateExp()) && exp.ident != Id.stringof) { exp.error("template `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars()); return ErrorExp.get(); @@ -11996,8 +12114,15 @@ Expression semanticX(DotIdExp exp, Scope* sc) return exp; } -// Resolve e1.ident without seeing UFCS. -// If flag == 1, stop "not a property" error and return NULL. +/****************************** + * Resolve properties, i.e. `e1.ident`, without seeing UFCS. + * Params: + * exp = expression to resolve + * sc = context + * flag = if 1 then do not emit error messages, just return null + * Returns: + * resolved expression, null if error + */ Expression semanticY(DotIdExp exp, Scope* sc, int flag) { //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); @@ -12008,32 +12133,35 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) * to be classtype.id and baseclasstype.id * if we have no this pointer. */ - if ((exp.e1.op == TOK.this_ || exp.e1.op == TOK.super_) && !hasThis(sc)) + if ((exp.e1.isThisExp() || exp.e1.isSuperExp()) && !hasThis(sc)) { if (AggregateDeclaration ad = sc.getStructClassScope()) { - if (exp.e1.op == TOK.this_) + if (exp.e1.isThisExp()) { exp.e1 = new TypeExp(exp.e1.loc, ad.type); } else { - ClassDeclaration cd = ad.isClassDeclaration(); - if (cd && cd.baseClass) - exp.e1 = new TypeExp(exp.e1.loc, cd.baseClass.type); + if (auto cd = ad.isClassDeclaration()) + { + if (cd.baseClass) + exp.e1 = new TypeExp(exp.e1.loc, cd.baseClass.type); + } } } } - Expression e = semanticX(exp, sc); - if (e != exp) - return e; + { + Expression e = semanticX(exp, sc); + if (e != exp) + return e; + } Expression eleft; Expression eright; - if (exp.e1.op == TOK.dot) + if (auto de = exp.e1.isDotExp()) { - DotExp de = cast(DotExp)exp.e1; eleft = de.e1; eright = de.e2; } @@ -12045,11 +12173,9 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) Type t1b = exp.e1.type.toBasetype(); - if (eright.op == TOK.scope_) // also used for template alias's + if (auto ie = eright.isScopeExp()) // also used for template alias's { - ScopeExp ie = cast(ScopeExp)eright; - - int flags = SearchLocalsOnly; + auto flags = SearchLocalsOnly; /* Disable access to another module's private imports. * The check for 'is sds our current module' is because * the current module should have access to its own imports. @@ -12082,13 +12208,11 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) exp.checkDeprecated(sc, s); exp.checkDisabled(sc, s); - EnumMember em = s.isEnumMember(); - if (em) + if (auto em = s.isEnumMember()) { return em.getVarExp(exp.loc, sc); } - VarDeclaration v = s.isVarDeclaration(); - if (v) + if (auto v = s.isVarDeclaration()) { //printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v.type.toChars()); if (!v.type || @@ -12100,7 +12224,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) exp.error("forward reference to %s `%s`", v.kind(), v.toPrettyChars()); return ErrorExp.get(); } - if (v.type.ty == Terror) + if (v.type.isTypeError()) return ErrorExp.get(); if ((v.storage_class & STC.manifest) && v._init && !exp.wantsym) @@ -12114,13 +12238,14 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) error(exp.loc, "circular initialization of %s `%s`", v.kind(), v.toPrettyChars()); return ErrorExp.get(); } - e = v.expandInitializer(exp.loc); + auto e = v.expandInitializer(exp.loc); v.inuse++; e = e.expressionSemantic(sc); v.inuse--; return e; } + Expression e; if (v.needThis()) { if (!eleft) @@ -12141,12 +12266,12 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) return e.expressionSemantic(sc); } - FuncDeclaration f = s.isFuncDeclaration(); - if (f) + if (auto f = s.isFuncDeclaration()) { //printf("it's a function\n"); if (!f.functionSemantic()) return ErrorExp.get(); + Expression e; if (f.needThis()) { if (!eleft) @@ -12167,6 +12292,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) } if (auto td = s.isTemplateDeclaration()) { + Expression e; if (eleft) e = new DotTemplateExp(exp.loc, eleft, td); else @@ -12176,7 +12302,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) } if (OverDeclaration od = s.isOverDeclaration()) { - e = new VarExp(exp.loc, od, true); + Expression e = new VarExp(exp.loc, od, true); if (eleft) { e = new CommaExp(exp.loc, eleft, e); @@ -12184,8 +12310,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) } return e; } - OverloadSet o = s.isOverloadSet(); - if (o) + if (auto o = s.isOverloadSet()) { //printf("'%s' is an overload set\n", o.toChars()); return new OverExp(exp.loc, o); @@ -12196,36 +12321,33 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) return (new TypeExp(exp.loc, t)).expressionSemantic(sc); } - TupleDeclaration tup = s.isTupleDeclaration(); - if (tup) + if (auto tup = s.isTupleDeclaration()) { if (eleft) { - e = new DotVarExp(exp.loc, eleft, tup); + Expression e = new DotVarExp(exp.loc, eleft, tup); e = e.expressionSemantic(sc); return e; } - e = new TupleExp(exp.loc, tup); + Expression e = new TupleExp(exp.loc, tup); e = e.expressionSemantic(sc); return e; } - ScopeDsymbol sds = s.isScopeDsymbol(); - if (sds) + if (auto sds = s.isScopeDsymbol()) { //printf("it's a ScopeDsymbol %s\n", ident.toChars()); - e = new ScopeExp(exp.loc, sds); + Expression e = new ScopeExp(exp.loc, sds); e = e.expressionSemantic(sc); if (eleft) e = new DotExp(exp.loc, eleft, e); return e; } - Import imp = s.isImport(); - if (imp) + if (auto imp = s.isImport()) { - ie = new ScopeExp(exp.loc, imp.pkg); - return ie.expressionSemantic(sc); + Expression se = new ScopeExp(exp.loc, imp.pkg); + return se.expressionSemantic(sc); } // BUG: handle other cases like in IdentifierExp::semantic() debug @@ -12236,7 +12358,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) } else if (exp.ident == Id.stringof) { - e = new StringExp(exp.loc, ie.toString()); + Expression e = new StringExp(exp.loc, ie.toString()); e = e.expressionSemantic(sc); return e; } @@ -12263,9 +12385,11 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) Type t1bn = t1b.nextOf(); if (flag) { - AggregateDeclaration ad = isAggregate(t1bn); - if (ad && !ad.members) // https://issues.dlang.org/show_bug.cgi?id=11312 - return null; + if (AggregateDeclaration ad = isAggregate(t1bn)) + { + if (!ad.members) // https://issues.dlang.org/show_bug.cgi?id=11312 + return null; + } } /* Rewrite: @@ -12275,27 +12399,27 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) */ if (flag && t1bn.ty == Tvoid) return null; - e = new PtrExp(exp.loc, exp.e1); + Expression e = new PtrExp(exp.loc, exp.e1); e = e.expressionSemantic(sc); return e.type.dotExp(sc, e, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); } else if (exp.ident == Id.__xalignof && exp.e1.isVarExp() && exp.e1.isVarExp().var.isVarDeclaration() && - exp.e1.isVarExp().var.isVarDeclaration().alignment) + !exp.e1.isVarExp().var.isVarDeclaration().alignment.isUnknown()) { // For `x.alignof` get the alignment of the variable, not the alignment of its type const explicitAlignment = exp.e1.isVarExp().var.isVarDeclaration().alignment; const naturalAlignment = exp.e1.type.alignsize(); - const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment); - e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t); + const actualAlignment = explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get(); + Expression e = new IntegerExp(exp.loc, actualAlignment, Type.tsize_t); return e; } else { - if (exp.e1.op == TOK.type || exp.e1.op == TOK.template_) + if (exp.e1.isTypeExp() || exp.e1.isTemplateExp()) flag = 0; - e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); + Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag | (exp.noderef ? DotExpFlag.noDeref : 0)); if (e) e = e.expressionSemantic(sc); return e; @@ -12875,7 +12999,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions const hasPointers = tb.hasPointers(); if (hasPointers) { - if ((stype.alignment() < target.ptrsize || + if ((!stype.alignment.isDefault() && stype.alignment.get() < target.ptrsize || (v.offset & (target.ptrsize - 1))) && (sc.func && sc.func.setUnsafe())) { diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d new file mode 100644 index 0000000..05aeb7d --- /dev/null +++ b/gcc/d/dmd/file_manager.d @@ -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(); + } +} diff --git a/gcc/d/dmd/root/root.h b/gcc/d/dmd/file_manager.h index 667ce67..7488fab1 100644 --- a/gcc/d/dmd/root/root.h +++ b/gcc/d/dmd/file_manager.h @@ -4,17 +4,16 @@ * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt - * https://github.com/dlang/dmd/blob/master/src/dmd/root/root.h + * https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.h */ #pragma once -#include "object.h" +#include "root/file.h" -#include "filename.h" - -#include "file.h" - -#include "outbuffer.h" - -#include "array.h" +struct FileManager +{ + static void _init(); + FileBuffer* lookup(const char* filename); + FileBuffer* add(const char* filename, FileBuffer* filebuffer); +}; diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 7f0b0bb..2d6a756 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -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; } diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index 9b65d02..747a113 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -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: diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index 6e79474..2275ec5 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -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(); diff --git a/gcc/d/dmd/gluelayer.d b/gcc/d/dmd/gluelayer.d index debb9ca..4018126 100644 --- a/gcc/d/dmd/gluelayer.d +++ b/gcc/d/dmd/gluelayer.d @@ -78,6 +78,7 @@ else version (IN_GCC) extern (C++) { Statement asmSemantic(AsmStatement s, Scope* sc); + void toObjFile(Dsymbol ds, bool multiobj); } // stubs diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 8c31590..4ff07b5 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -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()); } diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d index e61fb23..3ff0e89 100644 --- a/gcc/d/dmd/iasmgcc.d +++ b/gcc/d/dmd/iasmgcc.d @@ -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); diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index 1f04dcf..1ee5153 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -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" }, ]; diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d index 43a1435..8add74a 100644 --- a/gcc/d/dmd/identifier.d +++ b/gcc/d/dmd/identifier.d @@ -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; diff --git a/gcc/d/dmd/importc.d b/gcc/d/dmd/importc.d new file mode 100644 index 0000000..0dad1a8 --- /dev/null +++ b/gcc/d/dmd/importc.d @@ -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); +} diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d index 45e101b..d036ee1 100644 --- a/gcc/d/dmd/init.d +++ b/gcc/d/dmd/init.d @@ -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; diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index ae8bde2..5828486 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -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; } - - - diff --git a/gcc/d/dmd/intrange.h b/gcc/d/dmd/intrange.h deleted file mode 100644 index fd61532..0000000 --- a/gcc/d/dmd/intrange.h +++ /dev/null @@ -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; -}; diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index bfd31bc..fef5150 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -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(); } diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d index d29bdc1..44a6c06 100644 --- a/gcc/d/dmd/lambdacomp.d +++ b/gcc/d/dmd/lambdacomp.d @@ -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; diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index afffc2d..e2b4199 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -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; diff --git a/gcc/d/dmd/lexer.h b/gcc/d/dmd/lexer.h deleted file mode 100644 index b36e7f7..0000000 --- a/gcc/d/dmd/lexer.h +++ /dev/null @@ -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(); -}; diff --git a/gcc/d/dmd/macro.h b/gcc/d/dmd/macro.h deleted file mode 100644 index 80ec36e..0000000 --- a/gcc/d/dmd/macro.h +++ /dev/null @@ -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); -}; diff --git a/gcc/d/dmd/mars.h b/gcc/d/dmd/mars.h deleted file mode 100644 index 9b9c278..0000000 --- a/gcc/d/dmd/mars.h +++ /dev/null @@ -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); diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index 969290c..fe4c73a 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -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. diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 80e4791..a21924b 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -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); diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index cdf221f..430b39b 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -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); diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d index 7719ccf..605e9f3 100644 --- a/gcc/d/dmd/ob.d +++ b/gcc/d/dmd/ob.d @@ -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. diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index 85e371e..eb4ba1d 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -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; diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index 4ef55f3..ff03a6e 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -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 diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index 3ae3061..9f116fe 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -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. diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 21042dd..f00ceb6 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -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; diff --git a/gcc/d/dmd/parse.h b/gcc/d/dmd/parse.h deleted file mode 100644 index a2ad478..0000000 --- a/gcc/d/dmd/parse.h +++ /dev/null @@ -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(); diff --git a/gcc/d/dmd/printast.d b/gcc/d/dmd/printast.d index 3f12b17..414d6f6 100644 --- a/gcc/d/dmd/printast.d +++ b/gcc/d/dmd/printast.d @@ -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() : ""); diff --git a/gcc/d/dmd/root/README.md b/gcc/d/dmd/root/README.md index 539b940..e062d93 100644 --- a/gcc/d/dmd/root/README.md +++ b/gcc/d/dmd/root/README.md @@ -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 | diff --git a/gcc/d/dmd/root/aav.h b/gcc/d/dmd/root/aav.h deleted file mode 100644 index c65b674..0000000 --- a/gcc/d/dmd/root/aav.h +++ /dev/null @@ -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); - diff --git a/gcc/d/dmd/root/checkedint.h b/gcc/d/dmd/root/checkedint.h deleted file mode 100644 index 8a7d9c9..0000000 --- a/gcc/d/dmd/root/checkedint.h +++ /dev/null @@ -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); diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d index ef6056c..64e9571 100644 --- a/gcc/d/dmd/root/file.d +++ b/gcc/d/dmd/root/file.d @@ -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)(); -} diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index 1e4ccb5..d1500c8 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -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: @@ -1223,33 +1159,6 @@ version(Windows) } /********************************** - * 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 * function on it. @@ -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); diff --git a/gcc/d/dmd/root/hash.h b/gcc/d/dmd/root/hash.h deleted file mode 100644 index 6a32200..0000000 --- a/gcc/d/dmd/root/hash.h +++ /dev/null @@ -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)); -} diff --git a/gcc/d/dmd/root/rootobject.d b/gcc/d/dmd/root/rootobject.d index 854ec1a..64104b8 100644 --- a/gcc/d/dmd/root/rootobject.d +++ b/gcc/d/dmd/root/rootobject.d @@ -13,7 +13,7 @@ module dmd.root.rootobject; import core.stdc.stdio; -import dmd.root.outbuffer; +import dmd.common.outbuffer; /*********************************************************** */ diff --git a/gcc/d/dmd/root/speller.h b/gcc/d/dmd/root/speller.h deleted file mode 100644 index bd53fc4..0000000 --- a/gcc/d/dmd/root/speller.h +++ /dev/null @@ -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); - diff --git a/gcc/d/dmd/root/stringtable.h b/gcc/d/dmd/root/stringtable.h deleted file mode 100644 index 51304d3..0000000 --- a/gcc/d/dmd/root/stringtable.h +++ /dev/null @@ -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(); -}; diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index 35734b2..8904959 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -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()) { diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index 7b2fa5e..993db90 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -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. diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index ac2b239..3852d0b 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -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 && diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index b49c903..91e3fe7 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -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); } diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index 7825762..98b7bd3 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -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; diff --git a/gcc/d/dmd/statement_rewrite_walker.h b/gcc/d/dmd/statement_rewrite_walker.h deleted file mode 100644 index 28a930a..0000000 --- a/gcc/d/dmd/statement_rewrite_walker.h +++ /dev/null @@ -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 *) { } -}; - diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index f067c91..9eba2ff 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -54,7 +54,7 @@ import dmd.nogc; import dmd.opover; import dmd.parse; import dmd.printast; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.string; import dmd.semantic2; import dmd.sideeffect; @@ -659,20 +659,6 @@ private extern (C++) final class StatementSemanticVisitor : Visitor result = fs; } - /******************* - * Determines the return type of makeTupleForeach. - */ - private static template MakeTupleForeachRet(bool isDecl) - { - static if(isDecl) - { - alias MakeTupleForeachRet = Dsymbols*; - } - else - { - alias MakeTupleForeachRet = void; - } - } /******************* * Type check and unroll `foreach` over an expression tuple as well @@ -696,29 +682,24 @@ private extern (C++) final class StatementSemanticVisitor : Visitor * expands the tuples into multiple `STC.local` `static foreach` * variables. */ - MakeTupleForeachRet!isDecl makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args) + auto makeTupleForeach(bool isStatic, bool isDecl)(ForeachStatement fs, Dsymbols* dbody, bool needExpansion) { - auto returnEarly() - { - static if (isDecl) - { - return null; - } - else - { - result = new ErrorStatement(); - return; - } - } - static if(isDecl) + // Voldemort return type + union U { - static assert(isStatic); - auto dbody = args[0]; + Statement statement; + Dsymbols* decl; } - static if(isStatic) + + U result; + + auto returnEarly() { - auto needExpansion = args[$-1]; - assert(sc); + if (isDecl) + result.decl = null; + else + result.statement = new ErrorStatement(); + return result; } auto loc = fs.loc; @@ -827,7 +808,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor } Initializer ie = new ExpInitializer(Loc.initial, new IntegerExp(k)); auto var = new VarDeclaration(loc, p.type, p.ident, ie); - var.storage_class |= STC.manifest; + var.storage_class |= STC.foreach_ | STC.manifest; static if(isStatic) var.storage_class |= STC.local; static if(!isDecl) { @@ -919,8 +900,9 @@ private extern (C++) final class StatementSemanticVisitor : Visitor e = resolveProperties(sc, e); Initializer ie = new ExpInitializer(Loc.initial, e); auto v = new VarDeclaration(loc, type, ident, ie, storageClass); + v.storage_class |= STC.foreach_; if (storageClass & STC.ref_) - v.storage_class |= STC.ref_ | STC.foreach_; + v.storage_class |= STC.ref_; if (isStatic || storageClass&STC.manifest || e.isConst() || e.op == TOK.string_ || e.op == TOK.structLiteral || @@ -1057,23 +1039,17 @@ private extern (C++) final class StatementSemanticVisitor : Visitor ls.gotoTarget = res; if (te && te.e0) res = new CompoundStatement(loc, new ExpStatement(te.e0.loc, te.e0), res); + result.statement = res; } else static if (!isDecl) { - Statement res = new CompoundStatement(loc, statements); - } - else - { - auto res = declarations; - } - static if (!isDecl) - { - result = res; + result.statement = new CompoundStatement(loc, statements); } else { - return res; + result.decl = declarations; } + return result; } override void visit(ForeachStatement fs) @@ -1202,10 +1178,10 @@ private extern (C++) final class StatementSemanticVisitor : Visitor if (tab.ty == Ttuple) // don't generate new scope for tuple loops { - makeTupleForeach!(false,false)(fs); + Statement s = makeTupleForeach!(false,false)(fs, null, false).statement; if (vinit) - result = new CompoundStatement(loc, new ExpStatement(loc, vinit), result); - result = result.statementSemantic(sc); + s = new CompoundStatement(loc, new ExpStatement(loc, vinit), s); + result = s.statementSemantic(sc); return; } @@ -2727,7 +2703,8 @@ private extern (C++) final class StatementSemanticVisitor : Visitor needswitcherror = true; } - if (!sc.sw.sdefault && (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on)) + if (!sc.sw.sdefault && !(sc.flags & SCOPE.Cfile) && + (!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on)) { ss.hasNoDefault = 1; @@ -3061,7 +3038,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor if (lval - fval > 256) { - crs.error("had %llu cases which is more than 256 cases in case range", lval - fval); + crs.error("had %llu cases which is more than 257 cases in case range", 1 + lval - fval); errors = true; lval = fval + 256; } @@ -3295,12 +3272,14 @@ private extern (C++) final class StatementSemanticVisitor : Visitor if (e0) e0 = e0.optimize(WANTvalue); - /* Void-return function can have void typed expression + /* Void-return function can have void / noreturn typed expression * on return statement. */ - if (tbret && tbret.ty == Tvoid || rs.exp.type.ty == Tvoid) + const convToVoid = rs.exp.type.ty == Tvoid || rs.exp.type.ty == Tnoreturn; + + if (tbret && tbret.ty == Tvoid || convToVoid) { - if (rs.exp.type.ty != Tvoid) + if (!convToVoid) { rs.error("cannot return non-void from `void` function"); errors = true; @@ -3345,7 +3324,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor } else if (rs.exp.op != TOK.error) { - rs.error("Expected return type of `%s`, not `%s`:", + rs.error("expected return type of `%s`, not `%s`:", tret.toChars(), rs.exp.type.toChars()); errorSupplemental((fd.returns) ? (*fd.returns)[0].loc : fd.loc, @@ -3409,10 +3388,20 @@ private extern (C++) final class StatementSemanticVisitor : Visitor } else { + // Type of the returned expression (if any), might've been moved to e0 + auto resType = e0 ? e0.type : Type.tvoid; + // infer return type if (fd.inferRetType) { - if (tf.next && tf.next.ty != Tvoid) + // 1. First `return <noreturn exp>?` + // 2. Potentially found a returning branch, update accordingly + if (!tf.next || tf.next.toBasetype().isTypeNoreturn()) + { + tf.next = resType; // infer void or noreturn + } + // Found an actual return value before + else if (tf.next.ty != Tvoid && !resType.toBasetype().isTypeNoreturn()) { if (tf.next.ty != Terror) { @@ -3421,20 +3410,23 @@ private extern (C++) final class StatementSemanticVisitor : Visitor errors = true; tf.next = Type.terror; } - else - tf.next = Type.tvoid; - tret = tf.next; + tret = tf.next; tbret = tret.toBasetype(); } if (inferRef) // deduce 'auto ref' tf.isref = false; - if (tbret.ty != Tvoid) // if non-void return + if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return { if (tbret.ty != Terror) - rs.error("`return` expression expected"); + { + if (e0) + rs.error("expected return type of `%s`, not `%s`", tret.toChars(), resType.toChars()); + else + rs.error("`return` expression expected"); + } errors = true; } else if (fd.isMain()) @@ -3522,7 +3514,12 @@ private extern (C++) final class StatementSemanticVisitor : Visitor } else { - result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs); + auto es = new ExpStatement(rs.loc, e0); + if (e0.type.isTypeNoreturn()) + result = es; // Omit unreachable return; + else + result = new CompoundStatement(rs.loc, es, rs); + return; } } @@ -4014,7 +4011,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor /* If catch exception type is derived from Exception */ if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && - (!c.handler || !c.handler.comeFrom())) + (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_)) { // Remove c from the array of catches tcs.catches.remove(i); @@ -4570,44 +4567,13 @@ 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) { diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d index 2f27414..d1578ec 100644 --- a/gcc/d/dmd/staticcond.d +++ b/gcc/d/dmd/staticcond.d @@ -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; diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index d5b3de2..16739ad 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -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 diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index 83281a6..6a75ccc 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -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 diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index 08ce9ac..69cc84f 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -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); } diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index 7680fb8..1ea51a8 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -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; diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index 0fd6634..d14d0aa 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -187,6 +187,7 @@ enum TOKarrow, TOKcolonColon, TOKwchar_tLiteral, + TOKcompoundLiteral, TOKinline, TOKregister, diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index 8f968ed..cc1d2e3 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -49,7 +49,7 @@ import dmd.tokens; import dmd.typesem; import dmd.visitor; import dmd.root.rootobject; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.string; enum LOGSEMANTIC = false; @@ -1410,19 +1410,30 @@ Expression semanticTraits(TraitsExp e, Scope* sc) auto o = (*e.args)[0]; auto o1 = (*e.args)[1]; - FuncDeclaration fd; - TypeFunction tf = toTypeFunction(o, fd); - ParameterList fparams; - if (tf) - fparams = tf.parameterList; - else if (fd) - fparams = fd.getParameterList(); + + CallExp ce; + if (auto exp = isExpression(o)) + ce = exp.isCallExp(); + + if (ce) + { + fparams = ce.f.getParameterList(); + } else { - e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function", - o.toChars(), o1.toChars()); - return ErrorExp.get(); + FuncDeclaration fd; + auto tf = toTypeFunction(o, fd); + if (tf) + fparams = tf.parameterList; + else if (fd) + fparams = fd.getParameterList(); + else + { + e.error("first argument to `__traits(getParameterStorageClasses, %s, %s)` is not a function or a function call", + o.toChars(), o1.toChars()); + return ErrorExp.get(); + } } // Avoid further analysis for invalid functions leading to misleading error messages diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index ace4e42..f75ae0e 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -44,6 +44,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.imphint; +import dmd.importc; import dmd.init; import dmd.initsem; import dmd.visitor; @@ -53,7 +54,7 @@ import dmd.opover; import dmd.parse; import dmd.root.ctfloat; import dmd.root.rmem; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.rootobject; import dmd.root.string; import dmd.root.stringtable; @@ -1334,6 +1335,8 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) continue; } + fparam.type = fparam.type.cAdjustParamType(sc); // adjust C array and function parameter types + Type t = fparam.type.toBasetype(); /* If fparam after semantic() turns out to be a tuple, the number of parameters may @@ -1378,6 +1381,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) stc, narg.type, narg.ident, narg.defaultArg, narg.userAttribDecl); } fparam.type = new TypeTuple(newparams); + fparam.type = fparam.type.typeSemantic(loc, argsc); } fparam.storageClass = STC.parameter; @@ -1627,7 +1631,8 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) errors = true; } - if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0) + if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 && + !(sc.flags & SCOPE.Cfile)) { .error(loc, "variadic functions with non-D linkage must have at least one parameter"); errors = true; @@ -1750,10 +1755,10 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { // if there was an error evaluating the symbol, it might actually // be a type. Avoid misleading error messages. - .error(loc, "`%s` had previous errors", mtype.toChars()); + .error(loc, "`%s` had previous errors", mtype.toChars()); } else - .error(loc, "`%s` is used as a type", mtype.toChars()); + .error(loc, "`%s` is used as a type", mtype.toChars()); return error(); } return t; @@ -2133,6 +2138,15 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) * struct S { int a; } *s; */ sd.members = mtype.members; + if (sd.semanticRun == PASS.semanticdone) + { + /* The first semantic pass marked `sd` as an opaque struct. + * Re-run semantic so that all newly assigned members are + * picked up and added to the symtab. + */ + sd.semanticRun = PASS.semantic; + sd.dsymbolSemantic(sc); + } } else { @@ -2264,7 +2278,7 @@ RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) * Returns: * the type that was merged */ -Type merge(Type type) +extern (C++) Type merge(Type type) { switch (type.ty) { @@ -2361,7 +2375,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden { const explicitAlignment = mt.alignment(); const naturalAlignment = mt.alignsize(); - const actualAlignment = (explicitAlignment == STRUCTALIGN_DEFAULT ? naturalAlignment : explicitAlignment); + const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get()); e = new IntegerExp(loc, actualAlignment, Type.tsize_t); } else if (ident == Id._init) @@ -3734,10 +3748,18 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) } /*************************************** - * Figures out what to do with an undefined member reference - * for classes and structs. - * - * If flag & 1, don't report "not a property" error and just return NULL. + * `ident` was not found as a member of `mt`. + * Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`. + * If that fails, forward to visitType(). + * Params: + * mt = class or struct + * sc = context + * e = `this` for `ident` + * ident = name of member + * flag = flag & 1, don't report "not a property" error and just return NULL. + * flag & DotExpFlag.noAliasThis, don't do 'alias this' resolution. + * Returns: + * resolved expression if found, otherwise null */ Expression noMember(Type mt, Scope* sc, Expression e, Identifier ident, int flag) { @@ -3828,7 +3850,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) /* See if we should forward to the alias this. */ - auto alias_e = resolveAliasThis(sc, e, gagError); + auto alias_e = flag & DotExpFlag.noAliasThis ? null + : resolveAliasThis(sc, e, gagError); if (alias_e && alias_e != e) { /* Rewrite e.ident as: @@ -4611,7 +4634,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, int flag) * Returns: * The initialization expression for the type. */ -Expression defaultInit(Type mt, const ref Loc loc) +extern (C++) Expression defaultInit(Type mt, const ref Loc loc) { Expression visitBasic(TypeBasic mt) { @@ -4744,6 +4767,7 @@ Expression defaultInit(Type mt, const ref Loc loc) } auto cond = IntegerExp.createBool(false); auto msg = new StringExp(loc, "Accessed expression of type `noreturn`"); + msg.type = Type.tstring; auto ae = new AssertExp(loc, cond, msg); ae.type = mt; return ae; diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d index d8160f0..d05af61 100644 --- a/gcc/d/dmd/typinf.d +++ b/gcc/d/dmd/typinf.d @@ -1,5 +1,5 @@ /** - * Generate `TypeInfo` objects, which are needed for run-time introspection of classes. + * Generate `TypeInfo` objects, which are needed for run-time introspection of types. * * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) @@ -11,9 +11,87 @@ module dmd.typinf; +import dmd.astenums; +import dmd.declaration; +import dmd.dmodule; import dmd.dscope; +import dmd.dclass; +import dmd.dstruct; +import dmd.errors; import dmd.globals; +import dmd.gluelayer; import dmd.mtype; +import dmd.visitor; +import core.stdc.stdio; + +/**************************************************** + * Generates the `TypeInfo` object associated with `torig` if it + * hasn't already been generated + * Params: + * loc = the location for reporting line numbers in errors + * torig = the type to generate the `TypeInfo` object for + * sc = the scope + */ +extern (C++) void genTypeInfo(const ref Loc loc, Type torig, Scope* sc) +{ + // printf("genTypeInfo() %s\n", torig.toChars()); + + // Even when compiling without `useTypeInfo` (e.g. -betterC) we should + // still be able to evaluate `TypeInfo` at compile-time, just not at runtime. + // https://issues.dlang.org/show_bug.cgi?id=18472 + if (!sc || !(sc.flags & SCOPE.ctfe)) + { + if (!global.params.useTypeInfo) + { + .error(loc, "`TypeInfo` cannot be used with -betterC"); + fatal(); + } + } + + if (!Type.dtypeinfo) + { + .error(loc, "`object.TypeInfo` could not be found, but is implicitly used"); + fatal(); + } + + Type t = torig.merge2(); // do this since not all Type's are merge'd + if (!t.vtinfo) + { + if (t.isShared()) // does both 'shared' and 'shared const' + t.vtinfo = TypeInfoSharedDeclaration.create(t); + else if (t.isConst()) + t.vtinfo = TypeInfoConstDeclaration.create(t); + else if (t.isImmutable()) + t.vtinfo = TypeInfoInvariantDeclaration.create(t); + else if (t.isWild()) + t.vtinfo = TypeInfoWildDeclaration.create(t); + else + t.vtinfo = getTypeInfoDeclaration(t); + assert(t.vtinfo); + + // ClassInfos are generated as part of ClassDeclaration codegen + const isUnqualifiedClassInfo = (t.ty == Tclass && !t.mod); + + // generate a COMDAT for other TypeInfos not available as builtins in + // druntime + if (!isUnqualifiedClassInfo && !builtinTypeInfo(t)) + { + if (sc) // if in semantic() pass + { + // Find module that will go all the way to an object file + Module m = sc._module.importedFrom; + m.members.push(t.vtinfo); + } + else // if in obj generation pass + { + toObjFile(t.vtinfo, global.params.multiobj); + } + } + } + if (!torig.vtinfo) + torig.vtinfo = t.vtinfo; // Types aren't merged, but we can share the vtinfo's + assert(torig.vtinfo); +} /**************************************************** * Gets the type of the `TypeInfo` object associated with `t` @@ -24,5 +102,161 @@ import dmd.mtype; * Returns: * The type of the `TypeInfo` object associated with `t` */ -extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc); +extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc); + +private TypeInfoDeclaration getTypeInfoDeclaration(Type t) +{ + //printf("Type::getTypeInfoDeclaration() %s\n", t.toChars()); + switch (t.ty) + { + case Tpointer: + return TypeInfoPointerDeclaration.create(t); + case Tarray: + return TypeInfoArrayDeclaration.create(t); + case Tsarray: + return TypeInfoStaticArrayDeclaration.create(t); + case Taarray: + return TypeInfoAssociativeArrayDeclaration.create(t); + case Tstruct: + return TypeInfoStructDeclaration.create(t); + case Tvector: + return TypeInfoVectorDeclaration.create(t); + case Tenum: + return TypeInfoEnumDeclaration.create(t); + case Tfunction: + return TypeInfoFunctionDeclaration.create(t); + case Tdelegate: + return TypeInfoDelegateDeclaration.create(t); + case Ttuple: + return TypeInfoTupleDeclaration.create(t); + case Tclass: + if ((cast(TypeClass)t).sym.isInterfaceDeclaration()) + return TypeInfoInterfaceDeclaration.create(t); + else + return TypeInfoClassDeclaration.create(t); + + default: + return TypeInfoDeclaration.create(t); + } +} + +/************************************************** + * Returns: + * true if any part of type t is speculative. + * if t is null, returns false. + */ +bool isSpeculativeType(Type t) +{ + static bool visitVector(TypeVector t) + { + return isSpeculativeType(t.basetype); + } + + static bool visitAArray(TypeAArray t) + { + return isSpeculativeType(t.index) || + isSpeculativeType(t.next); + } + + static bool visitStruct(TypeStruct t) + { + StructDeclaration sd = t.sym; + if (auto ti = sd.isInstantiated()) + { + if (!ti.needsCodegen()) + { + if (ti.minst || sd.requestTypeInfo) + return false; + + /* https://issues.dlang.org/show_bug.cgi?id=14425 + * TypeInfo_Struct would refer the members of + * struct (e.g. opEquals via xopEquals field), so if it's instantiated + * in speculative context, TypeInfo creation should also be + * stopped to avoid 'unresolved symbol' linker errors. + */ + /* When -debug/-unittest is specified, all of non-root instances are + * automatically changed to speculative, and here is always reached + * from those instantiated non-root structs. + * Therefore, if the TypeInfo is not auctually requested, + * we have to elide its codegen. + */ + return true; + } + } + else + { + //assert(!sd.inNonRoot() || sd.requestTypeInfo); // valid? + } + return false; + } + + static bool visitClass(TypeClass t) + { + ClassDeclaration sd = t.sym; + if (auto ti = sd.isInstantiated()) + { + if (!ti.needsCodegen() && !ti.minst) + { + return true; + } + } + return false; + } + + static bool visitTuple(TypeTuple t) + { + if (t.arguments) + { + foreach (arg; *t.arguments) + { + if (isSpeculativeType(arg.type)) + return true; + } + } + return false; + } + + if (!t) + return false; + Type tb = t.toBasetype(); + switch (tb.ty) + { + case Tvector: return visitVector(tb.isTypeVector()); + case Taarray: return visitAArray(tb.isTypeAArray()); + case Tstruct: return visitStruct(tb.isTypeStruct()); + case Tclass: return visitClass(tb.isTypeClass()); + case Ttuple: return visitTuple(tb.isTypeTuple()); + case Tenum: return false; + default: + return isSpeculativeType(tb.nextOf()); + + /* For TypeFunction, TypeInfo_Function doesn't store parameter types, + * so only the .next (the return type) is checked here. + */ + } +} + +/* ========================================================================= */ + +/* Indicates whether druntime already contains an appropriate TypeInfo instance + * for the specified type (in module rt.util.typeinfo). + */ +extern (C++) bool builtinTypeInfo(Type t) +{ + if (!t.mod) // unqualified types only + { + // unqualified basic types + typeof(null) + if (t.isTypeBasic() || t.ty == Tnull) + return true; + // some unqualified arrays + if (t.ty == Tarray) + { + Type next = t.nextOf(); + return (next.isTypeBasic() && !next.mod) // of unqualified basic types + || (next.ty == Tchar && next.mod == MODFlags.immutable_) // string + || (next.ty == Tchar && next.mod == MODFlags.const_); // const(char)[] + } + } + return false; +} diff --git a/gcc/d/dmd/utf.h b/gcc/d/dmd/utf.h deleted file mode 100644 index b3782af..0000000 --- a/gcc/d/dmd/utf.h +++ /dev/null @@ -1,117 +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/utf.h - */ - -#pragma once - -#include "root/dsystem.h" - -/// A UTF-8 code unit -typedef unsigned char utf8_t; -/// A UTF-16 code unit -typedef unsigned short utf16_t; -/// A UTF-32 code unit -typedef unsigned int utf32_t; -typedef utf32_t dchar_t; - -#define ALPHA_TABLE_LENGTH 245 -static utf16_t const ALPHA_TABLE[ALPHA_TABLE_LENGTH][2] = -{ - { 0x00AA, 0x00AA }, { 0x00B5, 0x00B5 }, { 0x00B7, 0x00B7 }, { 0x00BA, 0x00BA }, - { 0x00C0, 0x00D6 }, { 0x00D8, 0x00F6 }, { 0x00F8, 0x01F5 }, { 0x01FA, 0x0217 }, - { 0x0250, 0x02A8 }, { 0x02B0, 0x02B8 }, { 0x02BB, 0x02BB }, { 0x02BD, 0x02C1 }, - { 0x02D0, 0x02D1 }, { 0x02E0, 0x02E4 }, { 0x037A, 0x037A }, { 0x0386, 0x0386 }, - { 0x0388, 0x038A }, { 0x038C, 0x038C }, { 0x038E, 0x03A1 }, { 0x03A3, 0x03CE }, - { 0x03D0, 0x03D6 }, { 0x03DA, 0x03DA }, { 0x03DC, 0x03DC }, { 0x03DE, 0x03DE }, - { 0x03E0, 0x03E0 }, { 0x03E2, 0x03F3 }, { 0x0401, 0x040C }, { 0x040E, 0x044F }, - { 0x0451, 0x045C }, { 0x045E, 0x0481 }, { 0x0490, 0x04C4 }, { 0x04C7, 0x04C8 }, - { 0x04CB, 0x04CC }, { 0x04D0, 0x04EB }, { 0x04EE, 0x04F5 }, { 0x04F8, 0x04F9 }, - { 0x0531, 0x0556 }, { 0x0559, 0x0559 }, { 0x0561, 0x0587 }, { 0x05B0, 0x05B9 }, - { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05D0, 0x05EA }, - { 0x05F0, 0x05F2 }, { 0x0621, 0x063A }, { 0x0640, 0x0652 }, { 0x0660, 0x0669 }, - { 0x0670, 0x06B7 }, { 0x06BA, 0x06BE }, { 0x06C0, 0x06CE }, { 0x06D0, 0x06DC }, - { 0x06E5, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x06F0, 0x06F9 }, { 0x0901, 0x0903 }, - { 0x0905, 0x0939 }, { 0x093D, 0x094D }, { 0x0950, 0x0952 }, { 0x0958, 0x0963 }, - { 0x0966, 0x096F }, { 0x0981, 0x0983 }, { 0x0985, 0x098C }, { 0x098F, 0x0990 }, - { 0x0993, 0x09A8 }, { 0x09AA, 0x09B0 }, { 0x09B2, 0x09B2 }, { 0x09B6, 0x09B9 }, - { 0x09BE, 0x09C4 }, { 0x09C7, 0x09C8 }, { 0x09CB, 0x09CD }, { 0x09DC, 0x09DD }, - { 0x09DF, 0x09E3 }, { 0x09E6, 0x09F1 }, { 0x0A02, 0x0A02 }, { 0x0A05, 0x0A0A }, - { 0x0A0F, 0x0A10 }, { 0x0A13, 0x0A28 }, { 0x0A2A, 0x0A30 }, { 0x0A32, 0x0A33 }, - { 0x0A35, 0x0A36 }, { 0x0A38, 0x0A39 }, { 0x0A3E, 0x0A42 }, { 0x0A47, 0x0A48 }, - { 0x0A4B, 0x0A4D }, { 0x0A59, 0x0A5C }, { 0x0A5E, 0x0A5E }, { 0x0A66, 0x0A6F }, - { 0x0A74, 0x0A74 }, { 0x0A81, 0x0A83 }, { 0x0A85, 0x0A8B }, { 0x0A8D, 0x0A8D }, - { 0x0A8F, 0x0A91 }, { 0x0A93, 0x0AA8 }, { 0x0AAA, 0x0AB0 }, { 0x0AB2, 0x0AB3 }, - { 0x0AB5, 0x0AB9 }, { 0x0ABD, 0x0AC5 }, { 0x0AC7, 0x0AC9 }, { 0x0ACB, 0x0ACD }, - { 0x0AD0, 0x0AD0 }, { 0x0AE0, 0x0AE0 }, { 0x0AE6, 0x0AEF }, { 0x0B01, 0x0B03 }, - { 0x0B05, 0x0B0C }, { 0x0B0F, 0x0B10 }, { 0x0B13, 0x0B28 }, { 0x0B2A, 0x0B30 }, - { 0x0B32, 0x0B33 }, { 0x0B36, 0x0B39 }, { 0x0B3D, 0x0B43 }, { 0x0B47, 0x0B48 }, - { 0x0B4B, 0x0B4D }, { 0x0B5C, 0x0B5D }, { 0x0B5F, 0x0B61 }, { 0x0B66, 0x0B6F }, - { 0x0B82, 0x0B83 }, { 0x0B85, 0x0B8A }, { 0x0B8E, 0x0B90 }, { 0x0B92, 0x0B95 }, - { 0x0B99, 0x0B9A }, { 0x0B9C, 0x0B9C }, { 0x0B9E, 0x0B9F }, { 0x0BA3, 0x0BA4 }, - { 0x0BA8, 0x0BAA }, { 0x0BAE, 0x0BB5 }, { 0x0BB7, 0x0BB9 }, { 0x0BBE, 0x0BC2 }, - { 0x0BC6, 0x0BC8 }, { 0x0BCA, 0x0BCD }, { 0x0BE7, 0x0BEF }, { 0x0C01, 0x0C03 }, - { 0x0C05, 0x0C0C }, { 0x0C0E, 0x0C10 }, { 0x0C12, 0x0C28 }, { 0x0C2A, 0x0C33 }, - { 0x0C35, 0x0C39 }, { 0x0C3E, 0x0C44 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, - { 0x0C60, 0x0C61 }, { 0x0C66, 0x0C6F }, { 0x0C82, 0x0C83 }, { 0x0C85, 0x0C8C }, - { 0x0C8E, 0x0C90 }, { 0x0C92, 0x0CA8 }, { 0x0CAA, 0x0CB3 }, { 0x0CB5, 0x0CB9 }, - { 0x0CBE, 0x0CC4 }, { 0x0CC6, 0x0CC8 }, { 0x0CCA, 0x0CCD }, { 0x0CDE, 0x0CDE }, - { 0x0CE0, 0x0CE1 }, { 0x0CE6, 0x0CEF }, { 0x0D02, 0x0D03 }, { 0x0D05, 0x0D0C }, - { 0x0D0E, 0x0D10 }, { 0x0D12, 0x0D28 }, { 0x0D2A, 0x0D39 }, { 0x0D3E, 0x0D43 }, - { 0x0D46, 0x0D48 }, { 0x0D4A, 0x0D4D }, { 0x0D60, 0x0D61 }, { 0x0D66, 0x0D6F }, - { 0x0E01, 0x0E3A }, { 0x0E40, 0x0E5B }, { 0x0E81, 0x0E82 }, - { 0x0E84, 0x0E84 }, { 0x0E87, 0x0E88 }, { 0x0E8A, 0x0E8A }, { 0x0E8D, 0x0E8D }, - { 0x0E94, 0x0E97 }, { 0x0E99, 0x0E9F }, { 0x0EA1, 0x0EA3 }, { 0x0EA5, 0x0EA5 }, - { 0x0EA7, 0x0EA7 }, { 0x0EAA, 0x0EAB }, { 0x0EAD, 0x0EAE }, { 0x0EB0, 0x0EB9 }, - { 0x0EBB, 0x0EBD }, { 0x0EC0, 0x0EC4 }, { 0x0EC6, 0x0EC6 }, { 0x0EC8, 0x0ECD }, - { 0x0ED0, 0x0ED9 }, { 0x0EDC, 0x0EDD }, { 0x0F00, 0x0F00 }, { 0x0F18, 0x0F19 }, - { 0x0F20, 0x0F33 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, - { 0x0F3E, 0x0F47 }, { 0x0F49, 0x0F69 }, { 0x0F71, 0x0F84 }, { 0x0F86, 0x0F8B }, - { 0x0F90, 0x0F95 }, { 0x0F97, 0x0F97 }, { 0x0F99, 0x0FAD }, { 0x0FB1, 0x0FB7 }, - { 0x0FB9, 0x0FB9 }, { 0x10A0, 0x10C5 }, { 0x10D0, 0x10F6 }, { 0x1E00, 0x1E9B }, - { 0x1EA0, 0x1EF9 }, { 0x1F00, 0x1F15 }, { 0x1F18, 0x1F1D }, { 0x1F20, 0x1F45 }, - { 0x1F48, 0x1F4D }, { 0x1F50, 0x1F57 }, { 0x1F59, 0x1F59 }, { 0x1F5B, 0x1F5B }, - { 0x1F5D, 0x1F5D }, { 0x1F5F, 0x1F7D }, { 0x1F80, 0x1FB4 }, { 0x1FB6, 0x1FBC }, - { 0x1FBE, 0x1FBE }, { 0x1FC2, 0x1FC4 }, { 0x1FC6, 0x1FCC }, { 0x1FD0, 0x1FD3 }, - { 0x1FD6, 0x1FDB }, { 0x1FE0, 0x1FEC }, { 0x1FF2, 0x1FF4 }, { 0x1FF6, 0x1FFC }, - { 0x203F, 0x2040 }, { 0x207F, 0x207F }, { 0x2102, 0x2102 }, { 0x2107, 0x2107 }, - { 0x210A, 0x2113 }, { 0x2115, 0x2115 }, { 0x2118, 0x211D }, { 0x2124, 0x2124 }, - { 0x2126, 0x2126 }, { 0x2128, 0x2128 }, { 0x212A, 0x2131 }, { 0x2133, 0x2138 }, - { 0x2160, 0x2182 }, { 0x3005, 0x3007 }, { 0x3021, 0x3029 }, { 0x3041, 0x3093 }, - { 0x309B, 0x309C }, { 0x30A1, 0x30F6 }, { 0x30FB, 0x30FC }, { 0x3105, 0x312C }, - { 0x4E00, 0x9FA5 }, { 0xAC00, 0xD7A3 }, -}; - -char const *const UTF8_DECODE_OK = NULL; -extern char const UTF8_DECODE_OUTSIDE_CODE_SPACE[]; -extern char const UTF8_DECODE_TRUNCATED_SEQUENCE[]; -extern char const UTF8_DECODE_OVERLONG[]; -extern char const UTF8_DECODE_INVALID_TRAILER[]; -extern char const UTF8_DECODE_INVALID_CODE_POINT[]; - -char const *const UTF16_DECODE_OK = NULL; -extern char const UTF16_DECODE_TRUNCATED_SEQUENCE[]; -extern char const UTF16_DECODE_INVALID_SURROGATE[]; -extern char const UTF16_DECODE_UNPAIRED_SURROGATE[]; -extern char const UTF16_DECODE_INVALID_CODE_POINT[]; - -/// \return true if \a c is a valid, non-private UTF-32 code point -bool utf_isValidDchar(dchar_t c); - -bool isUniAlpha(dchar_t c); - -int utf_codeLengthChar(dchar_t c); -int utf_codeLengthWchar(dchar_t c); -int utf_codeLength(int sz, dchar_t c); - -void utf_encodeChar(utf8_t *s, dchar_t c); -void utf_encodeWchar(utf16_t *s, dchar_t c); -void utf_encode(int sz, void *s, dchar_t c); - -const char *utf_decodeChar(utf8_t const *s, size_t len, size_t *pidx, dchar_t *presult); -const char *utf_decodeWchar(utf16_t const *s, size_t len, size_t *pidx, dchar_t *presult); diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index 600521f..6cd5d20 100644 --- a/gcc/d/dmd/utils.d +++ b/gcc/d/dmd/utils.d @@ -16,7 +16,7 @@ import dmd.errors; import dmd.globals; import dmd.root.file; import dmd.root.filename; -import dmd.root.outbuffer; +import dmd.common.outbuffer; import dmd.root.string; diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 3168056..2831eef 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -1757,7 +1757,7 @@ public: /* C++ constructors return void, even though front-end semantic treats them as implicitly returning `this'. Set returnvalue to override the result of this expression. */ - if (fd->isCtorDeclaration () && fd->linkage == LINK::cpp) + if (fd->isCtorDeclaration ()) { thisexp = d_save_expr (thisexp); returnvalue = thisexp; @@ -1846,6 +1846,11 @@ public: { tree init = TARGET_EXPR_INITIAL (cleanup); TARGET_EXPR_INITIAL (cleanup) = compound_expr (init, exp); + + /* Keep the return value outside the TARGET_EXPR. */ + if (returnvalue != NULL_TREE) + cleanup = compound_expr (cleanup, TREE_OPERAND (exp, 1)); + exp = cleanup; } @@ -1856,7 +1861,7 @@ public: void visit (DelegateExp *e) { - if (e->func->semanticRun == PASSsemantic3done) + if (e->func->semanticRun == PASS::semantic3done) { /* Add the function as nested function if it belongs to this module. ie: it is a member of this module, or it is a template instance. */ diff --git a/gcc/d/gdc.texi b/gcc/d/gdc.texi index c98eb1f..d37d205 100644 --- a/gcc/d/gdc.texi +++ b/gcc/d/gdc.texi @@ -283,6 +283,13 @@ Sets @code{__traits(getTargetInfo "cppStd")} to @code{202002}. @cindex @option{-fno-invariants} Turns off code generation for class @code{invariant} contracts. +@item -fmain +@cindex @option{-fmain} +Generates a default @code{main()} function when compiling. This is useful when +unittesting a library, as it enables running the unittests in a library without +having to manually define an entry-point function. This option does nothing +when @code{main} is already defined in user code. + @item -fno-moduleinfo @cindex @option{-fmoduleinfo} @cindex @option{-fno-moduleinfo} @@ -742,6 +749,8 @@ List information on all D language transitions. List all usages of complex or imaginary types. @item field List all non-mutable fields which occupy an object instance. +@item in +List all usages of @code{in} on parameter. @item nogc List all hidden GC allocations. @item templates diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index f7f90fb..d0a5e2f 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -436,6 +436,10 @@ ftransition=field D RejectNegative List all non-mutable fields which occupy an object instance. +ftransition=in +D RejectNegative +List all usages of 'in' on parameter. + ftransition=nogc D RejectNegative List all hidden GC allocations. diff --git a/gcc/d/modules.cc b/gcc/d/modules.cc index 06eb5ae..cb5c4a7 100644 --- a/gcc/d/modules.cc +++ b/gcc/d/modules.cc @@ -145,7 +145,7 @@ get_internal_fn (tree ident, const Visibility &visibility) fd->loc = Loc (mod->srcfile.toChars (), 1, 0); fd->parent = mod; fd->visibility = visibility; - fd->semanticRun = PASSsemantic3done; + fd->semanticRun = PASS::semantic3done; return fd; } diff --git a/gcc/d/types.cc b/gcc/d/types.cc index db500ee..b39b92e 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -610,7 +610,8 @@ public: void visit (TypeNoreturn *t) { - t->ctype = void_type_node; + t->ctype = noreturn_type_node; + TYPE_NAME (t->ctype) = get_identifier (t->toChars ()); } /* Basic Data Types. */ @@ -770,11 +771,17 @@ public: fnparams = chainon (fnparams, build_tree_list (0, type)); } - size_t n_args = t->parameterList.length (); + const size_t n_args = t->parameterList.length (); for (size_t i = 0; i < n_args; i++) { tree type = parameter_type (t->parameterList[i]); + + /* Type `noreturn` is a terminator, as no other arguments can possibly + be evaluated after it. */ + if (type == noreturn_type_node) + break; + fnparams = chainon (fnparams, build_tree_list (0, type)); } @@ -797,6 +804,10 @@ public: TYPE_LANG_SPECIFIC (t->ctype) = build_lang_type (t); d_keep (t->ctype); + /* Qualify function types that have the type `noreturn` as volatile. */ + if (fntype == noreturn_type_node) + t->ctype = build_qualified_type (t->ctype, TYPE_QUAL_VOLATILE); + /* Handle any special support for calling conventions. */ switch (t->linkage) { @@ -995,8 +1006,8 @@ public: the context or laying out fields as those types may make recursive references to this type. */ unsigned structsize = t->sym->structsize; - unsigned alignsize = (t->sym->alignment != STRUCTALIGN_DEFAULT) - ? t->sym->alignment : t->sym->alignsize; + unsigned alignsize = t->sym->alignment.isDefault () + ? t->sym->alignsize : t->sym->alignment.get (); TYPE_SIZE (t->ctype) = bitsize_int (structsize * BITS_PER_UNIT); TYPE_SIZE_UNIT (t->ctype) = size_int (structsize); diff --git a/gcc/testsuite/gdc.test/compilable/b19294.d b/gcc/testsuite/gdc.test/compilable/b19294.d new file mode 100644 index 0000000..063a9df --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/b19294.d @@ -0,0 +1,69 @@ +alias MT = MyStruct!int; + +struct MyStruct(T) +{ + T x; + + this(T y) + { + x = y; + } + + MyStruct!T opBinary(string op)(MyStruct!T y) const + { + alias C = typeof(return); + auto w = C(this.x); + return w.opOpAssign!(op)(y); + } + + MyStruct!T opBinaryRight(string op)(MyStruct!T y) const + { + return opBinary!(op)(y); + } + + ref MyStruct opOpAssign(string op, T)(const MyStruct!T z) + { + mixin ("x "~op~"= z.x;"); + return this; + } + + MyStruct!T opBinary(string op)(T y) const + { + alias C = typeof(return); + auto w = C(this.x); + return w.opOpAssign!(op)(y); + } + + MyStruct!T opBinaryRight(string op)(T y) const + { + return opBinary!(op)(y); + } + + ref MyStruct opOpAssign(string op, T)(const T z) + { + mixin ("x "~op~"= z;"); + return this; + } +} + +void test() +{ + MT s = MyStruct!int(1); + MT[] arr = [s, 2 * s, 3 * s, 4 * s, 5 * s, 6 * s]; + MT[] result = new MT[arr.length]; + + result[] = arr[] + s; + result[] = s + arr[]; + + result[] = arr[] - s; + result[] = s - arr[]; + + result[] = arr[] * s; + result[] = s * arr[]; + + result[] = arr[] / s; + result[] = s / arr[]; + + result[] = arr[] ^^ s; + result[] = s ^^ arr[]; +} diff --git a/gcc/testsuite/gdc.test/compilable/cdcmp.d b/gcc/testsuite/gdc.test/compilable/cdcmp.d index 614bdc8..4248818 100644 --- a/gcc/testsuite/gdc.test/compilable/cdcmp.d +++ b/gcc/testsuite/gdc.test/compilable/cdcmp.d @@ -2,7 +2,7 @@ // REQUIRED_ARGS: -O // POST_SCRIPT: compilable/extra-files/objdump-postscript.sh // only testing on SYSV-ABI, but backend code is identical across platforms -// DISABLED: win32 win64 osx linux32 freebsd32 freebsd64 +// DISABLED: win32 win64 osx linux32 freebsd32 freebsd64 openbsd32 openbsd64 bool test_ltz(ubyte x) { return x < 0; } bool test_lez(ubyte x) { return x <= 0; } diff --git a/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_22285.d b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_22285.d new file mode 100644 index 0000000..5e8836d --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc_markdown_tables_22285.d @@ -0,0 +1,15 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// TEST_OUTPUT_FILE: extra-files/ddoc_markdown_tables_22285.html +// OUTPUT_FILES: ${RESULTS_DIR}/compilable/ddoc_markdown_tables_22285.html + +module test.compilable.ddoc_markdown_tables_22285; + +/** +| A | B | C | +|---|---|---| +| a | 0 | | +| b | 1 1 1 | | +| c | 2 | | +*/ +enum _ = 0; diff --git a/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d b/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d index 54bbc79..47c0172 100644 --- a/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d +++ b/gcc/testsuite/gdc.test/compilable/dtoh_ignored.d @@ -144,4 +144,7 @@ __gshared void function(ifloat) onVariableFunctionParam; __gshared ifloat delegate() onVariableDelegate; -noreturn myExit() {} +noreturn myExit() +{ + assert(false); +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/cstuff3.c b/gcc/testsuite/gdc.test/compilable/imports/cstuff3.c deleted file mode 100644 index f6aaf3b..0000000 --- a/gcc/testsuite/gdc.test/compilable/imports/cstuff3.c +++ /dev/null @@ -1,6 +0,0 @@ -// check bugs in importing C files - -int squared(int a) -{ - return a * a; -} diff --git a/gcc/testsuite/gdc.test/compilable/mixintype2.d b/gcc/testsuite/gdc.test/compilable/mixintype2.d index 43803df..d160bd4 100644 --- a/gcc/testsuite/gdc.test/compilable/mixintype2.d +++ b/gcc/testsuite/gdc.test/compilable/mixintype2.d @@ -66,3 +66,52 @@ static assert(is(T8 == const(ubyte*))); alias T8 = mixin(q{immutable(__traits(getMember, S, "T"))})*; static assert(is(T8 == immutable(float*)*)); */ + +/**************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=22356 + +mixin("void") func22356(int) { } +static assert(is(typeof(&func22356) == void function(int))); + +static mixin("void") func22356_s(char) { } +static assert(is(typeof(&func22356_s) == void function(char))); + +mixin("int")[2] func22356_2(int) { return [1, 2]; } +static assert(is(typeof(&func22356_2) == int[2] function(int))); + +mixin("int") func22356tp(S, T)(S, T) { return 1; } +static assert(is(typeof(&func22356tp!(char, float)) == int function(char, float) pure nothrow @nogc @safe)); + +mixin("int") x22356; +static assert(is(typeof(x22356) == int)); + +mixin("int")** xpp22356; +static assert(is(typeof(xpp22356) == int**)); + +mixin("int") y22356, z22356; +static assert(is(typeof(y22356) == int) && is(typeof(z22356) == int)); + +// Already working but for completeness +void test_statements_22356() +{ + mixin("void") func22356(int) { } + static assert(is(typeof(&func22356) == void delegate(int) pure nothrow @nogc @safe)); + + static mixin("void") func22356_s(char) { } + static assert(is(typeof(&func22356_s) == void function(char) pure nothrow @nogc @safe)); + + mixin("int")[2] func22356_2(int) { return [1, 2]; } + static assert(is(typeof(&func22356_2) == int[2] delegate(int) pure nothrow @nogc @safe)); + + mixin("int") func22356tp(S, T)(S, T) { return 1; } + static assert(is(typeof(&func22356tp!(char, float)) == int delegate(char, float) pure nothrow @nogc @safe)); + + mixin("int") x22356; + static assert(is(typeof(x22356) == int)); + + mixin("int")** xpp22356; + static assert(is(typeof(xpp22356) == int**)); + + mixin("int") y22356, z22356; + static assert(is(typeof(y22356) == int) && is(typeof(z22356) == int)); +} diff --git a/gcc/testsuite/gdc.test/compilable/noreturn1.d b/gcc/testsuite/gdc.test/compilable/noreturn1.d index 7517bb2..b041e07 100644 --- a/gcc/testsuite/gdc.test/compilable/noreturn1.d +++ b/gcc/testsuite/gdc.test/compilable/noreturn1.d @@ -49,8 +49,17 @@ static assert(noreturn.alignof == 0); static assert((noreturn*).sizeof == (int*).sizeof); static assert((noreturn[]).sizeof == (int[]).sizeof); +static assert(is(typeof(noreturn.init) == noreturn)); +static assert(is(typeof((const noreturn).init) == const noreturn)); +static assert(is(typeof((immutable noreturn).init) == immutable noreturn)); +static assert(is(typeof((shared noreturn).init) == shared noreturn)); + version (DigitalMars) - noreturn exits(int* p) { *p = 3; } + noreturn exits(int* p) + { + *p = 3; + assert(false); // *p could be valid + } noreturn exit(); @@ -58,9 +67,47 @@ noreturn pureexits() @nogc nothrow pure @safe { assert(0); } noreturn callpureexits() { pureexits(); } +noreturn returnExits() +{ + return pureexits(); +} + +void alsoExits() +{ + return assert(0); +} + +int thisAlsoExits() +{ + return assert(0); +} + +void cast_() +{ + noreturn n; + int i = n; +} + int test1(int i) { if (exit()) return i + 1; return i - 1; } + +noreturn tlsNoreturn; +__gshared noreturn globalNoreturn; + +template CreateTLS(A) +{ + A a; +} + +void* useTls() +{ + alias Tnr = CreateTLS!noreturn; + void* a1 = &Tnr.a; + void* a2 = &tlsNoreturn; + void* a3 = &globalNoreturn; + return a1 < a2 ? a2 : a3; +} diff --git a/gcc/testsuite/gdc.test/compilable/previewall.d b/gcc/testsuite/gdc.test/compilable/previewall.d deleted file mode 100644 index e5ed7a8..0000000 --- a/gcc/testsuite/gdc.test/compilable/previewall.d +++ /dev/null @@ -1,10 +0,0 @@ -// ARG_SETS: -preview=all -// ARG_SETS: -transition=all -// ARG_SETS: -revert=all -import core.stdc.stdio; - -void main (string[] args) -{ - if (args.length == 42) - printf("Hello World\n"); -} diff --git a/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d b/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d index 1d6fd4f..9a4db35 100644 --- a/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d +++ b/gcc/testsuite/gdc.test/compilable/reinterpretctfe.d @@ -1,5 +1,11 @@ // https://issues.dlang.org/show_bug.cgi?id=21997 +struct Strukt +{ + int i; + string s; +} + int nonPureFunc(int i) { return 2 * i; @@ -28,6 +34,14 @@ int mainCtfe() auto baseDel = cast(int delegate(int)) pureDel; assert(baseDel(4) == 20); */ + + { + shared Strukt shStr; + Strukt str = *cast(Strukt*) &shStr; + + shared(Strukt)* ptr = cast(shared(Strukt)*) &str; + } + return 0; } diff --git a/gcc/testsuite/gdc.test/compilable/sroa.d b/gcc/testsuite/gdc.test/compilable/sroa.d new file mode 100644 index 0000000..8ea515c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/sroa.d @@ -0,0 +1,55 @@ +/* REQUIRED_ARGS: -O -release -inline +This compares two different ways to do a for loop. The range +version should SROA the VecRange struct into two register variables. +*/ + +extern (C): + +nothrow: +@nogc: +@safe: + +alias vec_base_t = size_t; // base type of vector +alias vec_t = vec_base_t*; + +@trusted +pure +size_t vec_index(size_t b, const vec_t vec); + +@trusted +pure ref inout(vec_base_t) vec_numbits(inout vec_t v) { return v[-1]; } +@trusted +pure ref inout(vec_base_t) vec_dim(inout vec_t v) { return v[-2]; } + +struct VecRange +{ + size_t i; + const vec_t v; + + @nogc @safe nothrow pure: + this(const vec_t v) { this.v = v; i = vec_index(0, v); } + bool empty() const { return i == vec_numbits(v); } + size_t front() const { return i; } + void popFront() { i = vec_index(i + 1, v); } +} + +@safe +pure +uint vec_numBitsSet(const vec_t vec) +{ + uint n = 0; + size_t length = vec_numbits(vec); + for (size_t i = 0; (i = vec_index(i, vec)) < length; ++i) + ++n; + return n; +} + +@safe +pure +uint vec_numBitsSet2(const vec_t vec) +{ + uint n = 0; + foreach (j; VecRange(vec)) + ++n; + return n; +} diff --git a/gcc/testsuite/gdc.test/compilable/stc_traits.d b/gcc/testsuite/gdc.test/compilable/stc_traits.d new file mode 100644 index 0000000..c5c4e5f --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/stc_traits.d @@ -0,0 +1,172 @@ +// REQUIRED_ARGS: -preview=dip1000 -preview=in +/* +TEST_OUTPUT: +--- +100 tuple() +101 tuple("return", "ref") +102 tuple("ref") +103 tuple() +104 tuple("ref") +105 tuple() +106 tuple() +107 tuple("ref") +108 tuple("ref") +109 tuple("ref") +110 tuple("ref") +111 tuple() +112 tuple("ref") +113 tuple("ref") +114 tuple("ref") +115 tuple("ref") +116 tuple() +117 tuple("ref") +118 tuple("ref") +119 tuple() +120 tuple("ref") +121 tuple() +122 tuple("ref") +123 tuple("in") +124 tuple("in") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("return", "ref") +m-mixin tuple("return", "ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple() +m-mixin tuple() +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("ref") +m-mixin tuple("ref") +m tuple("in") +m-mixin tuple("in") +--- +*/ + +void func(int i) {} +void func(return ref bool i) {} +void func(ref float a, int b) {} +void get(T : int)(ref T t) {} +void get()(float t) {} +void get(T)(ref T[] t) {} +void funcautoi()(auto ref int i) {} +void funcauto(T)(auto ref T a) {} +void funcin(in int i) {} + +struct Foo { + void foo(int i) {} + void foo(ref bool i) {} + static void sfoo(ref int i) {} +} + +struct FooT(T) { + void foo(ref T i) {} + static void sfoo(ref T i) {} +} + +class Bar { + void bar(int i) {} + void bar(ref bool i) {} + static void sbar(ref int i) {} +} + +class BarT(T) { + void bar(ref T i) {} + static void sbar(ref T i) {} +} + +int i; + +template match(handlers...) +{ + static foreach(h; handlers) + { + // should give the same result + pragma(msg, "m ", __traits(getParameterStorageClasses, h(i), 0)); + pragma(msg, "m-mixin ", __traits(getParameterStorageClasses, mixin("h(i)"), 0)); + } + + enum match = (){}; +} + +void funcT(T)(ref T t) {} + +void main() { + int i; + bool b; + float f; + int[] ia; + Foo foo; + FooT!int foot; + Bar bar = new Bar; + BarT!int bart = new BarT!int; + + ref int _foo(return ref const int* p, scope int* a, out int b, lazy int c); + + // From SPEC_RUNNABLE_EXAMPLE_COMPILE: + int* p, a; + int _b, c; + + static assert(__traits(getParameterStorageClasses, _foo(p, a, _b, c), 1)[0] == "scope"); + static assert(__traits(getParameterStorageClasses, _foo(p, a, _b, c), 2)[0] == "out"); + static assert(__traits(getParameterStorageClasses, _foo(p, a, _b, c), 3)[0] == "lazy"); + +#line 100 + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, func(0), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, func(b), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, func(f, i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, func(f, i), 1)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, get(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, get(0.0), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, get(f), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, get(ia), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, mixin("get(i)"), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, Foo.sfoo(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, FooT!int.sfoo(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, foo.foo(0), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, foo.foo(b), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, foot.foo(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, Bar.sbar(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, BarT!int.sbar(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, bar.bar(0), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, bar.bar(b), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, bart.bar(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, funcautoi(10), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, funcautoi(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, funcauto(10), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, funcauto(i), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, funcin(1), 0)); + pragma(msg, __LINE__, " ", __traits(getParameterStorageClasses, funcin(i), 0)); + + cast(void) match!( + function(ref int i) => true, + delegate(ref int i) => true, + (ref int i) => true, + (return ref int i) => &i, + get, + funcT, + (int i) => true, + FooT!int.sfoo, + Foo.sfoo, + BarT!int.sbar, + Bar.sbar, + funcautoi, + funcauto, + funcin, + ); +} diff --git a/gcc/testsuite/gdc.test/compilable/test15711.d b/gcc/testsuite/gdc.test/compilable/test15711.d new file mode 100644 index 0000000..ba7a93d --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test15711.d @@ -0,0 +1,31 @@ +// https://issues.dlang.org/show_bug.cgi?id=15711 + +struct Quu { + string val; +} + +string[] result = foo!(0, [Quu(['z']), Quu("")]); + +template foo(size_t i, Quu[] data, string[] results = []) { + static if (i < data.length) { + enum def = data[i]; + enum foo = foo!(i+1, data, results ~ def.val); + } + else { + enum foo = results; + } +} + +// Run-time version already works + +string[] result_rt = foo_rt(0, [Quu(['z']), Quu("")]); + +string[] foo_rt(size_t i, Quu[] data, string[] results = []) { + if (i < data.length) { + auto def = data[i]; + return foo_rt(i+1, data, results ~ def.val); + } + else { + return results; + } +} diff --git a/gcc/testsuite/gdc.test/compilable/test16492.d b/gcc/testsuite/gdc.test/compilable/test16492.d deleted file mode 100644 index 833be1d..0000000 --- a/gcc/testsuite/gdc.test/compilable/test16492.d +++ /dev/null @@ -1,87 +0,0 @@ -// ARG_SETS: -debug; -o-; -debug -preview=dip1000 -// https://issues.dlang.org/show_bug.cgi?id=16492 - -void mayCallGC(); - -void test() @nogc pure -{ - debug new int(1); - debug - { - mayCallGC(); - auto b = [1, 2, 3]; - b ~= 4; - } -} - -void debugSafe() @safe -{ - debug unsafeSystem(); - debug unsafeTemplated(); -} - -void unsafeSystem() @system {} -void unsafeTemplated()() { - int[] arr; - auto b = arr.ptr; -} - -void debugSafe2() @safe -{ - char[] arr1, arr2; - debug unsafeDIP1000Lifetime(arr1, arr2); - - char* ptr; - char[] arr; - debug ptr = arr.ptr; -} - -void unsafeDIP1000Lifetime()(ref char[] p, scope char[] s) -{ - p = s; -} - - -void test2() nothrow -{ - debug throw new Exception(""); -} - -void test3() nothrow -{ - debug { - foreach (_; 0 .. 10) { - if (1) { - throw new Exception(""); - } - } - } -} - -void test4() nothrow -{ - debug throwException(); -} - -void test5() nothrow -{ - debug willThrowException(); -} - -void willThrowException()() -{ - throwException(); -} - -void throwException() -{ - throw new Exception(""); -} - -void test6() nothrow -{ - debug - { - () {throw new Exception("");}(); - } -} diff --git a/gcc/testsuite/gdc.test/compilable/test19482.d b/gcc/testsuite/gdc.test/compilable/test19482.d new file mode 100644 index 0000000..09485a3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test19482.d @@ -0,0 +1,68 @@ +// https://issues.dlang.org/show_bug.cgi?id=19482 + +alias AliasSeq(T...) = T; + +extern (C++, "cppns") +@("asd", 123) +private +deprecated +immutable +static foreach (i; 0 .. 1) +{ + static assert(is(typeof(i) == int)); + static assert(__traits(getLinkage, i) == "D"); + static assert(__traits(isDeprecated, i) == false); + static assert(__traits(getAttributes, i).length == 0); + static assert(__traits(getCppNamespaces, i).length == 0); + static assert(__traits(getVisibility, i) == "public"); + + extern int x; + static assert(is(typeof(x) == immutable int)); + static assert(__traits(getLinkage, x) == "C++"); + static assert(__traits(isDeprecated, x) == true); + static assert(__traits(getAttributes, x) == AliasSeq!("asd", 123)); + static assert(__traits(getCppNamespaces, x) == AliasSeq!("cppns")); + static assert(__traits(getVisibility, x) == "private"); +} + +struct S +{ + @disable static foreach (j; 0 .. 1) + { + int y; + static assert(__traits(isDisabled, j) == false); + static assert(__traits(isDisabled, S.y) == true); + } +} + +const +static foreach (i, v; ['a']) +{ + static assert(is(typeof(i) == size_t)); + static assert(is(typeof(v) == char)); +} + +const +static foreach (i, s, f; Range()) +{ + static assert(is(typeof(i) == int)); + static assert(is(typeof(s) == string)); + static assert(is(typeof(f) == float)); +} + +struct Range +{ + int i; + auto front() + { + return Tup!(int, string, float)(123, "asd", 3.14f); + } + bool empty() { return i > 0; } + void popFront() { ++i; } +} + +struct Tup(T...) +{ + T fields; + alias fields this; +} diff --git a/gcc/testsuite/gdc.test/compilable/test21438.d b/gcc/testsuite/gdc.test/compilable/test21438.d new file mode 100644 index 0000000..02e2d8d --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21438.d @@ -0,0 +1,15 @@ +// https://issues.dlang.org/show_bug.cgi?id=21438 + +int genGBPLookup() { + static struct Table { + int[1] entries; + } + + auto table = new Table; + auto x = table.entries[0]; + + static assert(is(typeof(x) == int)); + return 0; +} + +enum x = genGBPLookup; diff --git a/gcc/testsuite/gdc.test/compilable/test21794.d b/gcc/testsuite/gdc.test/compilable/test21794.d new file mode 100644 index 0000000..68e504b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21794.d @@ -0,0 +1,52 @@ +// https://issues.dlang.org/show_bug.cgi?id=21794 +/* +TEST_OUTPUT: +--- +0 +0u +0L +0LU +0.0F +0.0 +0.0L +--- +*/ + +bool fun(void* p) { + const x = cast(ulong)p; + return 1; +} + +static assert(fun(null)); + +T fun2(T)(void* p) { + const x = cast(T)p; + return x; +} + +// These were an error before, they were returning a NullExp instead of IntegerExp/RealExp + +static assert(fun2!int(null) == 0); +static assert(fun2!uint(null) == 0); +static assert(fun2!long(null) == 0); +static assert(fun2!ulong(null) == 0); +static assert(fun2!float(null) == 0); +static assert(fun2!double(null) == 0); +static assert(fun2!real(null) == 0); + +// These were printing 'null' instead of the corresponding number + +const i = cast(int)null; +const ui = cast(uint)null; +const l = cast(long)null; +const ul = cast(ulong)null; +const f = cast(float)null; +const d = cast(double)null; +const r = cast(real)null; +pragma(msg, i); +pragma(msg, ui); +pragma(msg, l); +pragma(msg, ul); +pragma(msg, f); +pragma(msg, d); +pragma(msg, r); diff --git a/gcc/testsuite/gdc.test/compilable/test21850.d b/gcc/testsuite/gdc.test/compilable/test21850.d new file mode 100644 index 0000000..e7fe2d4 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21850.d @@ -0,0 +1,35 @@ +// https://issues.dlang.org/show_bug.cgi?id=21850 + +struct Strukt2 { + this(int* _block) { } +} + +struct Strukt { + int* block; + Strukt2 foo() { return Strukt2(null); } + alias foo this; +} + +bool wrapper(T)(ref T a, ref T b) +{ + return doesPointTo(a, b); +} + +void johan() pure { + Strukt a; + Strukt b; + assert(wrapper(a, b)); // error wrapper is not pure + assert(doesPointTo(a, b)); // fine +} + +bool doesPointTo(S, T)(S , T) { + return false; +} + +bool doesPointTo(S)(shared S) { + return false; +} + +bool mayPointTo(){ + return false; +} diff --git a/gcc/testsuite/gdc.test/compilable/test22214.d b/gcc/testsuite/gdc.test/compilable/test22214.d new file mode 100644 index 0000000..d218125 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22214.d @@ -0,0 +1,16 @@ +// https://issues.dlang.org/show_bug.cgi?id=22214 + +struct S +{ + struct T + { + } +} + +void main() { + const S s; + static if (__traits(compiles, { auto t = s.T; })) + { + auto t = s.T; + } +} diff --git a/gcc/testsuite/gdc.test/compilable/test22224.d b/gcc/testsuite/gdc.test/compilable/test22224.d new file mode 100644 index 0000000..d16b2f40 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22224.d @@ -0,0 +1,4 @@ +// REQUIRED_ARGS: -profile -c + +import core.stdc.stdarg; +void error(...) { } diff --git a/gcc/testsuite/gdc.test/compilable/test22228.d b/gcc/testsuite/gdc.test/compilable/test22228.d new file mode 100644 index 0000000..ef31b4b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22228.d @@ -0,0 +1,11 @@ +// https://issues.dlang.org/show_bug.cgi?id=22228 +// Note: fixed by reverting pull #11545 + +auto f() +{ immutable int i; + auto p = (() => &i)(); + + return 0; +} + +enum ctfeInvocation = f; diff --git a/gcc/testsuite/gdc.test/compilable/test22292.d b/gcc/testsuite/gdc.test/compilable/test22292.d new file mode 100644 index 0000000..945dffb --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22292.d @@ -0,0 +1,155 @@ +// https://issues.dlang.org/show_bug.cgi?id=22292 + +// Original case + +class C1 +{ + C1 c1; + this () pure + { + c1 = this; + } +} +immutable x = cast(immutable)r; + +auto r() +{ + C1 c1 = new C1; + return c1; +} + +// Reference stored in another class + +template Test2() +{ + class C1 + { + C2 c2; + this () pure + { + C1 a = this; + c2 = new C2(a); + } + } + class C2 + { + C1 c1; + this (C1 c) pure + { + c1 = c; + } + } + immutable x = cast(immutable)r; + + auto r() + { + C1 c1 = new C1(); + return c1; + } +} + +alias test2 = Test2!(); + +// Ditto but using a struct in the middle + +template Test3() +{ + class C0 + { + S1 s1; + + this() + { + s1 = S1(this); + } + } + struct S1 + { + C1 c1; + this (C0 c) + { + c1 = new C1(c); + } + } + class C1 + { + C0 c0; + this(C0 c) + { + c0 = c; + } + } + immutable x = cast(immutable)r; + + auto r() + { + C0 c0 = new C0(); + return c0; + } +} + +alias test3 = Test3!(); + +// From https://issues.dlang.org/show_bug.cgi?id=22114 + +template Test4() +{ + public class Test1(T) + { + private Test2!T val; + + this() + { + val = new Test2!T(this); + } + + private class Test2(T) + { + private Test1!(T) m_source; + + this(Test1!T source) + { + m_source = source; + } + } + } + + public class Demo + { + auto val = new Test1!int(); + } +} + +alias test4 = Test4!(); + +// ditto + +template Test5() +{ + public @nogc class TestA(T) + { + private TestB!T valA; + private TestB!T valB; + this() + { + valB = valA = new TestB!T(this); + } + + private @nogc class TestB(T) + { + private TestA!(T) m_source; + + this(TestA!T source) + { + m_source = source; + } + } + } + + public class Demo + { + auto val = new TestA!int(); + } +} + +alias test5 = Test5!(); diff --git a/gcc/testsuite/gdc.test/compilable/test22388.d b/gcc/testsuite/gdc.test/compilable/test22388.d new file mode 100644 index 0000000..cf8c3fc --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22388.d @@ -0,0 +1,22 @@ +// https://issues.dlang.org/show_bug.cgi?id=22388 + +void setTimer(void delegate()) @system; +void setTimer(void delegate() @safe) @safe; + +void setTimer2(void delegate() @safe) @safe; +void setTimer2(void delegate()) @system; + +void main() @safe +{ + setTimer(() => assert(false)); + + alias lambda = () => assert(false); + setTimer(lambda); + + // Reversed order + + setTimer2(() => assert(false)); + + alias lambda2 = () => assert(false); + setTimer2(lambda2); +} diff --git a/gcc/testsuite/gdc.test/compilable/test22410.d b/gcc/testsuite/gdc.test/compilable/test22410.d new file mode 100644 index 0000000..7e631b7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22410.d @@ -0,0 +1,59 @@ +// https://issues.dlang.org/show_bug.cgi?id=22410 + +alias A(T...) = T; + +void fun0(const A!(int, string) x = A!(1, "asdf")) +{ + static assert(is(typeof(x) == A!(const int, const string))); +} + +void fun1(const A!(immutable int, string) x = A!(1, "asdf")) +{ + static assert(is(typeof(x) == A!(immutable int, const string))); +} + +void fun2(shared A!(int, string) x = A!(1, "asdf")) +{ + static assert(is(typeof(x) == A!(shared int, shared string))); +} + +void fun3(shared const A!(int, string) x = A!(1, "asdf")) +{ + static assert(is(typeof(x) == A!(shared const int, shared const string))); +} + +void fun4(inout A!(int, const string) x = A!(1, "asdf")) +{ + static assert(is(typeof(x) == A!(inout int, inout const string))); +} + +void fun5(ref const A!(int, string) x = A!(1, "asdf")) +{ + static assert(is(typeof(x) == A!(const int, const string))); + static assert(__traits(isRef, x[0]) && __traits(isRef, x[1])); +} + +// Implicitly conversion is also fixed, for example: +// from (ulong, double) to (int, float) + +// Integral narrowing here, ulong(uint.max + 1UL) would fail. +void fun10(A!(uint, float) x = A!(ulong(uint.max), 3.14)) +{ + static assert(is(typeof(x) == A!(uint, float))); +} + +void fun11(A!(int, double) x = A!(byte(1), 2.5f)) +{ + static assert(is(typeof(x) == A!(int, double))); +} + +void fun12(A!(byte, float) x = A!(1, 'a')) +{ + static assert(is(typeof(x) == A!(byte, float))); +} + +A!(const int, shared char) tup = A!(1, 'a'); +void fun13(A!(byte, float) x = tup) +{ + static assert(is(typeof(x) == A!(byte, float))); +} diff --git a/gcc/testsuite/gdc.test/compilable/test22420.d b/gcc/testsuite/gdc.test/compilable/test22420.d new file mode 100644 index 0000000..c18d0a9 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22420.d @@ -0,0 +1,88 @@ +// https://issues.dlang.org/show_bug.cgi?id=22420 + +struct File +{ + ~this() + { + } + File impl() + { + return File.init; + } + alias impl this; +} +struct Variable +{ + this(File)(File) { } + this(File)(File[]) { } +} +Variable wrapFunctionReturn(alias handler)(Variable params) +{ + return Variable(handler(params)); +} +void registerFile() +{ + wrapFunctionReturn!((Variable) { + return File.init; + })(Variable.init); +} + +// Reduction from an 'automem' test + +struct Issue156 {} + +void test2() +{ + RefCounted!Issue156 s; + auto r1 = repeat(s); + zip(r1); +} + +struct RefCounted(RefCountedType) +{ + ~this() {} + alias _impl this; + + struct Impl {} + alias ImplType = Impl; + + private ImplType* _impl; + +} +template Tuple(Specs) +{ + struct Tuple + { + this(U)(U) {} + this()(int) {} + } +} + +template ElementType(R) +{ + static if (is(typeof(R.init) T)) + alias ElementType = T; +} + +struct Repeat(T) +{ + inout(T) front() inout {assert(0);} +} + +Repeat!T repeat(T)(T ) {assert(0);} + +auto zip(Ranges)(Ranges ) +{ + return ZipShortest!Ranges(); +} + +struct ZipShortest(Ranges...) +{ + Ranges ranges; + alias ElementType = Tuple!(.ElementType!(Ranges[0])); + + ElementType front() + { + return typeof(return)(ranges[0].front); + } +} diff --git a/gcc/testsuite/gdc.test/compilable/test22421.d b/gcc/testsuite/gdc.test/compilable/test22421.d new file mode 100644 index 0000000..902646d --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22421.d @@ -0,0 +1,19 @@ +// https://issues.dlang.org/show_bug.cgi?id=22421 + +alias AliasSeq(T...) = T; + +template staticMap(alias fun, args...) +{ + alias staticMap = AliasSeq!(); + static foreach(arg; args) + staticMap = AliasSeq!(staticMap, fun!arg); +} + +template id(alias what) +{ + enum id = __traits(identifier, what); +} + +enum A { a } + +static assert(staticMap!(id, A.a) == AliasSeq!("a")); diff --git a/gcc/testsuite/gdc.test/compilable/test318.d b/gcc/testsuite/gdc.test/compilable/test318.d new file mode 100644 index 0000000..fd3cabe --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test318.d @@ -0,0 +1,19 @@ +// LINK: +// PERMUTE_ARGS: -version=C_Main + +version (C_Main) +{ + // Fine, infers int + extern(C) auto main(int argc, const char** argv) + { + return argc; + } +} +else +{ + // Fine, infers void + auto main() + { + + } +} diff --git a/gcc/testsuite/gdc.test/compilable/test4090.d b/gcc/testsuite/gdc.test/compilable/test4090.d index 8f8f7c9..0e785cf 100644 --- a/gcc/testsuite/gdc.test/compilable/test4090.d +++ b/gcc/testsuite/gdc.test/compilable/test4090.d @@ -12,9 +12,6 @@ void test4090a() // inference + qualifier + ref foreach ( ref x; arr) static assert(is(typeof(x) == int)); foreach ( const ref x; arr) static assert(is(typeof(x) == const int)); - static assert(!__traits(compiles, { - foreach (immutable ref x; arr) {} - })); // with exact type + qualifier foreach ( int x; arr) static assert(is(typeof(x) == int)); @@ -24,25 +21,11 @@ void test4090a() // with exact type + qualifier + ref foreach ( ref int x; arr) static assert(is(typeof(x) == int)); foreach ( const ref int x; arr) static assert(is(typeof(x) == const int)); - static assert(!__traits(compiles, { - foreach (immutable ref int x; arr) {} - })); // convertible type + qualifier foreach ( double x; arr) static assert(is(typeof(x) == double)); foreach ( const double x; arr) static assert(is(typeof(x) == const double)); foreach (immutable double x; arr) static assert(is(typeof(x) == immutable double)); - - // convertible type + qualifier + ref - static assert(!__traits(compiles, { - foreach ( ref double x; arr) {} - })); - static assert(!__traits(compiles, { - foreach ( const ref double x; arr) {} - })); - static assert(!__traits(compiles, { - foreach (immutable ref double x; arr) {} - })); } // for the immutable elements { diff --git a/gcc/testsuite/gdc.test/compilable/test9766.d b/gcc/testsuite/gdc.test/compilable/test9766.d index 3cfc22f..aaceb7d 100644 --- a/gcc/testsuite/gdc.test/compilable/test9766.d +++ b/gcc/testsuite/gdc.test/compilable/test9766.d @@ -69,9 +69,9 @@ static assert(U9766.var4.offsetof == 40); struct TestMaxAlign { -align(1u << 31): +align(1u << 15): ubyte a; ubyte b; } -static assert(TestMaxAlign.b.offsetof == 2147483648u); +static assert(TestMaxAlign.b.offsetof == (1 << 15)); diff --git a/gcc/testsuite/gdc.test/compilable/testcstuff3.d b/gcc/testsuite/gdc.test/compilable/testcstuff3.d deleted file mode 100644 index 89228a9..0000000 --- a/gcc/testsuite/gdc.test/compilable/testcstuff3.d +++ /dev/null @@ -1,4 +0,0 @@ -// EXTRA_FILES: imports/cstuff3.c -import imports.cstuff3; - -static assert(squared(4) == 16); diff --git a/gcc/testsuite/gdc.test/compilable/transition_in.d b/gcc/testsuite/gdc.test/compilable/transition_in.d new file mode 100644 index 0000000..cc492a7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/transition_in.d @@ -0,0 +1,26 @@ +// REQUIRED_ARGS: -transition=in +/* +TRANSFORM_OUTPUT: remove_lines(druntime) +TEST_OUTPUT: +--- +compilable/transition_in.d(3): Usage of 'in' on parameter +compilable/transition_in.d(3): Usage of 'in' on parameter +compilable/transition_in.d(8): Usage of 'in' on parameter +compilable/transition_in.d(13): Usage of 'in' on parameter +--- +*/ +#line 1 +struct Foobar +{ + void bar (in int a, in Object c); +} + +version (none) +{ + void barfoo (in string arg); +} + +void main () +{ + void nested (in char c) {} +} diff --git a/gcc/testsuite/gdc.test/compilable/zerosize.d b/gcc/testsuite/gdc.test/compilable/zerosize.d index 3c0062e..6e26deb 100644 --- a/gcc/testsuite/gdc.test/compilable/zerosize.d +++ b/gcc/testsuite/gdc.test/compilable/zerosize.d @@ -1,12 +1,17 @@ extern (C) struct S { } -static assert(S.sizeof == 0); -static assert(S.alignof == 1); +version (CRuntime_Microsoft) + static assert(S.sizeof == 4); +else + static assert(S.sizeof == 0); + +version (CRuntime_DigitalMars) + static assert(S.alignof == 0); +else + static assert(S.alignof == 1); extern (C++) struct T { } static assert(T.sizeof == 1); static assert(T.alignof == 1); - - diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10327.d b/gcc/testsuite/gdc.test/fail_compilation/diag10327.d index 38f9ccb..1366882 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag10327.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10327.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10327.d(11): Error: module `test10327` is in file 'imports/test10327.d' which cannot be read +fail_compilation/diag10327.d(12): Error: unable to read module `test10327` +fail_compilation/diag10327.d(12): Expected 'imports/test10327.d' or 'imports/test10327/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ import path[2] = $p:phobos$ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag20059.d b/gcc/testsuite/gdc.test/fail_compilation/diag20059.d index a7a5914..2c8063a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag20059.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag20059.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag20059.d(15): Error: Expected return type of `string`, not `string[]`: +fail_compilation/diag20059.d(15): Error: expected return type of `string`, not `string[]`: fail_compilation/diag20059.d(13): Return type of `string` inferred here. --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20618.d b/gcc/testsuite/gdc.test/fail_compilation/fail20618.d new file mode 100644 index 0000000..ac6b33a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail20618.d @@ -0,0 +1,16 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail20618.d(13): Error: in slice `a[1 .. 12]`, upper bound is greater than array length `10` +fail_compilation/fail20618.d(14): Error: in slice `a[4 .. 3]`, lower bound is greater than upper bound +fail_compilation/fail20618.d(15): Error: in slice `a[0 .. 11]`, upper bound is greater than array length `10` +--- +*/ + +void main() +{ + int[10] a; + auto b = a[1..12]; + auto c = a[4..3]; + auto d = a[0..$ + 1]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d b/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d index 74f40c2..c2bbe4d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail21091a.d @@ -3,7 +3,8 @@ /* TEST_OUTPUT: ---- -fail_compilation/fail21091a.d(15): Error: module `Ternary` is in file 'Ternary.d' which cannot be read +fail_compilation/fail21091a.d(16): Error: unable to read module `Ternary` +fail_compilation/fail21091a.d(16): Expected 'Ternary.d' or 'Ternary/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ import path[2] = $p:phobos$ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d b/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d index d9467aa..3d7d600 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail21091b.d @@ -3,7 +3,8 @@ /* TEST_OUTPUT: ---- -fail_compilation/fail21091b.d(15): Error: module `Tid` is in file 'Tid.d' which cannot be read +fail_compilation/fail21091b.d(16): Error: unable to read module `Tid` +fail_compilation/fail21091b.d(16): Expected 'Tid.d' or 'Tid/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ import path[2] = $p:phobos$ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22084.d b/gcc/testsuite/gdc.test/fail_compilation/fail22084.d index 02fff2f..bd11832 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail22084.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail22084.d @@ -18,6 +18,6 @@ struct Destructor void test() { - auto a0 = Destructor; + auto a0 = Destructor(); testVariadic(1, a0); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22151.d b/gcc/testsuite/gdc.test/fail_compilation/fail22151.d new file mode 100644 index 0000000..c6c3b1b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail22151.d @@ -0,0 +1,24 @@ +// https://issues.dlang.org/show_bug.cgi?id=22151 +/* +TEST_OUTPUT: +--- +fail_compilation/fail22151.d(14): Error: function `test` is not an lvalue and cannot be modified +fail_compilation/fail22151.d(15): Error: function `test2` is not an lvalue and cannot be modified +fail_compilation/fail22151.d(18): Error: function pointed to by `fp` is not an lvalue and cannot be modified +fail_compilation/fail22151.d(21): Error: function pointed to by `ff` is not an lvalue and cannot be modified +--- +*/ + +void test() +{ + *&test = *&test; + *&test2 = *&test; + + void function() fp; + *fp = *fp; + + auto ff = &test2; + *ff = *&test2; +} + +void test2(); diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22366.d b/gcc/testsuite/gdc.test/fail_compilation/fail22366.d new file mode 100644 index 0000000..3a2469f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail22366.d @@ -0,0 +1,15 @@ +// REQUIRED_ARGS: -dip1000 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail22366.d(13): Error: scope variable `__aaval2` assigned to non-scope `aa[0]` +--- +*/ + +int* fun(scope int* x) @safe +{ + int*[int] aa; + aa[0] = x; // should give an error + return aa[0]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail225.d b/gcc/testsuite/gdc.test/fail_compilation/fail225.d deleted file mode 100644 index dee9a54..0000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail225.d +++ /dev/null @@ -1,17 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail225.d(15): Error: cannot implicitly convert expression `1` of type `int` to `immutable(char*)` -fail_compilation/fail225.d(15): Error: cannot implicitly convert expression `& ch` of type `char*` to `immutable(char*)` ---- -*/ -struct Struct { - char* chptr; -} - -void main() -{ - char ch = 'd'; - immutable Struct iStruct = {1, &ch}; -} - diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail287.d b/gcc/testsuite/gdc.test/fail_compilation/fail287.d index 7ed8f13..e5b1a79 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail287.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail287.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail287.d(14): Error: had 299 cases which is more than 256 cases in case range +fail_compilation/fail287.d(14): Error: had 300 cases which is more than 257 cases in case range --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail318.d b/gcc/testsuite/gdc.test/fail_compilation/fail318.d deleted file mode 100644 index d99175e..0000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail318.d +++ /dev/null @@ -1,8 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail318.d(8): Error: function `D main` must return `int` or `void` ---- -*/ - -auto main() { } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail318_b.d b/gcc/testsuite/gdc.test/fail_compilation/fail318_b.d new file mode 100644 index 0000000..efbf45b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail318_b.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail318_b.d(8): Error: function `D main` must return `int`, `void` or `noreturn`, not `string` +--- +*/ + +auto main() +{ + return ""; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7173.d b/gcc/testsuite/gdc.test/fail_compilation/fail7173.d index 2a2e46b..05ba7f9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail7173.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail7173.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail7173.d(23): Error: cannot implicitly convert expression `b1._a.opBinary(b2._a).fun()` of type `void` to `B` +fail_compilation/fail7173.d(23): Error: expression `b1._a.opBinary(b2._a).fun()` is `void` and has no value --- */ struct A{ diff --git a/gcc/testsuite/gdc.test/fail_compilation/foreach.d b/gcc/testsuite/gdc.test/fail_compilation/foreach.d new file mode 100644 index 0000000..9a1c7c8 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/foreach.d @@ -0,0 +1,14 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/foreach.d(12): Error: cannot declare `out` loop variable, use `ref` instead +fail_compilation/foreach.d(13): Error: cannot declare `out` loop variable, use `ref` instead +fail_compilation/foreach.d(13): Error: cannot declare `out` loop variable, use `ref` instead +--- +*/ +void main () +{ + int[] array; + foreach (out val; array) {} + foreach (out idx, out val; array) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/foreach2.d b/gcc/testsuite/gdc.test/fail_compilation/foreach2.d new file mode 100644 index 0000000..8bd4893 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/foreach2.d @@ -0,0 +1,22 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/foreach2.d(15): Error: argument type mismatch, `int` to `ref immutable(int)` +fail_compilation/foreach2.d(16): Error: argument type mismatch, `int` to `ref immutable(int)` +fail_compilation/foreach2.d(19): Error: argument type mismatch, `int` to `ref double` +fail_compilation/foreach2.d(20): Error: argument type mismatch, `int` to `ref const(double)` +fail_compilation/foreach2.d(21): Error: argument type mismatch, `int` to `ref immutable(double)` +--- +*/ +void test4090 () +{ + // From https://issues.dlang.org/show_bug.cgi?id=4090 + int[] arr = [1,2,3]; + foreach (immutable ref x; arr) {} + foreach (immutable ref int x; arr) {} + + // convertible type + qualifier + ref + foreach ( ref double x; arr) {} + foreach ( const ref double x; arr) {} + foreach (immutable ref double x; arr) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10212.d b/gcc/testsuite/gdc.test/fail_compilation/ice10212.d index b9fe2aa..99fe1ed 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10212.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10212.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10212.d(13): Error: Expected return type of `int`, not `int function() pure nothrow @nogc @safe`: +fail_compilation/ice10212.d(13): Error: expected return type of `int`, not `int function() pure nothrow @nogc @safe`: fail_compilation/ice10212.d(13): Return type of `int` inferred here. --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice22377.d b/gcc/testsuite/gdc.test/fail_compilation/ice22377.d new file mode 100644 index 0000000..4616f99 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice22377.d @@ -0,0 +1,8 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice22377.d(8): Error: Internal Compiler Error: type `string` cannot be mapped to C++ +--- +*/ + +extern(C++) void foo(string a) {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice7782.d b/gcc/testsuite/gdc.test/fail_compilation/ice7782.d index d42011a..551933b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice7782.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice7782.d @@ -2,7 +2,8 @@ EXTRA_FILES: imports/ice7782algorithm.d imports/ice7782range.d TEST_OUTPUT: ---- -fail_compilation/ice7782.d(13): Error: module `ice7782math` is in file 'imports/ice7782range/imports/ice7782math.d' which cannot be read +fail_compilation/ice7782.d(14): Error: unable to read module `ice7782math` +fail_compilation/ice7782.d(14): Expected 'imports/ice7782range/imports/ice7782math.d' or 'imports/ice7782range/imports/ice7782math/package.d' in one of the following import paths: import path[0] = fail_compilation import path[1] = $p:druntime/import$ import path[2] = $p:phobos$ diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/imp22329.d b/gcc/testsuite/gdc.test/fail_compilation/imports/imp22329.d new file mode 100644 index 0000000..9dc3c8e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/imp22329.d @@ -0,0 +1,4 @@ +void func(T)(T arg) +{ + auto a = arg + 1; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/noreturn.d b/gcc/testsuite/gdc.test/fail_compilation/noreturn.d index 3b340e8..4a588b4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/noreturn.d +++ b/gcc/testsuite/gdc.test/fail_compilation/noreturn.d @@ -72,7 +72,7 @@ noreturn casting(int i) return cast() n; } } - + assert(false); } enum forceCasting0 = casting(0); diff --git a/gcc/testsuite/gdc.test/fail_compilation/noreturn2.d b/gcc/testsuite/gdc.test/fail_compilation/noreturn2.d new file mode 100644 index 0000000..e7d28dc --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/noreturn2.d @@ -0,0 +1,90 @@ +/* +REQUIRED_ARGS: -w -o- + +TEST_OUTPUT: +--- +fail_compilation/noreturn2.d(18): Error: expected return type of `noreturn`, not `void` +--- + +https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1034.md +*/ + +alias noreturn = typeof(*null); + +void doStuff(); + +noreturn returnVoid() +{ + return doStuff(); +} + + +/+ +TEST_OUTPUT: +--- +fail_compilation/noreturn2.d(37): Error: expected return type of `int`, not `string`: +fail_compilation/noreturn2.d(35): Return type of `int` inferred here. +--- ++/ + +auto missmatch(int i) +{ + if (i < 0) + return assert(false); + if (i == 0) + return i; + if (i > 0) + return ""; +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/noreturn2.d(50): Error: function `noreturn2.returns` is typed as `NR` but does return +fail_compilation/noreturn2.d(50): `noreturn` functions must either throw, abort or loop indefinitely +--- ++/ + +enum NR : noreturn; + +NR returns() +{ + // Fallthrough despite noreturn +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/noreturn2.d(64): Error: cannot implicitly convert expression `1` of type `int` to `noreturn` +--- ++/ + +noreturn returnsValue() +{ + return 1; +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/noreturn2.d(75): Error: expected return type of `int`, not `void` +--- ++/ +int returnVoid2() +{ + return doStuff(); +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/noreturn2.d(89): Error: mismatched function return type inference of `void` and `int` +--- ++/ +auto returnVoid3(int i) +{ + if (i > 0) + return i; + else + return doStuff(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d b/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d index 034fa54..43998b9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d +++ b/gcc/testsuite/gdc.test/fail_compilation/reserved_version.d @@ -113,6 +113,8 @@ fail_compilation/reserved_version.d(214): Error: version identifier `CppRuntime_ fail_compilation/reserved_version.d(215): Error: version identifier `CppRuntime_Sun` is reserved and cannot be set fail_compilation/reserved_version.d(216): Error: version identifier `D_PIE` is reserved and cannot be set fail_compilation/reserved_version.d(217): Error: version identifier `AVR` is reserved and cannot be set +fail_compilation/reserved_version.d(218): Error: version identifier `D_PreConditions` is reserved and cannot be set +fail_compilation/reserved_version.d(219): Error: version identifier `D_PostConditions` is reserved and cannot be set --- */ @@ -232,6 +234,8 @@ version = CppRuntime_Microsoft; version = CppRuntime_Sun; version = D_PIE; version = AVR; +version = D_PreConditions; +version = D_PostConditions; // This should work though debug = DigitalMars; @@ -340,3 +344,5 @@ debug = none; debug = D_P16; debug = MSP430; debug = AVR; +debug = D_PreConditions; +debug = D_PostConditions; diff --git a/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d b/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d index b8b6fa4..6333309 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d +++ b/gcc/testsuite/gdc.test/fail_compilation/reserved_version_switch.d @@ -103,6 +103,8 @@ // REQUIRED_ARGS: -version=assert // REQUIRED_ARGS: -version=all // REQUIRED_ARGS: -version=none +// REQUIRED_ARGS: -version=D_PreConditions +// REQUIRED_ARGS: -version=D_PostConditions // REQUIRED_ARGS: -debug=DigitalMars // REQUIRED_ARGS: -debug=GNU // REQUIRED_ARGS: -debug=LDC @@ -203,6 +205,8 @@ // REQUIRED_ARGS: -debug=assert // REQUIRED_ARGS: -debug=all // REQUIRED_ARGS: -debug=none +// REQUIRED_ARGS: -debug=D_PreConditions +// REQUIRED_ARGS: -debug=D_PostConditions /* TEST_OUTPUT: --- @@ -309,5 +313,7 @@ Error: version identifier `unittest` is reserved and cannot be set Error: version identifier `assert` is reserved and cannot be set Error: version identifier `all` is reserved and cannot be set Error: version identifier `none` is reserved and cannot be set +Error: version identifier `D_PreConditions` is reserved and cannot be set +Error: version identifier `D_PostConditions` is reserved and cannot be set --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17425.d b/gcc/testsuite/gdc.test/fail_compilation/test17425.d index f7628b8..77c4956 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test17425.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test17425.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- fail_compilation/test17425.d(24): Error: parameter index must be in range 0..4 not 4 -fail_compilation/test17425.d(27): Error: first argument to `__traits(getParameterStorageClasses, i, 4)` is not a function +fail_compilation/test17425.d(27): Error: first argument to `__traits(getParameterStorageClasses, i, 4)` is not a function or a function call fail_compilation/test17425.d(29): Error: expression expected as second argument of `__traits(getParameterStorageClasses, foo, int)` fail_compilation/test17425.d(31): Error: expected 2 arguments for `getParameterStorageClasses` but had 3 --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/test17868b.d b/gcc/testsuite/gdc.test/fail_compilation/test17868b.d index 5bfff5c..7833b61 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test17868b.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test17868b.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: ---- +fail_compilation/test17868b.d(9): Error: pragma `crt_constructor` can only apply to a single declaration fail_compilation/test17868b.d(10): Error: function `test17868b.foo` must be `extern(C)` for `pragma(crt_constructor)` fail_compilation/test17868b.d(14): Error: function `test17868b.bar` must be `extern(C)` for `pragma(crt_constructor)` -fail_compilation/test17868b.d(9): Error: pragma `crt_constructor` can only apply to a single declaration ---- */ pragma(crt_constructor): diff --git a/gcc/testsuite/gdc.test/fail_compilation/test20998.d b/gcc/testsuite/gdc.test/fail_compilation/test20998.d new file mode 100644 index 0000000..16eb026 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test20998.d @@ -0,0 +1,120 @@ +// https://issues.dlang.org/show_bug.cgi?id=20998 +/* +REQUIRED_ARGS: -verrors=context +TEST_OUTPUT: +--- +fail_compilation/test20998.d(76): Error: undefined identifier `invalid` +X x = { invalid, 2, "asd" }; + ^ +fail_compilation/test20998.d(76): Error: too many initializers for `X` +X x = { invalid, 2, "asd" }; + ^ +fail_compilation/test20998.d(83): Error: cannot implicitly convert expression `"a"` of type `string` to `int` +X2 x2 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(83): Error: duplicate initializer for field `ptr` +X2 x2 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(83): Error: too many initializers for `X2` +X2 x2 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(90): Error: overlapping initialization for field `ptr` and `x` +X3 x3 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(90): Error: cannot implicitly convert expression `"a"` of type `string` to `int` +X3 x3 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(90): Error: duplicate initializer for field `ptr` +X3 x3 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(90): Error: too many initializers for `X3` +X3 x3 = { ptr: null, "a", ptr: 2, 444 }; + ^ +fail_compilation/test20998.d(98): Error: field `X4.ptr` cannot assign to misaligned pointers in `@safe` code + X4 x4 = { ptr: null, "a", 444, ptr: 2, true }; + ^ +fail_compilation/test20998.d(98): Error: cannot implicitly convert expression `"a"` of type `string` to `int` + X4 x4 = { ptr: null, "a", 444, ptr: 2, true }; + ^ +fail_compilation/test20998.d(98): Error: too many initializers for `X4` + X4 x4 = { ptr: null, "a", 444, ptr: 2, true }; + ^ +fail_compilation/test20998.d(102): called from here: `test()` +auto e = test(); + ^ +fail_compilation/test20998.d(104): Error: cannot implicitly convert expression `1` of type `int` to `void*` +X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 }; + ^ +fail_compilation/test20998.d(104): Error: duplicate initializer for field `ptr` +X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 }; + ^ +fail_compilation/test20998.d(104): Error: duplicate initializer for field `ptr` +X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 }; + ^ +fail_compilation/test20998.d(104): Error: too many initializers for `X2` +X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 }; + ^ +fail_compilation/test20998.d(107): Error: too many initializers for `X2` +X2 c6 = { null, 2, true, null }; + ^ +fail_compilation/test20998.d(116): Error: cannot implicitly convert expression `1` of type `int` to `immutable(char*)` + immutable Struct iStruct = {1, &ch}; + ^ +fail_compilation/test20998.d(116): Error: too many initializers for `Struct` + immutable Struct iStruct = {1, &ch}; + ^ +fail_compilation/test20998.d(120): called from here: `test2()` +auto t = test2(); + ^ +--- +*/ + +struct X { + void* ptr; + int x; +} +X x = { invalid, 2, "asd" }; + +struct X2 { + void* ptr; + int x; + bool y; +} +X2 x2 = { ptr: null, "a", ptr: 2, 444 }; + +union X3 { + void* ptr; + int x; + bool y; +} +X3 x3 = { ptr: null, "a", ptr: 2, 444 }; + +int test() @safe +{ + align (1) struct X4 { + void* ptr; + int x; + } + X4 x4 = { ptr: null, "a", 444, ptr: 2, true }; + return 0; +} + +auto e = test(); + +X2 a5 = { ptr: 1, ptr: 2, ptr: 444, ptr: 555 }; +X2 b5 = { ptr: null, y: true }; +X2 c5 = { x: 2, true, ptr: null }; +X2 c6 = { null, 2, true, null }; + +struct Struct { + char* chptr; +} + +int test2() +{ + char ch = 'd'; + immutable Struct iStruct = {1, &ch}; + return 0; +} + +auto t = test2(); diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21093.d b/gcc/testsuite/gdc.test/fail_compilation/test21093.d new file mode 100644 index 0000000..b85d0c3 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21093.d @@ -0,0 +1,56 @@ +// https://issues.dlang.org/show_bug.cgi?id=21093 +/* +TEST_OUTPUT: +--- +fail_compilation/test21093.d(24): Error: function `test21093.LocalTime.hasDST` does not override any function +fail_compilation/test21093.d(32): Error: class `test21093.LocalTime2` cannot implicitly generate a default constructor when base class `test21093.TimeZone2` is missing a default constructor +fail_compilation/test21093.d(44): Error: function `test21093.LocalTime3.string` does not override any function +fail_compilation/test21093.d(55): Error: cannot implicitly override base class method `test21093.TimeZone4.hasDST` with `test21093.LocalTime4.hasDST`; add `override` attribute +--- +*/ + +void fromUnixTime(immutable TimeZone tz = LocalTime()) { } +void fromUnixTime(immutable TimeZone2 tz = LocalTime2()) { } +void fromUnixTime(immutable TimeZone3 tz = LocalTime3()) { } +void fromUnixTime(immutable TimeZone4 tz = LocalTime4()) { } + +class TimeZone +{ +} + +class LocalTime : TimeZone +{ + static immutable(LocalTime) opCall() { } + override hasDST() { } +} + +class TimeZone2 +{ + this(string) { } +} + +class LocalTime2 : TimeZone2 +{ + static immutable(LocalTime2) opCall() { } +} + +class TimeZone3 +{ +} + +class LocalTime3 : TimeZone3 +{ + static immutable(LocalTime3) opCall() { } + override string () { } +} + +class TimeZone4 +{ + bool hasDST(); +} + +class LocalTime4 : TimeZone4 +{ + static immutable(LocalTime4) opCall() { } + bool hasDST() { } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21380.d b/gcc/testsuite/gdc.test/fail_compilation/test21380.d new file mode 100644 index 0000000..6a2da1b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21380.d @@ -0,0 +1,46 @@ +// https://issues.dlang.org/show_bug.cgi?id=21380 +/* +TEST_OUTPUT: +--- +fail_compilation/test21380.d(39): Error: partial template instance `MySerializer().serializeSinkType!int` has no value +fail_compilation/test21380.d(44): Error: template instance `test21380.SupportSinkTypeSer!(MySerializer!int)` error instantiating +--- +*/ + +template isSomeFunction(T...) +if (T.length == 1) +{ + static if (is(typeof(& T[0]) U : U*) && is(U == function) || is(typeof(& T[0]) U == delegate)) + { + // T is a (nested) function symbol. + enum bool isSomeFunction = true; + } + else static if (is(T[0] W) || is(typeof(T[0]) W)) + { + // T is an expression or a type. Take the type of it and examine. + static if (is(W F : F*) && is(F == function)) + enum bool isSomeFunction = true; // function pointer + else + enum bool isSomeFunction = is(W == function) || is(W == delegate); + } + else + enum bool isSomeFunction = false; +} + +struct MySerializer (T) +{ + void serializeSinkType(T2) (scope auto ref T2 record) {} +} + +template SupportSinkTypeSer(SerT) +{ + /* Note: Partial template instance because it needs inference, in this case + it cannot infer 'auto ref' parameter */ + enum SupportSinkTypeSer = isSomeFunction!(SerT.init.serializeSinkType!int); +} + +int main() +{ + enum x = SupportSinkTypeSer!(MySerializer!int); + return 0; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21930.d b/gcc/testsuite/gdc.test/fail_compilation/test21930.d new file mode 100644 index 0000000..6c93243 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21930.d @@ -0,0 +1,27 @@ +// https://issues.dlang.org/show_bug.cgi?id=21930 +/* +TEST_OUTPUT: +--- +fail_compilation/test21930.d(21): Error: variable `string` is used as a type +fail_compilation/test21930.d(15): variable `string` is declared here +fail_compilation/test21930.d(26): Error: constructor `test21930.R.this(string)` is not callable using argument types `()` +--- +*/ + +alias AliasSeq(T...) = T; + +alias TP(alias name) = AliasSeq!name; + +int string; // 'string' declared as a variable + +alias a = TP!(main); + +class R +{ + this(string) { } // so constructor have errors +} + +@system main() +{ + new R; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22329.d b/gcc/testsuite/gdc.test/fail_compilation/test22329.d new file mode 100644 index 0000000..237f9c7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test22329.d @@ -0,0 +1,21 @@ +// https://issues.dlang.org/show_bug.cgi?id=22329 +// EXTRA_FILES: imports/imp22329.d +/* +TEST_OUTPUT: +--- +fail_compilation/imports/imp22329.d(3): Error: no property `values` for type `test22329.Foo` +fail_compilation/imports/imp22329.d(3): Error: incompatible types for `(arg) + (1)`: `Foo` and `int` +fail_compilation/test22329.d(20): Error: template instance `imp22329.func!(Foo)` error instantiating +--- +*/ + +public struct Foo { + private int values; + alias values this; +} + +void main() +{ + import imports.imp22329 : func; + func(Foo()); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test22361.d b/gcc/testsuite/gdc.test/fail_compilation/test22361.d new file mode 100644 index 0000000..11255ff --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test22361.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test22361.d(11): Error: unable to read module `this_module_does_not_exist` +fail_compilation/test22361.d(11): Expected 'this_module_does_not_exist.d' or 'this_module_does_not_exist/package.d' in one of the following import paths: +import path[0] = fail_compilation +import path[1] = $p:druntime/import$ +import path[2] = $p:phobos$ +--- +*/ +import this_module_does_not_exist; diff --git a/gcc/testsuite/gdc.test/fail_compilation/testOpApply.d b/gcc/testsuite/gdc.test/fail_compilation/testOpApply.d new file mode 100644 index 0000000..9203685 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/testOpApply.d @@ -0,0 +1,161 @@ +/+ +TEST_OUTPUT: +--- +fail_compilation/testOpApply.d(27): Error: `testOpApply.SameAttr.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @safe)` matches both: +fail_compilation/testOpApply.d(13): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` +and: +fail_compilation/testOpApply.d(18): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` +--- ++/ + +struct SameAttr +{ + int opApply(int delegate(int) @system dg) @system + { + return 0; + } + + int opApply(int delegate(int) @system dg) @safe + { + return 0; + } +} + +void testSameAttr() @safe +{ + SameAttr sa; + foreach (int i; sa) {} +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/testOpApply.d(104): Error: `testOpApply.SameAttr.opApply` called with argument types `(int delegate(int i) pure nothrow @nogc @system)` matches both: +fail_compilation/testOpApply.d(13): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` +and: +fail_compilation/testOpApply.d(18): `testOpApply.SameAttr.opApply(int delegate(int) @system dg)` +--- ++/ +#line 100 + +void testSameAttr() @system +{ + SameAttr sa; + foreach (int i; sa) {} +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/testOpApply.d(217): Error: `sa.opApply` matches more than one declaration: +`fail_compilation/testOpApply.d(203)`: `int(int delegate(int) dg)` +and: +`fail_compilation/testOpApply.d(208)`: `int(int delegate(string) dg)` +fail_compilation/testOpApply.d(217): Error: cannot uniquely infer `foreach` argument types +--- ++/ +#line 200 + +struct DifferentTypes +{ + int opApply(int delegate(int) dg) + { + return 0; + } + + int opApply(int delegate(string) dg) + { + return 0; + } +} + +void testDifferentTypes() +{ + DifferentTypes sa; + foreach (i; sa) {} +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/testOpApply.d(317): Error: `sa.opApply` matches more than one declaration: +`fail_compilation/testOpApply.d(303)`: `int(int delegate(int) dg)` +and: +`fail_compilation/testOpApply.d(308)`: `int(int delegate(long) dg)` +fail_compilation/testOpApply.d(317): Error: cannot uniquely infer `foreach` argument types +--- ++/ +#line 300 + +struct CovariantTypes +{ + int opApply(int delegate(int) dg) + { + return 0; + } + + int opApply(int delegate(long) dg) + { + return 0; + } +} + +void testCovariantTypes() +{ + CovariantTypes sa; + foreach (i; sa) {} +} + +/+ +See https://issues.dlang.org/show_bug.cgi?id=21683 + +TEST_OUTPUT: +--- +fail_compilation/testOpApply.d(420): Error: `sa.opApply` matches more than one declaration: +`fail_compilation/testOpApply.d(404)`: `int(int delegate(int) dg)` +and: +`fail_compilation/testOpApply.d(410)`: `int(int delegate(ref int) dg)` +fail_compilation/testOpApply.d(420): Error: cannot uniquely infer `foreach` argument types +--- ++/ +#line 400 + +struct DifferentQualifiers +{ + int x; + int opApply(int delegate(int) dg) + { + x = 1; + return 0; + } + + int opApply(int delegate(ref int) dg) + { + x = 2; + return 0; + } +} + +void testDifferentQualifiers() +{ + DifferentQualifiers sa; + foreach (i; sa) {} +} + +/+ +TEST_OUTPUT: +--- +fail_compilation/testOpApply.d(504): Error: `sa.opApply` matches more than one declaration: +`fail_compilation/testOpApply.d(404)`: `int(int delegate(int) dg)` +and: +`fail_compilation/testOpApply.d(410)`: `int(int delegate(ref int) dg)` +fail_compilation/testOpApply.d(504): Error: cannot uniquely infer `foreach` argument types +--- ++/ +#line 500 + +void testDifferentQualifiersRef() +{ + DifferentQualifiers sa; + foreach (ref i; sa) {} +} diff --git a/gcc/testsuite/gdc.test/runnable/aliasthis.d b/gcc/testsuite/gdc.test/runnable/aliasthis.d index cc12f55..db5913c 100644 --- a/gcc/testsuite/gdc.test/runnable/aliasthis.d +++ b/gcc/testsuite/gdc.test/runnable/aliasthis.d @@ -2102,6 +2102,42 @@ void test16633() root.populate; } +/***************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=13009 + +struct RefCounted13009_2(T) +{ + ref T refCountedPayload() + { + assert(false); + } + + ref inout(T) refCountedPayload() inout + { + assert(false); + } + + alias refCountedPayload this; +} + +struct S13009_2 +{ + struct Payload + { + int[] data; + } + + RefCounted13009_2!Payload payload; + alias X = typeof(payload.data[0]); + + void foo() + { + payload.data[0] = 0; + } +} + +/***************************************************/ + int main() { test1(); diff --git a/gcc/testsuite/gdc.test/runnable/dhry.d b/gcc/testsuite/gdc.test/runnable/dhry.d index f772d61..1eb463c 100644 --- a/gcc/testsuite/gdc.test/runnable/dhry.d +++ b/gcc/testsuite/gdc.test/runnable/dhry.d @@ -929,3 +929,19 @@ version (NetBSD) return q; } } + +version (OpenBSD) +{ + import core.sys.posix.sys.time; + + double dtime() + { + double q; + timeval tv; + + gettimeofday(&tv,null); + q = cast(double)tv.tv_sec + cast(double)tv.tv_usec * 1.0e-6; + + return q; + } +} diff --git a/gcc/testsuite/gdc.test/runnable/fix22372.d b/gcc/testsuite/gdc.test/runnable/fix22372.d new file mode 100644 index 0000000..55864a0 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/fix22372.d @@ -0,0 +1,38 @@ +/* PERMUTE_ARGS: -O + */ + +// https://issues.dlang.org/show_bug.cgi?id=22104 + +struct S { int a1, a2, a3; } + +version (none) +void throws2ndCall(ref S x); +else +{ +void throws2ndCall(ref S x) +{ + __gshared bool b; + if (b) + throw new Exception("n == 1"); + b = true; +} +} + +void main() { foo(); } + +void foo() +{ + S[] arr = [S(), S()]; + size_t i; + try + { + for (i = 0; i < 2; i++) + throws2ndCall(arr[i]); + } + catch (Exception o) + { + //printf("Exception: i = %lu\n", i); + assert(i == 1); // this fails + } +} + diff --git a/gcc/testsuite/gdc.test/runnable/interpret.d b/gcc/testsuite/gdc.test/runnable/interpret.d index a626749..989fb2e 100644 --- a/gcc/testsuite/gdc.test/runnable/interpret.d +++ b/gcc/testsuite/gdc.test/runnable/interpret.d @@ -3605,6 +3605,62 @@ void test21878() } /************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=20133 + +void bar20133(ref string text) +{ + text = text[1 .. $]; + assert(text.length < 3); + if (text.length == 2) assert(text == "oo"); + if (text.length == 1) assert(text == "o"); + if (text.length == 0) assert(text == ""); + string tcopy = text; + if (tcopy.length > 0) + bar20133(tcopy); + assert(tcopy.length < 2); + if (tcopy.length == 1) assert(tcopy == "o"); + if (tcopy.length == 0) assert(tcopy == ""); +} + +void bar20133_2(ref string text) +{ + auto ptext = &text; + *ptext = text[1 .. $]; + assert(text.length < 3); + if (text.length == 2) assert(text == "oo"); + if (text.length == 1) assert(text == "o"); + if (text.length == 0) assert(text == ""); + string tcopy = text; + if (tcopy.length > 0) + bar20133_2(tcopy); + assert(tcopy.length < 2); + if (tcopy.length == 1) assert(tcopy == "o"); + if (tcopy.length == 0) assert(tcopy == ""); +} + +alias fun20133 = { + string input = "foo"; + bar20133(input); + assert(input == "oo"); + return input; +}; + +alias fun20133_2 = { + string input = "foo"; + bar20133_2(input); + assert(input == "oo"); + return input; +}; + +void test20133() +{ + enum ctest = fun20133(); + enum ctest2 = fun20133_2(); + auto rtest = fun20133(); + auto rtest2 = fun20133_2(); +} + +/************************************************/ int main() { @@ -3732,6 +3788,7 @@ int main() test20366(); test20400(); test21878(); + test20133(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/noreturn1.d b/gcc/testsuite/gdc.test/runnable/noreturn1.d index 447ea28..1da0479 100644 --- a/gcc/testsuite/gdc.test/runnable/noreturn1.d +++ b/gcc/testsuite/gdc.test/runnable/noreturn1.d @@ -66,9 +66,56 @@ void test2() /*****************************************/ +struct BasicStruct +{ + int firstInt; + noreturn noRet; + long lastLong; +} + +struct AlignedStruct +{ + int firstInt; + align(16) noreturn noRet; + long lastLong; +} + +void takeBasic(BasicStruct bs) +{ + assert(bs.firstInt == 13); + assert(bs.lastLong == 42); + + assert(&bs.noRet == (&bs.firstInt + 1)); +} + +void takeAligned(AlignedStruct as) +{ + assert(as.firstInt == 99); + assert(as.lastLong == 0xDEADBEEF); + + assert(&as.noRet == &as.lastLong); +} + +void test3() +{ + { + BasicStruct bs; + bs.firstInt = 13; + bs.lastLong = 42; + takeBasic(bs); + } + { + AlignedStruct as; + as.firstInt = 99; + as.lastLong = 0xDEADBEEF; + takeAligned(as); + } +} + int main() { test1(); test2(); + test3(); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/noreturn2.d b/gcc/testsuite/gdc.test/runnable/noreturn2.d new file mode 100644 index 0000000..1d3d362 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/noreturn2.d @@ -0,0 +1,220 @@ +/* +PERMUTE_ARGS: -O -inline +RUN_OUTPUT: +--- +getAndPrintS +--- +*/ + +import core.stdc.stdio; +import core.exception : AssertError; + +/*****************************************/ + +// noreturn is inferred for functions that always throw +// The code must not strip the destructor when calling a noreturn function + +struct WithDtor +{ + __gshared int destroyed; + + int num; + + int acceptNoreturn(int a, int b, int c) + { + puts("unreachable"); + return num + a + b + c; + } + + ~this() + { + destroyed += num; + } +} + +noreturn doesThrow() +{ + WithDtor wd = WithDtor(1); + throw new Exception(""); +} + +noreturn callDoesThrow() +{ + WithDtor wd = WithDtor(2); + doesThrow(); +} + + +void testDtors() +{ + try + { + callDoesThrow(); + assert(0); + } catch (Exception e) {} + + assert(WithDtor.destroyed == 3); +} + +/*****************************************************************************/ + +/// Verifies that `func` throws a `Throwable` with `message` at `line` +void testAssertFailure(size_t expLine, string expMsg, void function() func, size_t callLine = __LINE__) +{ + void enforce(bool check, string error) + { + if (!check) + throw new AssertError(error, __FILE__, callLine); + } + + bool caught; + try + { + func(); + } + catch (Throwable t) + { + // Save members because t might be overwritten by an Assertion failure below + string actFile = t.file; + size_t actLine = t.line; + string actMsg = t.msg; + caught = true; + + scope (failure) + { + printf("\nfile = \"%.*s\"\nline = %zu\nmsg = \"%.*s\"\n\n", + cast(int) actFile.length, actFile.ptr, + actLine, + cast(int) actMsg.length, actMsg.ptr + ); + fflush(stdout); + } + + enforce(actFile == __FILE__, "Wrong file"); + enforce(actLine == expLine, "Wrong line"); + enforce(actMsg == expMsg, "Wrong message"); + } + + enforce(caught, "No Throwable was thrown!"); +} + +void testAccess() +{ + enum msg = "Accessed expression of type `noreturn`"; + + // FIXME: Another assertion failure in the backend trying to generate noreturn.sizeof = 0 byte assignment + version (FIXME) + testAssertFailure(__LINE__ + 3, msg, function noreturn() + { + noreturn a; + noreturn b = a; + }); + + if (false) // read does not assert! + testAssertFailure(__LINE__ + 3, msg, function noreturn() + { + noreturn a; + int b = a; + assert(false, "Unreachable!"); // Statement above not detected as noreturn + }); + + testAssertFailure(__LINE__ + 2, msg, function noreturn() + { + cast(noreturn) 1; + }); + + version (FIXME) + testAssertFailure(__LINE__ + 3, msg, function noreturn() + { + noreturn a; + noreturn b = cast(noreturn) 1; + }); + + if (false) // Read does not assert + testAssertFailure(__LINE__ + 3, msg, function noreturn() + { + noreturn a; + return a; + }); + + if (false) // Read does not assert + testAssertFailure(__LINE__ + 4, msg, function noreturn() + { + static void foo(noreturn) {} + noreturn a; + foo(a); + assert(false, "Unreachable!"); // Ditto + }); +} + +/*****************************************/ + +void testFuncCall() +{ + enum msg = "Called abort()"; + enum line = __LINE__ + 1; + static noreturn abort() { assert(0, msg); } + + // Canaries to check for side effects + __gshared int countLeft, countRight; + + scope (failure) printf("countLeft = %d\ncountRight = %d\n", countLeft, countRight); + + + // D function arguments are evaluated left to right + testAssertFailure(line, msg, function() + { + static void acceptNoreturnD(int, int, int) { puts("unreachable"); } + + acceptNoreturnD(countLeft++, abort(), countRight++); + }); + + assert(countLeft == 1); + assert(countRight == 0); + +// // C function arguments are still evaluated left to right +// // Despite them being push in reverse order + testAssertFailure(line, msg, function() + { + static extern(C) void acceptNoreturnC(int, int, int) { puts("unreachable"); } + + acceptNoreturnC(countLeft++, abort(), countRight++); + + assert(false); + }); + + assert(countLeft == 2); + assert(countRight == 0); + + WithDtor.destroyed = 0; + + testAssertFailure(__LINE__ + 2, "Error", function() + { + static WithDtor getS() { assert(false, "Error"); } + + getS().acceptNoreturn(countLeft++, abort(), countRight++); + }); + + assert(countLeft == 2); // No changes + assert(countRight == 0); + assert(WithDtor.destroyed == 0); // No temporary to destruct + + testAssertFailure(line, msg, function() + { + static WithDtor getAndPrintS() { puts("getAndPrintS"); return WithDtor(1); } + + getAndPrintS().acceptNoreturn(countLeft++, abort(), countRight++); + }); + + assert(countLeft == 3); + assert(countRight == 0); + assert(WithDtor.destroyed == 1); +} + +int main() +{ + testDtors(); + testAccess(); + testFuncCall(); + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/sroa13220.d b/gcc/testsuite/gdc.test/runnable/sroa13220.d new file mode 100644 index 0000000..2cec666 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/sroa13220.d @@ -0,0 +1,103 @@ +/* REQUIRED_ARGS: -O -inline -noboundscheck + */ +// https://github.com/dlang/pull/13220 + +version (D_SIMD) +{ + +mixin template VectorOps(VectorType, ArrayType: BaseType[N], BaseType, size_t N) +{ + enum Count = N; + alias Base = BaseType; + + BaseType* ptr() return pure nothrow @nogc + { + return array.ptr; + } + + // Unary operators + VectorType opUnary(string op)() pure nothrow @safe @nogc + { + VectorType res = void; + mixin("res.array[] = " ~ op ~ "array[];"); + return res; + } + + // Binary operators + VectorType opBinary(string op)(VectorType other) pure const nothrow @safe @nogc + { + VectorType res = void; + mixin("res.array[] = array[] " ~ op ~ " other.array[];"); + return res; + } + + // Assigning a BaseType value + void opAssign(BaseType e) pure nothrow @safe @nogc + { + array[] = e; + } + + // Assigning a static array + void opAssign(ArrayType v) pure nothrow @safe @nogc + { + array[] = v[]; + } + + void opOpAssign(string op)(VectorType other) pure nothrow @safe @nogc + { + mixin("array[] " ~ op ~ "= other.array[];"); + } + + // Assigning a dyn array + this(ArrayType v) pure nothrow @safe @nogc + { + array[] = v[]; + } + + // Broadcast constructor + this(BaseType x) pure nothrow @safe @nogc + { + array[] = x; + } + + ref inout(BaseType) opIndex(size_t i) inout pure nothrow @safe @nogc + { + return array[i]; + } +} + +// Note: can't be @safe with this signature +Vec loadUnaligned(Vec)(const(BaseType!Vec)* pvec) @trusted +{ + // Since this vector is emulated, it doesn't have alignement constraints + // and as such we can just cast it. + return *cast(Vec*)(pvec); +} + +private template BaseType(V) +{ + alias typeof( ( { V v; return v; }()).array[0]) BaseType; +} + +struct int4 +{ + int[4] array; + mixin VectorOps!(int4, int[4]); +} + +alias __m128i = int4; +} + +int main() +{ + version (D_SIMD) + { + int4 A = [1, 2, 3, 4]; + int4 ia = A; + ia.ptr[2] = 5; + int4 C = ia; + int[4] result = [1, 2, 5, 4]; + assert(C.array == result); + } + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/test15624.d b/gcc/testsuite/gdc.test/runnable/test15624.d deleted file mode 100644 index 7927579..0000000 --- a/gcc/testsuite/gdc.test/runnable/test15624.d +++ /dev/null @@ -1,51 +0,0 @@ -/* PERMUTE_ARGS: - */ - -// https://issues.dlang.org/show_bug.cgi?id=15624 - -struct Foo { - int x; - int opApply(int delegate(int, string, string) @safe dg) @safe { - x = 1; - return 0; - } - int opApply(int delegate(int, string, string) @system dg) @system { - x = 2; - return 0; - } -} - -void testSafe() @safe { - Foo foo; - foreach (i, k, v; foo) { - } - assert(foo.x == 1); -} - -void testSystem() @system { - Foo foo; - foreach (i, k, v; foo) { - } - assert(foo.x == 2); -} - -void test() @system -{ - Foo f; - - int dgsafe (int x, string s, string t) @safe { return 1; } - int dgsystem(int x, string s, string t) @system { return 1; } - - f.opApply(&dgsafe); - assert(f.x == 1); - f.opApply(&dgsystem); - assert(f.x == 2); -} - -int main() -{ - testSafe(); - testSystem(); - test(); - return 0; -} diff --git a/gcc/testsuite/gdc.test/runnable/test21039.d b/gcc/testsuite/gdc.test/runnable/test21039.d new file mode 100644 index 0000000..c58600f --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test21039.d @@ -0,0 +1,27 @@ +// https://issues.dlang.org/show_bug.cgi?id=21039 + +class Inner {} + +class Outer { + Inner inner; + alias inner this; + this(Inner i) { inner = i; } +} + +void main() { + auto inner = new Inner; + auto outer = new Outer(new Inner); + + // implicit cast goes through 'alias this' + + Inner inner1 = outer; // Already does it + assert(inner1); + + Inner[] inners = [inner, outer]; // Fixed + + assert(inners[0], "first element is null"); + assert(inners[1], "second element is null"); + + Inner inner2 = 1 ? outer : inner; // Fixed + assert(inner2); +} diff --git a/gcc/testsuite/gdc.test/runnable/test22205.d b/gcc/testsuite/gdc.test/runnable/test22205.d new file mode 100644 index 0000000..78abf2f --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test22205.d @@ -0,0 +1,17 @@ +// REQUIRED_ARGS: -debug + +void main() nothrow +{ + debug + { + try + { + throw new Exception("2"); + } + catch (Exception) {} + catch (Throwable) + { + assert(0); + } + } +} diff --git a/gcc/testsuite/gdc.test/runnable/test22278.d b/gcc/testsuite/gdc.test/runnable/test22278.d new file mode 100644 index 0000000..72332a4 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test22278.d @@ -0,0 +1,24 @@ +/* +REQUIRED_ARGS: -release +PERMUTE_ARGS: -check=in=on -check=out=on +*/ + +// https://issues.dlang.org/show_bug.cgi?id=22278 + +bool resultIn; +bool resultOut; + +void foo22278() + in { resultIn = true; } + out { resultOut = true; } +do {} + +int main() +{ + foo22278(); + + version(D_PreConditions) assert(resultIn); else assert(!resultIn); + version(D_PostConditions) assert(resultOut); else assert(!resultOut); + + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/testOpApply.d b/gcc/testsuite/gdc.test/runnable/testOpApply.d new file mode 100644 index 0000000..7b884e5 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testOpApply.d @@ -0,0 +1,142 @@ +/* PERMUTE_ARGS: + */ + +// https://issues.dlang.org/show_bug.cgi?id=15624 + +struct Foo { + int x; + int opApply(int delegate(int, string, string) @safe dg) @safe { + x = 1; + return 0; + } + int opApply(int delegate(int, string, string) @system dg) @system { + x = 2; + return 0; + } +} + +void testSafe() @safe { + Foo foo; + foreach (i, k, v; foo) { + } + assert(foo.x == 1); +} + +void testSystem() @system { + Foo foo; + foreach (i, k, v; foo) { + } + assert(foo.x == 2); +} + +void test() @system +{ + Foo f; + + int dgsafe (int x, string s, string t) @safe { return 1; } + int dgsystem(int x, string s, string t) @system { return 1; } + + f.opApply(&dgsafe); + assert(f.x == 1); + f.opApply(&dgsystem); + assert(f.x == 2); +} + +int main() +{ + testSafe(); + testSystem(); + test(); + testDifferentTypes(); + testSameAttributes(); + testInverseAttributes(); + return 0; +} + +void testDifferentTypes() +{ + static struct DifferentTypes + { + int x; + int opApply(int delegate(int) dg) @safe { + x = 1; + return 0; + } + int opApply(int delegate(long) dg) @safe { + x = 2; + return 0; + } + } + + DifferentTypes dt; + foreach (int i; dt) {} + assert(dt.x == 1); + + foreach (long i; dt) {} + assert(dt.x == 2); +} + +void testSameAttributes() +{ + static struct SameAttributes + { + int x; + int opApply(int delegate(int) @system dg) @safe { + x = 1; + return 0; + } + int opApply(int delegate(int) @safe dg) @safe { + x = 2; + return 0; + } + } + + static void safe() @safe + { + SameAttributes sa; + foreach (i; sa) {} + assert(sa.x == 2); + } + safe(); + + static void system() @system + { + SameAttributes sa; + foreach (i; sa) {} + assert(sa.x == 1); + } + system(); +} + +// Not useful but enabled by the associated patch +void testInverseAttributes() +{ + static struct InverseAttributes + { + int x; + int opApply(int delegate(int) @system dg) @safe { + x = 1; + return 0; + } + int opApply(int delegate(int) @safe dg) @system { + x = 2; + return 0; + } + } + + static void system() @system + { + InverseAttributes sa; + foreach (i; sa) {} + assert(sa.x == 1); + } + system(); + + static void safe() @safe + { + InverseAttributes sa; + (() @trusted { foreach (i; sa) {} })(); + assert(sa.x == 2); + } + safe(); +} diff --git a/gcc/testsuite/gdc.test/runnable/testmainb.d b/gcc/testsuite/gdc.test/runnable/testmainb.d new file mode 100644 index 0000000..d6452ec --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testmainb.d @@ -0,0 +1,15 @@ +/* +Test that -main does nothing when main is already defined + +REQUIRED_ARGS: -main +RUN_OUTPUT: +--- +Success +--- +*/ +extern(C) int printf(const char*, ...); + +void main() +{ + printf("Success\n"); +} diff --git a/gcc/testsuite/gdc.test/runnable/uda.d b/gcc/testsuite/gdc.test/runnable/uda.d index cdb9aa6..1d01098 100644 --- a/gcc/testsuite/gdc.test/runnable/uda.d +++ b/gcc/testsuite/gdc.test/runnable/uda.d @@ -697,6 +697,54 @@ static if(is(typeof(foo20831) Params20831 == __parameters)) /************************************************/ +/************************************************/ +// https://issues.dlang.org/show_bug.cgi?id=15804 + +template test15804() +{ + alias AliasSeq(T...) = T; + + @(42) struct Foo(D) {} + auto fooFac(T)() + { + static assert(__traits(getAttributes, Foo) == AliasSeq!42); + static assert(__traits(getAttributes, Foo!int) == AliasSeq!42); + return Foo!T(); + } + + auto booFac(T)() + { + @(43) struct Boo {} + static assert(__traits(getAttributes, Boo) == AliasSeq!43); + return Boo(); + } + + auto barFac(T)() + { + @(44) struct Bar(D) {} + static assert(__traits(getAttributes, Bar) == AliasSeq!44); // Fixed + static assert(__traits(getAttributes, Bar!int) == AliasSeq!44); + return Bar!T(); + } + + auto bazFac(T)() + { + @(45) static struct Baz(D) {} + static assert(__traits(getAttributes, Baz) == AliasSeq!45); // Fixed + static assert(__traits(getAttributes, Baz!int) == AliasSeq!45); + return Baz!T(); + } + + auto foo = fooFac!int; + auto boo = booFac!int; + auto bar = barFac!int; + auto baz = bazFac!int; +} + +alias a15804 = test15804!(); + +/************************************************/ + int main() { test1(); diff --git a/gcc/testsuite/gdc.test/runnable/ufcs.d b/gcc/testsuite/gdc.test/runnable/ufcs.d index 2d9bf15..8fd7bb2 100644 --- a/gcc/testsuite/gdc.test/runnable/ufcs.d +++ b/gcc/testsuite/gdc.test/runnable/ufcs.d @@ -196,6 +196,7 @@ void test5() { // f5_1 .. f5_5 are symbols which declared in module scope assert(100.f5_1() == 1); + assert(001.f5_1() == 1); // https://issues.dlang.org/show_bug.cgi?id=8346 assert("s".f5_2() == 2); assert(1.4.f5_3() == 3); assert(100.f5_4() == 1); diff --git a/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp22287.cpp b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp22287.cpp new file mode 100644 index 0000000..ba7b25a --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable_cxx/extra-files/cpp22287.cpp @@ -0,0 +1,337 @@ +#include <assert.h> + +class X +{ +public: + virtual ~X(); + int i; +}; + +X::~X() +{ +} + +class Y : public X +{ +}; + +class A +{ +public: + virtual ~A(); + virtual int f1() const; + + int i; +}; + +class I +{ +public: + virtual int f2() const = 0; + virtual X *f4() = 0; +}; + +class B : public A, public I +{ +public: + virtual int f1() const; + virtual int f2() const; + virtual int f3() const; + virtual X *f4(); +}; + +class C : public B +{ +public: + virtual int f1() const; + virtual int f2() const; + virtual int f3() const; + virtual Y *f4(); +}; + +#ifdef _WIN32 +class D : public B +{ +public: + virtual int f1() const; + virtual int f2() const; + virtual int f3() const; + virtual Y *f4(); +}; + +class E : public B +{ +public: + virtual int f1() const; + virtual int f2() const; + virtual int f3() const; + virtual Y *f4(); +}; +#endif + +A::~A() +{ +} + +int A::f1() const +{ + return i + 11; +} + +int B::f1() const +{ + return i + 21; +} + +int B::f2() const +{ + return i + 22; +} + +int B::f3() const +{ + return i + 23; +} + +X *B::f4() +{ + X *r = new X; + r->i = i + 24; + return r; +} + +int C::f1() const +{ + return i + 31; +} + +int C::f2() const +{ + return i + 32; +} + +int C::f3() const +{ + return i + 33; +} + +Y *C::f4() +{ + Y *r = new Y; + r->i = i + 34; + return r; +} + +I *createIFromCPP(char type, int i) +{ + switch (type) + { + case 'B': + { + B *b = new B(); + b->i = i; + return b; + } + case 'C': + { + C *c = new C(); + c->i = i; + return c; + } +#ifdef _WIN32 + case 'D': + { + D *d = new D(); + d->i = i; + return d; + } + case 'E': + { + E *e = new E(); + e->i = i; + return e; + } +#endif + default: + return 0; + } +} + +B *createBFromCPP(char type, int i) +{ + switch (type) + { + case 'B': + { + B *b = new B(); + b->i = i; + return b; + } + case 'C': + { + C *c = new C(); + c->i = i; + return c; + } +#ifdef _WIN32 + case 'D': + { + D *d = new D(); + d->i = i; + return d; + } + case 'E': + { + E *e = new E(); + e->i = i; + return e; + } +#endif + default: + return 0; + } +} + +C *createCFromCPP(int i) +{ + C *c = new C(); + c->i = i; + return c; +} + +#ifdef _WIN32 +D *createDFromCPP(int i) +{ + D *d = new D(); + d->i = i; + return d; +} + +E *createEFromCPP(int i) +{ + E *e = new E(); + e->i = i; + return e; +} +#endif + +I *createIFromD(char type, int i); +B *createBFromD(char type, int i); +C *createCFromD(int i); +#ifdef _WIN32 +D *createDFromD(int i); +E *createEFromD(int i); +#endif + +void runCPPTests() +{ + { + B *b = new B(); + b->i = 100; + assert(b->f1() == 121); + assert(b->f2() == 122); + assert(b->f3() == 123); + assert(b->f4()->i == 124); + } + { + C *c = new C(); + c->i = 100; + assert(c->f1() == 131); + assert(c->f2() == 132); + assert(c->f3() == 133); + assert(c->f4()->i == 134); + } +#ifdef _WIN32 + { + D *d = new D(); + d->i = 100; + assert(d->f1() == 141); + assert(d->f2() == 142); + assert(d->f3() == 143); + assert(d->f4()->i == 144); + } + { + E *e = new E(); + e->i = 100; + assert(e->f1() == 151); + assert(e->f2() == 152); + assert(e->f3() == 153); + assert(e->f4()->i == 154); + } +#endif + { + I *i = createIFromD('B', 100); + assert(i->f2() == 122); + assert(i->f4()->i == 124); + } + { + I *i = createIFromD('C', 100); + assert(i->f2() == 132); + assert(i->f4()->i == 134); + } +#ifdef _WIN32 + { + I *i = createIFromD('D', 100); + assert(i->f2() == 142); + assert(i->f4()->i == 144); + } + { + I *i = createIFromD('E', 100); + assert(i->f2() == 152); + assert(i->f4()->i == 154); + } +#endif + { + B *b = createBFromD('B', 100); + assert(b->f1() == 121); + assert(b->f2() == 122); + assert(b->f3() == 123); + assert(b->f4()->i == 124); + } + { + B *b = createBFromD('C', 100); + assert(b->f1() == 131); + assert(b->f2() == 132); + assert(b->f3() == 133); + assert(b->f4()->i == 134); + } +#ifdef _WIN32 + { + B *b = createBFromD('D', 100); + assert(b->f1() == 141); + assert(b->f2() == 142); + assert(b->f3() == 143); + assert(b->f4()->i == 144); + } + { + B *b = createBFromD('E', 100); + assert(b->f1() == 151); + assert(b->f2() == 152); + assert(b->f3() == 153); + assert(b->f4()->i == 154); + } +#endif + { + C *c = createCFromD(100); + assert(c->f1() == 131); + assert(c->f2() == 132); + assert(c->f3() == 133); + assert(c->f4()->i == 134); + } +#ifdef _WIN32 + { + D *d = createDFromD(100); + assert(d->f1() == 141); + assert(d->f2() == 142); + assert(d->f3() == 143); + assert(d->f4()->i == 144); + } + { + E *e = createEFromD(100); + assert(e->f1() == 151); + assert(e->f2() == 152); + assert(e->f3() == 153); + assert(e->f4()->i == 154); + } +#endif +} diff --git a/gcc/testsuite/gdc.test/runnable_cxx/test22287.d b/gcc/testsuite/gdc.test/runnable_cxx/test22287.d new file mode 100644 index 0000000..a0c7475 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable_cxx/test22287.d @@ -0,0 +1,327 @@ +// EXTRA_CPP_SOURCES: cpp22287.cpp + +extern(C++): + +class X +{ +public: + ~this(); + int i; +} + +class Y : X +{ +} + +class A +{ + ~this(); + int f1() const; + + int i; +} + +interface I +{ + int f2() const; + X f4(); +} + +class B : A, I +{ + override int f1() const; + override int f2() const; + int f3() const; + override X f4(); +} + +class C : B +{ + override int f1() const; + override int f2() const; + override int f3() const; + override Y f4(); +} + +version(Windows) +{ +class D : B +{ + override int f1() const + { + return i + 41; + } + + override int f2() const + { + return i + 42; + } + + override int f3() const + { + return i + 43; + } + + override Y f4() + { + Y r = new Y; + r.i = i + 44; + return r; + } +} + +mixin template MixinE() +{ + override int f1() const + { + return i + 51; + } + + override int f2() const + { + return i + 52; + } + + override int f3() const + { + return i + 53; + } + + override Y f4() + { + Y r = new Y; + r.i = i + 54; + return r; + } +} + +class E : B +{ + mixin MixinE; +} +} + +I createIFromCPP(char type, int i); +B createBFromCPP(char type, int i); +C createCFromCPP(int i); +version(Windows) +{ +D createDFromCPP(int i); +E createEFromCPP(int i); +} + +I createIFromD(char type, int i) +{ + switch (type) + { + case 'B': + { + B b = new B(); + b.i = i; + return b; + } + case 'C': + { + C c = new C(); + c.i = i; + return c; + } + version(Windows) + { + case 'D': + { + D d = new D(); + d.i = i; + return d; + } + case 'E': + { + E e = new E(); + e.i = i; + return e; + } + } + default: + return null; + } +} + +B createBFromD(char type, int i) +{ + switch (type) + { + case 'B': + { + B b = new B(); + b.i = i; + return b; + } + case 'C': + { + C c = new C(); + c.i = i; + return c; + } + version(Windows) + { + case 'D': + { + D d = new D(); + d.i = i; + return d; + } + case 'E': + { + E e = new E(); + e.i = i; + return e; + } + } + default: + return null; + } +} + +C createCFromD(int i) +{ + C c = new C(); + c.i = i; + return c; +} + +version(Windows) +{ +D createDFromD(int i) +{ + D d = new D(); + d.i = i; + return d; +} + +E createEFromD(int i) +{ + E e = new E(); + e.i = i; + return e; +} +} + +void runCPPTests(); + +extern(D) void main() +{ + { + B b = new B(); + b.i = 100; + assert(b.f1() == 121); + assert(b.f2() == 122); + assert(b.f3() == 123); + assert(b.f4().i == 124); + } + { + C c = new C(); + c.i = 100; + assert(c.f1() == 131); + assert(c.f2() == 132); + assert(c.f3() == 133); + assert(c.f4().i == 134); + } + version(Windows) + { + { + D d = new D(); + d.i = 100; + assert(d.f1() == 141); + assert(d.f2() == 142); + assert(d.f3() == 143); + assert(d.f4().i == 144); + } + { + E e = new E(); + e.i = 100; + assert(e.f1() == 151); + assert(e.f2() == 152); + assert(e.f3() == 153); + assert(e.f4().i == 154); + } + } + { + I i = createIFromCPP('B', 100); + assert(i.f2() == 122); + assert(i.f4().i == 124); + } + { + I i = createIFromCPP('C', 100); + assert(i.f2() == 132); + assert(i.f4().i == 134); + } + version(Windows) + { + { + I i = createIFromCPP('D', 100); + assert(i.f2() == 142); + assert(i.f4().i == 144); + } + { + I i = createIFromCPP('E', 100); + assert(i.f2() == 152); + assert(i.f4().i == 154); + } + } + { + B b = createBFromCPP('B', 100); + assert(b.f1() == 121); + assert(b.f2() == 122); + assert(b.f3() == 123); + assert(b.f4().i == 124); + } + { + B b = createBFromCPP('C', 100); + assert(b.f1() == 131); + assert(b.f2() == 132); + assert(b.f3() == 133); + assert(b.f4().i == 134); + } + version(Windows) + { + { + B b = createBFromCPP('D', 100); + assert(b.f1() == 141); + assert(b.f2() == 142); + assert(b.f3() == 143); + assert(b.f4().i == 144); + } + { + B b = createBFromCPP('E', 100); + assert(b.f1() == 151); + assert(b.f2() == 152); + assert(b.f3() == 153); + assert(b.f4().i == 154); + } + } + { + C c = createCFromCPP(100); + assert(c.f1() == 131); + assert(c.f2() == 132); + assert(c.f3() == 133); + assert(c.f4().i == 134); + } + version(Windows) + { + { + D d = createDFromCPP(100); + assert(d.f1() == 141); + assert(d.f2() == 142); + assert(d.f3() == 143); + assert(d.f4().i == 144); + } + { + E e = createEFromCPP(100); + assert(e.f1() == 151); + assert(e.f2() == 152); + assert(e.f3() == 153); + assert(e.f4().i == 154); + } + } + runCPPTests(); +} |