From patchwork Wed Dec 8 23:56:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Buclaw X-Patchwork-Id: 1565535 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.a=rsa-sha256 header.s=default header.b=u47doNfF; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4J8YyF4xf2z9sVq for ; Thu, 9 Dec 2021 10:57:57 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 504A03858408 for ; Wed, 8 Dec 2021 23:57:55 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 504A03858408 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1639007875; bh=xmurIiTPNKkC4pgJhqAvQJnymHv2Z4zYP6AaUIf/ZJw=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=u47doNfF4U/tMkfx5mO/HnI+wgrhNREdroF3YFxPeT+meuu8r1zCgv+I7J4YhCGU5 z7bhpTjA6KSfdQ0wFjb2zE/5GO2v5XCZA3cfS+HGOyThYqeOoOOBRQbMBssQtKbCMH GBUoHfSEKlgxYiUDg+GSXqStcbHXTfFAiXmpIr4E= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-201.mailbox.org (mout-p-201.mailbox.org [IPv6:2001:67c:2050::465:201]) by sourceware.org (Postfix) with ESMTPS id 218333858D35 for ; Wed, 8 Dec 2021 23:56:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 218333858D35 Received: from smtp202.mailbox.org (smtp202.mailbox.org [80.241.60.245]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-201.mailbox.org (Postfix) with ESMTPS id 4J8Ywl42htzQjxM; Thu, 9 Dec 2021 00:56:39 +0100 (CET) X-Virus-Scanned: amavisd-new at heinlein-support.de To: gcc-patches@gcc.gnu.org Subject: [committed 1/4] d: Merge upstream dmd 568496d5b Date: Thu, 9 Dec 2021 00:56:23 +0100 Message-Id: <20211208235624.3251697-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Iain Buclaw via Gcc-patches From: Iain Buclaw Reply-To: Iain Buclaw Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Hi, This patch merges the D front-end implementation with upstream dmd 568496d5b, bumping the internal version to 2.098.0. 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. Bootstrapped and regression tested on x86_64-linux-gnu/-m32/-mx32, and committed to mainline. Regards, Iain. --- 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. --- gcc/d/Make-lang.in | 10 +- gcc/d/d-builtins.cc | 7 +- gcc/d/d-codegen.cc | 36 +- gcc/d/d-frontend.cc | 11 +- gcc/d/d-gimplify.cc | 65 +- gcc/d/d-lang.cc | 16 + gcc/d/d-tree.h | 9 +- gcc/d/decl.cc | 155 ++-- gcc/d/dmd/MERGE | 2 +- gcc/d/expr.cc | 9 +- gcc/d/gdc.texi | 9 + gcc/d/lang.opt | 4 + gcc/d/modules.cc | 2 +- gcc/d/types.cc | 19 +- 111 files changed, 4199 insertions(+), 2920 deletions(-) diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 4ce11e3cada..d7f714760f7 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 ab3a950689f..ea8e1eda244 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 403e3c74377..c082ac5ab80 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 *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 522095f12c5..b2e52c0c5e7 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 0fa744247ff..e3668815731 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 dbf7a8b60b9..576eefcc01f 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 328b6b861d2..a5970d8a2e3 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -47,7 +47,6 @@ typedef Array 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 e28a581a7ec..a4976b68bf0 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 % for %", - 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 129050b6780..d23e1fedba4 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 720d25683af..3cb7e127bb6 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -20,6 +20,7 @@ |--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [dmd/](https://github.com/dlang/dmd/tree/master/src/dmd) | The dmd driver and front-end | | [dmd/backend/](https://github.com/dlang/dmd/tree/master/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). | +| [dmd/common/](https://github.com/dlang/dmd/tree/master/src/dmd/common) | Code shared by the front-end and back-end | | [dmd/root/](https://github.com/dlang/dmd/tree/master/src/dmd/root) | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516). | DMD has a mostly flat directory structure, so this section aims to divide all source files into logical groups for easier navigation. @@ -126,6 +127,7 @@ | [optimize.d](https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d) | Do constant folding more generally | | [dcast.d](https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d) | Implicit or explicit cast(), finding common types e.g. in `x ? a : b`, integral promotions | | [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types | +| [importc.d](https://github.com/dlang/dmd/blob/master/src/dmd/importc.d) | Helpers specific to ImportC | | [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. | **Compile Time Function Execution (CTFE)** @@ -243,14 +245,14 @@ Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/master/src/dmd/root). -| File | Purpose | -|-----------------------------------------------------------------------------|---------------------------------------------------| -| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/env.d) | Modify environment variables | -| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color | -| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/utf.d) | Encoding/decoding Unicode text | -| [filecache.d](https://github.com/dlang/dmd/blob/master/src/dmd/filecache.d) | Keep file contents in memory | -| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths | -| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/complex.d) | A complex number type | +| File | Purpose | +|-----------------------------------------------------------------------------------|---------------------------------------------------| +| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/env.d) | Modify environment variables | +| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color | +| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/utf.d) | Encoding/decoding Unicode text | +| [file_manager.d](https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d) | Keep file contents in memory | +| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths | +| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/complex.d) | A complex number type | | File | Purpose | |---------------------------------------------------------------------------------|---------------------------------------------------------------| diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index 64ce79a7c45..fa5940ed249 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 cff4b9feb45..1fe8e809aa5 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 f8d2f45706a..48e5f4a8062 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 81e0d7e64be..e048cdc2e1b 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 66be73ea21f..e2b33194f06 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 ae8f65b6daa..0bf40ef9a72 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 1fd90051830..0ecd6351f7d 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 b99f690e156..c4f84b12968 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 97adc5ad8ac..b168b4f7e0a 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 d3006170be7..da66812b954 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 00000000000..a9b65c3dd8d --- /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 00000000000..b8cde375736 --- /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 similarity index 77% rename from gcc/d/dmd/root/outbuffer.d rename to gcc/d/dmd/common/outbuffer.d index e756917b88d..c5a84375aaa 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 { @@ -47,6 +54,14 @@ struct OutBuffer int level; // state } + /** + Construct given size. + */ + this(size_t initialSize) nothrow + { + reserve(initialSize); + } + /** Construct from filename. Will map the file into memory (or create it anew if necessary) and start writing at the beginning of it. @@ -56,14 +71,36 @@ struct OutBuffer */ @trusted this(const(char)* filename) { - fileMapping = new FileMapping!ubyte(filename); + FileMapping!ubyte model; + fileMapping = cast(FileMapping!ubyte*) malloc(model.sizeof); + memcpy(fileMapping, &model, model.sizeof); + fileMapping.__ctor(filename); + //fileMapping = new FileMapping!ubyte(filename); data = (*fileMapping)[]; } + /** + Frees resources associated. + */ + extern (C++) void dtor() nothrow @trusted + { + if (fileMapping) + { + if (fileMapping.active) + fileMapping.close(); + fileMapping = null; + } + else + { + debug (stomp) memset(data.ptr, 0xFF, data.length); + free(data.ptr); + } + } + /** Frees resources associated automatically. */ - extern (C++) ~this() pure nothrow + extern (C++) ~this() pure nothrow @trusted { if (fileMapping) { @@ -74,10 +111,23 @@ struct OutBuffer else { debug (stomp) memset(data.ptr, 0xFF, data.length); - mem.xfree(data.ptr); + pureFree(data.ptr); } } + /// For porting with ease from dmd.backend.outbuf.Outbuffer + ubyte* buf() nothrow { + return data.ptr; + } + + /// For porting with ease from dmd.backend.outbuf.Outbuffer + ubyte** bufptr() nothrow { + static struct Array { size_t length; ubyte* ptr; } + auto a = cast(Array*) &data; + assert(a.length == data.length && a.ptr == data.ptr); + return &a.ptr; + } + extern (C++) size_t length() const pure @nogc @safe nothrow { return offset; } /********************** @@ -109,7 +159,7 @@ struct OutBuffer else { debug (stomp) memset(data.ptr, 0xFF, data.length); - mem.xfree(extractData()); + pureFree(extractData()); } } @@ -141,17 +191,18 @@ struct OutBuffer { debug (stomp) { - auto p = cast(ubyte*)mem.xmalloc(size); + auto p = cast(ubyte*) pureMalloc(size); + p || assert(0, "OutBuffer: out of memory."); memcpy(p, data.ptr, offset); memset(data.ptr, 0xFF, data.length); // stomp old location - mem.xfree(data.ptr); + pureFree(data.ptr); memset(p + offset, 0xff, size - offset); // stomp unused data } else { - auto p = cast(ubyte*)mem.xrealloc(data.ptr, size); - if (mem.isGCEnabled) // clear currently unused data to avoid false pointers - memset(p + offset + nbytes, 0xff, size - offset - nbytes); + auto p = cast(ubyte*) pureRealloc(data.ptr, size); + p || assert(0, "OutBuffer: out of memory."); + memset(p + offset + nbytes, 0xff, size - offset - nbytes); } data = p[0 .. size]; } @@ -164,7 +215,7 @@ struct OutBuffer */ extern (C++) void setsize(size_t size) pure nothrow @nogc @safe { - assert(size <= offset); + assert(size <= data.length); offset = size; } @@ -185,6 +236,14 @@ struct OutBuffer notlinehead = true; } + // Write an array to the buffer, no reserve check + @trusted nothrow + void writen(const void *b, size_t len) + { + memcpy(data.ptr + offset, b, len); + offset += len; + } + extern (C++) void write(const(void)* data, size_t nbytes) pure nothrow { write(data[0 .. nbytes]); @@ -199,27 +258,90 @@ struct OutBuffer offset += buf.length; } - extern (C++) void writestring(const(char)* string) pure nothrow + /** + * Writes a 16 bit value, no reserve check. + */ + @trusted nothrow + void write16n(int v) { - write(string.toDString); + auto x = cast(ushort) v; + data[offset] = x & 0x00FF; + data[offset + 1] = x >> 8u; + offset += 2; } + /** + * Writes a 16 bit value. + */ + void write16(int v) nothrow + { + auto u = cast(ushort) v; + write(&u, u.sizeof); + } + + /** + * Writes a 32 bit int. + */ + void write32(int v) nothrow @trusted + { + write(&v, v.sizeof); + } + + /** + * Writes a 64 bit int. + */ + @trusted void write64(long v) nothrow + { + write(&v, v.sizeof); + } + + /// NOT zero-terminated + extern (C++) void writestring(const(char)* s) pure nothrow + { + if (!s) + return; + import core.stdc.string : strlen; + write(s[0 .. strlen(s)]); + } + + /// ditto void writestring(const(char)[] s) pure nothrow { write(s); } + /// ditto void writestring(string s) pure nothrow { write(s); } + /// NOT zero-terminated, followed by newline void writestringln(const(char)[] s) pure nothrow { writestring(s); writenl(); } + // Zero-terminated + void writeString(const(char)* s) pure nothrow @trusted + { + write(s[0 .. strlen(s)+1]); + } + + /// ditto + void writeString(const(char)[] s) pure nothrow + { + write(s); + writeByte(0); + } + + /// ditto + void writeString(string s) pure nothrow + { + writeString(cast(const(char)[])(s)); + } + extern (C++) void prependstring(const(char)* string) pure nothrow { size_t len = strlen(string); @@ -244,6 +366,38 @@ struct OutBuffer notlinehead = false; } + // Write n zeros; return pointer to start of zeros + @trusted + void *writezeros(size_t n) nothrow + { + reserve(n); + auto result = memset(data.ptr + offset, 0, n); + offset += n; + return result; + } + + // Position buffer to accept the specified number of bytes at offset + @trusted + void position(size_t where, size_t nbytes) nothrow + { + if (where + nbytes > data.length) + { + reserve(where + nbytes - offset); + } + offset = where; + + debug assert(offset + nbytes <= data.length); + } + + /** + * Writes an 8 bit byte, no reserve check. + */ + extern (C++) @trusted nothrow + void writeByten(int b) + { + this.data[offset++] = cast(ubyte) b; + } + extern (C++) void writeByte(uint b) pure nothrow { if (doindent && !notlinehead && b != '\n') @@ -369,14 +523,6 @@ struct OutBuffer } } - extern (C++) void write(RootObject obj) /*nothrow*/ - { - if (obj) - { - writestring(obj.toChars()); - } - } - extern (C++) void fill0(size_t nbytes) pure nothrow { reserve(nbytes); @@ -428,8 +574,8 @@ struct OutBuffer break; } offset += count; - if (mem.isGCEnabled) - memset(data.ptr + offset, 0xff, psize - count); + // if (mem.isGCEnabled) + memset(data.ptr + offset, 0xff, psize - count); } static if (__VERSION__ < 2092) @@ -460,7 +606,6 @@ struct OutBuffer */ extern (C++) void print(ulong u) pure nothrow { - //import core.internal.string; // not available UnsignedStringBuf buf = void; writestring(unsignedToTempString(u, buf)); } @@ -558,6 +703,11 @@ struct OutBuffer return extractData()[0 .. length]; } + extern (D) byte[] extractUbyteSlice(bool nullTerminate = false) pure nothrow + { + return cast(byte[]) extractSlice(nullTerminate); + } + // Append terminating null if necessary and get view of internal buffer extern (C++) char* peekChars() pure nothrow { @@ -577,6 +727,36 @@ struct OutBuffer return extractData(); } + void writesLEB128(int value) pure nothrow + { + while (1) + { + ubyte b = value & 0x7F; + + value >>= 7; // arithmetic right shift + if ((value == 0 && !(b & 0x40)) || + (value == -1 && (b & 0x40))) + { + writeByte(b); + break; + } + writeByte(b | 0x80); + } + } + + void writeuLEB128(uint value) pure nothrow + { + do + { + ubyte b = value & 0x7F; + + value >>= 7; + if (value) + b |= 0x80; + writeByte(b); + } while (value); + } + /** Destructively saves the contents of `this` to `filename`. As an optimization, if the file already has identical contents with the buffer, @@ -591,7 +771,6 @@ struct OutBuffer */ extern(D) bool moveToFile(const char* filename) { - import dmd.root.file; bool result = true; const bool identical = this[] == FileMapping!(const ubyte)(filename)[]; @@ -615,12 +794,12 @@ struct OutBuffer else { if (!identical) - File.write(filename, this[]); + writeFile(filename, this[]); destroy(); } return identical - ? result && File.touch(filename) + ? result && touchFile(filename) : result; } } @@ -645,7 +824,7 @@ char[] unsignedToTempString(ulong value, char[] buf, uint radix = 10) @safe pure else { ubyte x = cast(ubyte)(value % radix); - value = value / radix; + value /= radix; buf[--i] = cast(char)((x < 10) ? x + '0' : x - 10 + 'a'); } } while (value); diff --git a/gcc/d/dmd/root/outbuffer.h b/gcc/d/dmd/common/outbuffer.h similarity index 90% rename from gcc/d/dmd/root/outbuffer.h rename to gcc/d/dmd/common/outbuffer.h index b635373c183..a5e3f9c541d 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 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 00000000000..026374af89d --- /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 d4a8b136d43..05bd4bd550d 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 bb40649dc0b..7d8ab67ee6a 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 0381f9ad6a4..df742c0bd8f 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 22633a869d4..7f76d7565e0 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 4c70565e5c2..87c3adae9e6 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 b065251e652..34a236ba26e 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 0f40c1142c7..e3f135a03d8 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 1c56defd3ee..4a4c3530417 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 541fac7dfb4..a1f36c0c48f 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 ddfee2ccab0..357f7bdcd9f 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 71b8c7a6098..c417f93c353 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 768eaa05a08..4e0071304e4 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 9b4329b8b0a..f9b765c521c 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 638fc7e5295..42c0d18b2db 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 80ecd361135..0925e7cd38a 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 3a6dff2d44f..9aa435d6463 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 f43bc837992..02252fd13e5 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 eac20952eb5..047c1eb6f69 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 c3503bbf7d6..9d7957a975d 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 28054d02904..e19adfc4b14 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 0dff754bee5..49ee4b3b3ec 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 0d6fa1e88f3..2a623324454 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 dec3713b676..691364cb265 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 3b604af53b9..8e152d6a1fc 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 00000000000..05aeb7df5dc --- /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 similarity index 50% rename from gcc/d/dmd/root/root.h rename to gcc/d/dmd/file_manager.h index 667ce67fb7c..7488fab17fc 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 7f0b0bb9a28..2d6a756178e 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 9b65d024b97..747a1138905 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 6e794748bee..2275ec517e5 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* versionids; // command line versions and predefined versions Array* 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 debb9ca62d4..4018126cb26 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 8c31590a311..4ff07b5de32 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 e61fb23eb5d..3ff0e894e2a 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 1f04dcfbc11..1ee51533b53 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 43a1435cd10..8add74a1427 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 00000000000..0dad1a83a8b --- /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 45e101b903a..d036ee1635b 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 ae8bde2ff57..5828486c6fe 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 fd6153205c9..00000000000 diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index bfd31bc1d13..fef515071f9 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 d29bdc13806..44a6c066507 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 afffc2dcf30..e2b4199b80a 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 b36e7f7bdaf..00000000000 diff --git a/gcc/d/dmd/macro.h b/gcc/d/dmd/macro.h deleted file mode 100644 index 80ec36e00e6..00000000000 diff --git a/gcc/d/dmd/mars.h b/gcc/d/dmd/mars.h deleted file mode 100644 index 9b9c278c8a7..00000000000 diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index 969290c476c..fe4c73a021c 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 80e47918f2d..a21924b3aac 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 cdf221f55f7..430b39b205c 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 7719ccfe297..605e9f3bfdd 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 85e371e6e63..eb4ba1db20d 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 4ef55f3acc9..ff03a6e0d3c 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 3ae30619a20..9f116fe8509 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 21042dd80ea..f00ceb6cc93 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 a2ad47882ef..00000000000 diff --git a/gcc/d/dmd/printast.d b/gcc/d/dmd/printast.d index 3f12b173357..414d6f665f5 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 539b940d6fb..e062d93da46 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 c65b674a7f5..00000000000 diff --git a/gcc/d/dmd/root/checkedint.h b/gcc/d/dmd/root/checkedint.h deleted file mode 100644 index 8a7d9c90d9f..00000000000 diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d index ef6056cfed0..64e95716322 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 1e4ccb5d344..d1500c8953b 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: @@ -1222,33 +1158,6 @@ version(Windows) return newBuffer[0 .. length]; } - /********************************** - * Converts a narrow string to a (null-terminated) UTF-16 string. - * Returns: - * If `buffer` is specified and the result fits, a slice of that buffer, - * otherwise a new buffer which can be released via `mem.xfree()`. - * Nulls are propagated, i.e., if `narrow` is null, the returned slice is - * null too. - */ - wchar[] toWStringz(const(char)[] narrow, wchar[] buffer = null) nothrow - { - if (narrow is null) - return null; - - const requiredLength = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, buffer.ptr, cast(int) buffer.length); - if (requiredLength < buffer.length) - { - buffer[requiredLength] = 0; - return buffer[0 .. requiredLength]; - } - - wchar* newBuffer = cast(wchar*) mem.xmalloc_noscan((requiredLength + 1) * wchar.sizeof); - const length = MultiByteToWideChar(CodePage, 0, narrow.ptr, cast(int) narrow.length, newBuffer, requiredLength); - assert(length == requiredLength); - newBuffer[length] = 0; - return newBuffer[0 .. length]; - } - /********************************** * Converts a slice of UTF-8 characters to an array of wchar that's null * terminated so it can be passed to Win32 APIs then calls the supplied @@ -1262,9 +1171,12 @@ version(Windows) */ private auto toWStringzThen(alias F)(const(char)[] str) nothrow { + import dmd.common.string : SmallBuffer, toWStringz; + if (!str.length) return F(""w.ptr); - wchar[1024] buf = void; + wchar[1024] support = void; + auto buf = SmallBuffer!wchar(support.length, support); wchar[] wide = toWStringz(str, buf); scope(exit) wide.ptr != buf.ptr && mem.xfree(wide.ptr); diff --git a/gcc/d/dmd/root/hash.h b/gcc/d/dmd/root/hash.h deleted file mode 100644 index 6a322006213..00000000000 diff --git a/gcc/d/dmd/root/rootobject.d b/gcc/d/dmd/root/rootobject.d index 854ec1a65bc..64104b823d1 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 bd53fc452c9..00000000000 diff --git a/gcc/d/dmd/root/stringtable.h b/gcc/d/dmd/root/stringtable.h deleted file mode 100644 index 51304d32c3f..00000000000 diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index 35734b2caa5..89049599dac 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 7b2fa5e93cd..993db905523 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 ac2b239efaf..3852d0b2693 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 b49c9035d3f..91e3fe7bfeb 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 7825762db9e..98b7bd318eb 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 28a930a28bc..00000000000 diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index f067c91b316..9eba2ffaed2 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 ?` + // 2. Potentially found a returning branch, update accordingly + if (!tf.next || tf.next.toBasetype().isTypeNoreturn()) + { + tf.next = resType; // infer void or noreturn + } + // Found an actual return value before + else if (tf.next.ty != Tvoid && !resType.toBasetype().isTypeNoreturn()) { if (tf.next.ty != Terror) { @@ -3421,20 +3410,23 @@ private extern (C++) final class StatementSemanticVisitor : Visitor errors = true; tf.next = Type.terror; } - else - tf.next = Type.tvoid; - tret = tf.next; + tret = tf.next; tbret = tret.toBasetype(); } if (inferRef) // deduce 'auto ref' tf.isref = false; - if (tbret.ty != Tvoid) // if non-void return + if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return { if (tbret.ty != Terror) - rs.error("`return` expression expected"); + { + if (e0) + rs.error("expected return type of `%s`, not `%s`", tret.toChars(), resType.toChars()); + else + rs.error("`return` expression expected"); + } errors = true; } else if (fd.isMain()) @@ -3522,7 +3514,12 @@ private extern (C++) final class StatementSemanticVisitor : Visitor } else { - result = new CompoundStatement(rs.loc, new ExpStatement(rs.loc, e0), rs); + auto es = new ExpStatement(rs.loc, e0); + if (e0.type.isTypeNoreturn()) + result = es; // Omit unreachable return; + else + result = new CompoundStatement(rs.loc, es, rs); + return; } } @@ -4014,7 +4011,7 @@ private extern (C++) final class StatementSemanticVisitor : Visitor /* If catch exception type is derived from Exception */ if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && - (!c.handler || !c.handler.comeFrom())) + (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_)) { // Remove c from the array of catches tcs.catches.remove(i); @@ -4569,45 +4566,14 @@ Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out St } -/******************* - * Determines additional argument types for makeTupleForeach. - */ -static template TupleForeachArgs(bool isStatic, bool isDecl) -{ - alias Seq(T...)=T; - static if(isStatic) alias T = Seq!(bool); - else alias T = Seq!(); - static if(!isDecl) alias TupleForeachArgs = T; - else alias TupleForeachArgs = Seq!(Dsymbols*,T); -} - -/******************* - * Determines the return type of makeTupleForeach. - */ -static template TupleForeachRet(bool isStatic, bool isDecl) -{ - alias Seq(T...)=T; - static if(!isDecl) alias TupleForeachRet = Statement; - else alias TupleForeachRet = Dsymbols*; -} - - /******************* * See StatementSemanticVisitor.makeTupleForeach. This is a simple * wrapper that returns the generated statements/declarations. */ -TupleForeachRet!(isStatic, isDecl) makeTupleForeach(bool isStatic, bool isDecl)(Scope* sc, ForeachStatement fs, TupleForeachArgs!(isStatic, isDecl) args) +auto makeTupleForeach(bool isStatic, bool isDecl)(Scope* sc, ForeachStatement fs, Dsymbols* dbody, bool needExpansion) { scope v = new StatementSemanticVisitor(sc); - static if(!isDecl) - { - v.makeTupleForeach!(isStatic, isDecl)(fs, args); - return v.result; - } - else - { - return v.makeTupleForeach!(isStatic, isDecl)(fs, args); - } + return v.makeTupleForeach!(isStatic, isDecl)(fs, dbody, needExpansion); } /********************************* @@ -4731,7 +4697,7 @@ private Statements* flatten(Statement statement, Scope* sc) sfs.sfe.prepare(sc); if (sfs.sfe.ready()) { - auto s = makeTupleForeach!(true, false)(sc, sfs.sfe.aggrfe, sfs.sfe.needExpansion); + Statement s = makeTupleForeach!(true, false)(sc, sfs.sfe.aggrfe, null, sfs.sfe.needExpansion).statement; auto result = s.flatten(sc); if (result) { diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d index 2f27414a56c..d1578ec3a12 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 d5b3de2c522..16739addf57 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 83281a6358f..6a75ccc5ebd 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 08ce9acef99..69cc84f6573 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 7680fb8500b..1ea51a89bff 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 0fd6634f2ce..d14d0aab593 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 8f968ed0dc2..cc1d2e351e6 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 ace4e423bcc..f75ae0e0b5a 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 d8160f0d633..d05af61ce6f 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 b3782afe78b..00000000000 diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index 600521fbd49..6cd5d20f6f7 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 31680564bdd..2831eefc4ba 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 c98eb1f45d9..d37d205e870 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 f7f90fb08c3..d0a5e2f6859 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 06eb5ae3424..cb5c4a7b1e7 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 db500ee2efe..b39b92eb822 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);