From patchwork Sat Apr 3 23:28:28 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Iain Buclaw X-Patchwork-Id: 1462053 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org 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@gcc.gnu.org; receiver=) Authentication-Results: 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=cbiH/yfu; dkim-atps=neutral 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 ozlabs.org (Postfix) with ESMTPS id 4FCY631bV6z9sVb for ; Sun, 4 Apr 2021 09:29:13 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 56DCA3851C0B; Sat, 3 Apr 2021 23:29:10 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 56DCA3851C0B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1617492550; bh=0dJXbgGhe8dQ3QEhJMIhtoLMsj0CZFSsBQSII6e4kjQ=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=cbiH/yfuEluGLuksiXcmyeJ9c1AYmBgwIVgD9p4v2GZ3ag0dRrTezh8R1p+vPEqyi saDlMpXMgOGR2+tma2Hdbc3Hna3l+c/8/eJUi8ornJ56Xl0TVx0FT/r020qqgQ8bTB sGXmDkm22kaL2lievZ4gDAvzS4bbTfn50Ijp/7jc= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-202.mailbox.org (mout-p-202.mailbox.org [IPv6:2001:67c:2050::465:202]) by sourceware.org (Postfix) with ESMTPS id A3E96385781B for ; Sat, 3 Apr 2021 23:28:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org A3E96385781B Received: from smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:105:465:1:2:0]) (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-202.mailbox.org (Postfix) with ESMTPS id 4FCY5T6tbBzQjmd; Sun, 4 Apr 2021 01:28:45 +0200 (CEST) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp2.mailbox.org ([80.241.60.241]) by spamfilter04.heinlein-hosting.de (spamfilter04.heinlein-hosting.de [80.241.56.122]) (amavisd-new, port 10030) with ESMTP id 05l1bflf3Fx6; Sun, 4 Apr 2021 01:28:34 +0200 (CEST) To: gcc-patches@gcc.gnu.org Subject: [committed 1/3] d: Merge upstream dmd 3b808e838 Date: Sun, 4 Apr 2021 01:28:28 +0200 Message-Id: <20210403232828.1822347-1-ibuclaw@gdcproject.org> MIME-Version: 1.0 X-MBO-SPAM-Probability: X-Rspamd-Score: -1.06 / 15.00 / 15.00 X-Rspamd-Queue-Id: 977FF16F2 X-Rspamd-UID: 256525 X-Spam-Status: No, score=-15.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_NUMSUBJECT, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) 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@gcc.gnu.org Sender: "Gcc-patches" Hi, This patch series merges the D front-end implementation with upstream dmd 3b808e838, and the Phobos standard library with druntime 483bc129 and phobos f89dc217a. These changes are specific to the front-end implementation part. D front-end changes: - Explicit package visibility attribute is now always applied to introducing scopes. - Added `__traits(totype, string)' to convert mangled type string to an existing type. - Printf-like and scanf-like functions are now detected by prefixing them with `pragma(printf)' for printf-like functions or `pragma(scanf)' for scanf-like functions. - Added `__c_wchar_t', `__c_complex_float', `__c_complex_double', and `__c_complex_real' types for interfacing with C and C++. - Template alias parameters can now be instantiated with basic types, such as `int` or `void function()`. - Mixins can now be used as types in the form `mixin(string) var'. - Mixin expressions can take an argument list, same as `pragma(msg)'. - Implement DIP1034, add `typeof(*null)' types to represent `noreturn'. - `pragma(msg)' can print expressions of type `void'. - It is now an error to use private variables selectively imported from other modules. Due to a bug, some imported private members were visible from other modules, violating the specification. - Added new syntax to declare an alias to a function type using the `alias' syntax based on the assignment operator. - Function literals can now return a value by reference. 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 3b808e838. * Make-lang.in (D_FRONTEND_OBJS): Add d/chkformat.o. * d-codegen.cc (build_struct_literal): Handle special enums. * d-convert.cc (convert_expr): Handle noreturn type. (convert_for_condition): Likewise. * d-target.cc (Target::_init): Set type for wchar_t. (TargetCPP::derivedClassOffset): New method. (Target::libraryObjectMonitors): New method. * decl.cc (get_symbol_decl): Set TREE_THIS_VOLATILE for functions of type noreturn. * toir.cc (IRVisitor::visit (ReturnStatement *)): Handle returning noreturn types. * types.cc (TypeVisitor::visit (TypeNoreturn *)): New method. (TypeVisitor::visit (TypeEnum *)): Handle special enums. --- gcc/d/Make-lang.in | 1 + gcc/d/d-codegen.cc | 8 + gcc/d/d-convert.cc | 14 +- gcc/d/d-target.cc | 28 + gcc/d/decl.cc | 4 + gcc/d/dmd/MERGE | 2 +- gcc/d/dmd/attrib.c | 39 +- gcc/d/dmd/attrib.h | 4 +- gcc/d/dmd/blockexit.c | 2 + gcc/d/dmd/chkformat.c | 975 +++++++++++++++ gcc/d/dmd/cppmangle.c | 18 +- gcc/d/dmd/ctfeexpr.c | 2 +- gcc/d/dmd/dcast.c | 5 +- gcc/d/dmd/dclass.c | 20 +- gcc/d/dmd/declaration.h | 4 +- gcc/d/dmd/denum.c | 19 +- gcc/d/dmd/dimport.c | 80 +- gcc/d/dmd/dmangle.c | 37 +- gcc/d/dmd/dmodule.c | 33 +- gcc/d/dmd/dscope.c | 6 +- gcc/d/dmd/dsymbol.c | 32 +- gcc/d/dmd/dsymbol.h | 3 +- gcc/d/dmd/dsymbolsem.c | 139 ++- gcc/d/dmd/dtemplate.c | 210 ++-- gcc/d/dmd/expression.c | 2218 ++++------------------------------ gcc/d/dmd/expression.h | 9 +- gcc/d/dmd/expressionsem.c | 2091 ++++++++++++++++++++++++++++++-- gcc/d/dmd/func.c | 18 +- gcc/d/dmd/hdrgen.c | 57 +- gcc/d/dmd/hdrgen.h | 1 + gcc/d/dmd/idgen.c | 8 +- gcc/d/dmd/import.h | 1 + gcc/d/dmd/module.h | 1 + gcc/d/dmd/mtype.c | 282 ++++- gcc/d/dmd/mtype.h | 38 +- gcc/d/dmd/parse.c | 217 +++- gcc/d/dmd/parse.h | 1 + gcc/d/dmd/scope.h | 4 + gcc/d/dmd/semantic2.c | 20 + gcc/d/dmd/semantic3.c | 34 +- gcc/d/dmd/statement.c | 36 +- gcc/d/dmd/statement.h | 3 +- gcc/d/dmd/statementsem.c | 2 +- gcc/d/dmd/target.h | 4 + gcc/d/dmd/template.h | 1 + gcc/d/dmd/templateparamsem.c | 2 +- gcc/d/dmd/traits.c | 103 +- gcc/d/dmd/typesem.c | 77 +- gcc/d/dmd/visitor.h | 4 + gcc/d/toir.cc | 7 + gcc/d/types.cc | 47 +- 51 files changed, 4517 insertions(+), 2454 deletions(-) create mode 100644 gcc/d/dmd/chkformat.c diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 75857d81ec7..b3c77a08f46 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -64,6 +64,7 @@ D_FRONTEND_OBJS = \ d/blockexit.o \ d/canthrow.o \ d/checkedint.o \ + d/chkformat.o \ d/clone.o \ d/cond.o \ d/constfold.o \ diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index 5e6f240cb90..608abcd94f5 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -1153,6 +1153,14 @@ build_struct_literal (tree type, vec *init) if (vec_safe_is_empty (init)) return build_constructor (type, NULL); + /* Struct literals can be seen for special enums representing `_Complex', + make sure to reinterpret the literal as the correct type. */ + if (COMPLEX_FLOAT_TYPE_P (type)) + { + gcc_assert (vec_safe_length (init) == 2); + return build_complex (type, (*init)[0].value, (*init)[1].value); + } + vec *ve = NULL; HOST_WIDE_INT offset = 0; bool constant_p = true; diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc index f407a46da6e..3073edaae9f 100644 --- a/gcc/d/d-convert.cc +++ b/gcc/d/d-convert.cc @@ -559,7 +559,9 @@ convert_expr (tree exp, Type *etype, Type *totype) break; case Tnull: - /* Casting from typeof(null) is represented as all zeros. */ + case Tnoreturn: + /* Casting from `typeof(null)' for `null' expressions, or `typeof(*null)' + for `noreturn' expressions is represented as all zeros. */ result = build_typeof_null_value (totype); /* Make sure the expression is still evaluated if necessary. */ @@ -742,6 +744,16 @@ convert_for_condition (tree expr, Type *type) break; } + case Tnoreturn: + /* Front-end allows conditionals that never return, represent the + conditional result value as all zeros. */ + result = build_zero_cst (d_bool_type); + + /* Make sure the expression is still evaluated if necessary. */ + if (TREE_SIDE_EFFECTS (expr)) + result = compound_expr (expr, result); + break; + default: result = expr; break; diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc index d50fcef22e2..a1dc2ee286f 100644 --- a/gcc/d/d-target.cc +++ b/gcc/d/d-target.cc @@ -164,6 +164,15 @@ Target::_init (const Param &) this->c.longsize = int_size_in_bytes (long_integer_type_node); this->c.long_doublesize = int_size_in_bytes (long_double_type_node); + /* Define what type to use for wchar_t. We don't want to support wide + characters less than "short" in D. */ + if (WCHAR_TYPE_SIZE == 32) + this->c.twchar_t = Type::basic[Tdchar]; + else if (WCHAR_TYPE_SIZE == 16) + this->c.twchar_t = Type::basic[Twchar]; + else + sorry ("D does not support wide characters on this target."); + /* Set-up target C++ ABI. */ this->cpp.reverseOverloads = false; this->cpp.exceptions = true; @@ -417,6 +426,15 @@ TargetCPP::fundamentalType (const Type *, bool &) return false; } +/* Get the starting offset position for fields of an `extern(C++)` class + that is derived from the given BASE_CLASS. */ + +unsigned +TargetCPP::derivedClassOffset(ClassDeclaration *base_class) +{ + return base_class->structsize; +} + /* Return the default system linkage for the target. */ LINK @@ -517,3 +535,13 @@ Target::getTargetInfo (const char *key, const Loc &loc) return NULL; } + +/** + * Returns true if the implementation for object monitors is always defined + * in the D runtime library (rt/monitor_.d). */ + +bool +Target::libraryObjectMonitors (FuncDeclaration *, Statement *) +{ + return true; +} diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 042abbfc48a..0ec19345272 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -1277,6 +1277,10 @@ get_symbol_decl (Declaration *decl) if (decl->storage_class & STCfinal) DECL_FINAL_P (decl->csym) = 1; + /* Function is of type `noreturn' or `typeof(*null)'. */ + if (fd->type->nextOf ()->ty == Tnoreturn) + TREE_THIS_VOLATILE (decl->csym) = 1; + /* Check whether this function is expanded by the frontend. */ DECL_INTRINSIC_CODE (decl->csym) = INTRINSIC_NONE; maybe_set_intrinsic (fd); diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 78b454c1c64..86475c80d35 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -a3c9bf422e7ff54d45846b8c577ee82da4234db1 +3b808e838bb00f527eb4ed5281cd985756237b8f 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/attrib.c b/gcc/d/dmd/attrib.c index 56b8ce816be..a808b8a6c3f 100644 --- a/gcc/d/dmd/attrib.c +++ b/gcc/d/dmd/attrib.c @@ -80,8 +80,8 @@ Scope *AttribDeclaration::createNewScope(Scope *sc, if (stc != sc->stc || linkage != sc->linkage || cppmangle != sc->cppmangle || - !protection.isSubsetOf(sc->protection) || explicitProtection != sc->explicitProtection || + !(protection == sc->protection) || aligndecl != sc->aligndecl || inlining != sc->inlining) { @@ -552,10 +552,21 @@ void ProtDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) if (protection.kind == Prot::package_ && protection.pkg && sc->_module) { Module *m = sc->_module; - Package* pkg = m->parent ? m->parent->isPackage() : NULL; - if (!pkg || !protection.pkg->isAncestorPackageOf(pkg)) - error("does not bind to one of ancestor packages of module `%s`", - m->toPrettyChars(true)); + + // While isAncestorPackageOf does an equality check, the fix for issue 17441 adds a check to see if + // each package's .isModule() properites are equal. + // + // Properties generated from `package(foo)` i.e. protection.pkg have .isModule() == null. + // This breaks package declarations of the package in question if they are declared in + // the same package.d file, which _do_ have a module associated with them, and hence a non-null + // isModule() + if (!m->isPackage() || !protection.pkg->ident->equals(m->isPackage()->ident)) + { + Package* pkg = m->parent ? m->parent->isPackage() : NULL; + if (!pkg || !protection.pkg->isAncestorPackageOf(pkg)) + error("does not bind to one of ancestor packages of module `%s`", + m->toPrettyChars(true)); + } } return AttribDeclaration::addMember(sc, sds); @@ -795,6 +806,18 @@ Scope *PragmaDeclaration::newScope(Scope *sc) sc->protection, sc->explicitProtection, sc->aligndecl, inlining); } + if (ident == Id::printf || ident == Id::scanf) + { + Scope *sc2 = sc->push(); + + if (ident == Id::printf) + // Override previous setting, never let both be set + sc2->flags = (sc2->flags & ~SCOPEscanf) | SCOPEprintf; + else + sc2->flags = (sc2->flags & ~SCOPEprintf) | SCOPEscanf; + + return sc2; + } return sc; } @@ -1164,12 +1187,12 @@ void ForwardingAttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds) // These are mixin declarations, like mixin("int x"); -CompileDeclaration::CompileDeclaration(Loc loc, Expression *exp) +CompileDeclaration::CompileDeclaration(Loc loc, Expressions *exps) : AttribDeclaration(NULL) { //printf("CompileDeclaration(loc = %d)\n", loc.linnum); this->loc = loc; - this->exp = exp; + this->exps = exps; this->scopesym = NULL; this->compiled = false; } @@ -1177,7 +1200,7 @@ CompileDeclaration::CompileDeclaration(Loc loc, Expression *exp) Dsymbol *CompileDeclaration::syntaxCopy(Dsymbol *) { //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); - return new CompileDeclaration(loc, exp->syntaxCopy()); + return new CompileDeclaration(loc, Expression::arraySyntaxCopy(exps)); } void CompileDeclaration::addMember(Scope *, ScopeDsymbol *sds) diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h index 74364176128..174d3c1ad5b 100644 --- a/gcc/d/dmd/attrib.h +++ b/gcc/d/dmd/attrib.h @@ -234,12 +234,12 @@ public: class CompileDeclaration : public AttribDeclaration { public: - Expression *exp; + Expressions *exps; ScopeDsymbol *scopesym; bool compiled; - CompileDeclaration(Loc loc, Expression *exp); + CompileDeclaration(Loc loc, Expressions *exps); Dsymbol *syntaxCopy(Dsymbol *s); void addMember(Scope *sc, ScopeDsymbol *sds); void setScope(Scope *sc); diff --git a/gcc/d/dmd/blockexit.c b/gcc/d/dmd/blockexit.c index 44e3cc13bd3..1895d36fb1e 100644 --- a/gcc/d/dmd/blockexit.c +++ b/gcc/d/dmd/blockexit.c @@ -62,6 +62,8 @@ int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow) return; } } + if (s->exp->type->toBasetype()->isTypeNoreturn()) + result = BEhalt; if (canThrow(s->exp, func, mustNotThrow)) result |= BEthrow; } diff --git a/gcc/d/dmd/chkformat.c b/gcc/d/dmd/chkformat.c new file mode 100644 index 00000000000..d00b658ca00 --- /dev/null +++ b/gcc/d/dmd/chkformat.c @@ -0,0 +1,975 @@ + +/* Compiler implementation of the D programming language + * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + */ + +// Check the arguments to `printf` and `scanf` against the `format` string. + +#include "root/dsystem.h" +#include "root/dcompat.h" + +#include "arraytypes.h" +#include "cond.h" +#include "errors.h" +#include "expression.h" +#include "globals.h" +#include "identifier.h" +#include "mtype.h" +#include "target.h" + + +/* Different kinds of formatting specifications, variations we don't + care about are merged. (Like we don't care about the difference between + f, e, g, a, etc.) + + For `scanf`, every format is a pointer. + */ +enum Format +{ + Format_d, // int + Format_hhd, // signed char + Format_hd, // short int + Format_ld, // long int + Format_lld, // long long int + Format_jd, // intmax_t + Format_zd, // size_t + Format_td, // ptrdiff_t + Format_u, // unsigned int + Format_hhu, // unsigned char + Format_hu, // unsigned short int + Format_lu, // unsigned long int + Format_llu, // unsigned long long int + Format_ju, // uintmax_t + Format_g, // float (scanf) / double (printf) + Format_lg, // double (scanf) + Format_Lg, // long double (both) + Format_s, // char string (both) + Format_ls, // wchar_t string (both) + Format_c, // char (printf) + Format_lc, // wint_t (printf) + Format_p, // pointer + Format_n, // pointer to int + Format_hhn, // pointer to signed char + Format_hn, // pointer to short + Format_ln, // pointer to long int + Format_lln, // pointer to long long int + Format_jn, // pointer to intmax_t + Format_zn, // pointer to size_t + Format_tn, // pointer to ptrdiff_t + Format_GNU_a, // GNU ext. : address to a string with no maximum size (scanf) + Format_GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) / length modifier (scanf) + Format_percent, // %% (i.e. no argument) + Format_error, // invalid format specification +}; + +/************************************** + * Parse the *length specifier* and the *specifier* of the following form: + * `[length]specifier` + * + * Params: + * format = format string + * idx = index of of start of format specifier, + * which gets updated to index past the end of it, + * even if `Format_error` is returned + * genSpecifier = Generic specifier. For instance, it will be set to `d` if the + * format is `hdd`. + * Returns: + * Format + */ +static Format parseGenericFormatSpecifier(const char *format, + size_t &idx, char &genSpecifier, bool useGNUExts = + findCondition(global.versionids, Identifier::idPool("CRuntime_Glibc"))) +{ + genSpecifier = 0; + + const size_t length = strlen(format); + + /* Read the `length modifier` + */ + const char lm = format[idx]; + bool lm1= false; // if jztL + bool lm2= false; // if `hh` or `ll` + if (lm == 'j' || + lm == 'z' || + lm == 't' || + lm == 'L') + { + ++idx; + if (idx == length) + return Format_error; + lm1 = true; + } + else if (lm == 'h' || lm == 'l') + { + ++idx; + if (idx == length) + return Format_error; + lm2 = lm == format[idx]; + if (lm2) + { + ++idx; + if (idx == length) + return Format_error; + } + } + + /* Read the `specifier` + */ + Format specifier; + const char sc = format[idx]; + genSpecifier = sc; + switch (sc) + { + case 'd': + case 'i': + if (lm == 'L') + specifier = Format_error; + else + specifier = lm == 'h' && lm2 ? Format_hhd : + lm == 'h' ? Format_hd : + lm == 'l' && lm2 ? Format_lld : + lm == 'l' ? Format_ld : + lm == 'j' ? Format_jd : + lm == 'z' ? Format_zd : + lm == 't' ? Format_td : + Format_d; + break; + + case 'u': + case 'o': + case 'x': + case 'X': + if (lm == 'L') + specifier = Format_error; + else + specifier = lm == 'h' && lm2 ? Format_hhu : + lm == 'h' ? Format_hu : + lm == 'l' && lm2 ? Format_llu : + lm == 'l' ? Format_lu : + lm == 'j' ? Format_ju : + lm == 'z' ? Format_zd : + lm == 't' ? Format_td : + Format_u; + break; + + case 'a': + if (useGNUExts) + { + // https://www.gnu.org/software/libc/manual/html_node/Dynamic-String-Input.html + specifier = Format_GNU_a; + break; + } + /* fall through */ + + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'A': + if (lm == 'L') + specifier = Format_Lg; + else if (lm1 || lm2 || lm == 'h') + specifier = Format_error; + else + specifier = lm == 'l' ? Format_lg : Format_g; + break; + + case 'c': + if (lm1 || lm2 || lm == 'h') + specifier = Format_error; + else + specifier = lm == 'l' ? Format_lc : Format_c; + break; + + case 's': + if (lm1 || lm2 || lm == 'h') + specifier = Format_error; + else + specifier = lm == 'l' ? Format_ls : Format_s; + break; + + case 'p': + if (lm1 || lm2 || lm == 'h' || lm == 'l') + specifier = Format_error; + else + specifier = Format_p; + break; + + case 'n': + if (lm == 'L') + specifier = Format_error; + else + specifier = lm == 'l' && lm2 ? Format_lln : + lm == 'l' ? Format_ln : + lm == 'h' && lm2 ? Format_hhn : + lm == 'h' ? Format_hn : + lm == 'j' ? Format_jn : + lm == 'z' ? Format_zn : + lm == 't' ? Format_tn : + Format_n; + break; + + case 'm': + if (useGNUExts) + { + // http://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html + specifier = Format_GNU_m; + break; + } + goto Ldefault; + + default: + Ldefault: + specifier = Format_error; + break; + } + + ++idx; + return specifier; // success +} + +Format formatError(size_t &idx, size_t i) +{ + idx = i; + return Format_error; +} + +/************************************** + * Parse the *format specifier* which is of the form: + * + * `%[*][width][length]specifier` + * + * Params: + * format = format string + * idx = index of `%` of start of format specifier, + * which gets updated to index past the end of it, + * even if `Format_error` is returned + * asterisk = set if there is a `*` sub-specifier + * Returns: + * Format + */ +static Format parseScanfFormatSpecifier(const char *format, size_t &idx, + bool &asterisk) +{ + asterisk = false; + + size_t i = idx; + assert(format[i] == '%'); + const size_t length = strlen(format); + + ++i; + if (i == length) + return formatError(idx, i); + + if (format[i] == '%') + { + idx = i + 1; + return Format_percent; + } + + // * sub-specifier + if (format[i] == '*') + { + ++i; + if (i == length) + return formatError(idx, i); + asterisk = true; + } + + // fieldWidth + while (isdigit(format[i])) + { + i++; + if (i == length) + return formatError(idx, i); + } + + /* Read the scanset + * A scanset can be anything, so we just check that it is paired + */ + if (format[i] == '[') + { + while (i < length) + { + if (format[i] == ']') + break; + ++i; + } + + // no `]` found + if (i == length) + return formatError(idx, i); + + ++i; + // no specifier after `]` + // it could be mixed with the one above, but then idx won't have the right index + if (i == length) + return formatError(idx, i); + } + + /* Read the specifier + */ + char genSpec; + Format specifier = parseGenericFormatSpecifier(format, i, genSpec); + if (specifier == Format_error) + return formatError(idx, i); + + idx = i; + return specifier; // success +} + +/************************************** + * Parse the *format specifier* which is of the form: + * + * `%[flags][field width][.precision][length modifier]specifier` + * + * Params: + * format = format string + * idx = index of `%` of start of format specifier, + * which gets updated to index past the end of it, + * even if `Format_error` is returned + * widthStar = set if * for width + * precisionStar = set if * for precision + * Returns: + * Format + */ +static Format parsePrintfFormatSpecifier(const char *format, size_t &idx, + bool &widthStar, bool &precisionStar) +{ + widthStar = false; + precisionStar = false; + + size_t i = idx; + assert(format[i] == '%'); + const size_t format_length = strlen(format); + const size_t length = format_length; + bool hash = false; + bool zero = false; + bool flags = false; + bool width = false; + bool precision = false; + + ++i; + if (i == length) + return formatError(idx, i); + + if (format[i] == '%') + { + idx = i + 1; + return Format_percent; + } + + /* Read the `flags` + */ + while (1) + { + const char c = format[i]; + if (c == '-' || + c == '+' || + c == ' ') + { + flags = true; + } + else if (c == '#') + { + hash = true; + } + else if (c == '0') + { + zero = true; + } + else + break; + ++i; + if (i == length) + return formatError(idx, i); + } + + /* Read the `field width` + */ + { + const char c = format[i]; + if (c == '*') + { + width = true; + widthStar = true; + ++i; + if (i == length) + return formatError(idx, i); + } + else if ('1' <= c && c <= '9') + { + width = true; + ++i; + if (i == length) + return formatError(idx, i); + while ('0' <= format[i] && format[i] <= '9') + { + ++i; + if (i == length) + return formatError(idx, i); + } + } + } + + /* Read the `precision` + */ + if (format[i] == '.') + { + precision = true; + ++i; + if (i == length) + return formatError(idx, i); + const char c = format[i]; + if (c == '*') + { + precisionStar = true; + ++i; + if (i == length) + return formatError(idx, i); + } + else if ('0' <= c && c <= '9') + { + ++i; + if (i == length) + return formatError(idx, i); + while ('0' <= format[i] && format[i] <= '9') + { + ++i; + if (i == length) + return formatError(idx, i); + } + } + } + + /* Read the specifier + */ + char genSpec; + Format specifier = parseGenericFormatSpecifier(format, i, genSpec); + if (specifier == Format_error) + return formatError(idx, i); + + switch (genSpec) + { + case 'c': + case 's': + if (hash || zero) + return formatError(idx, i); + break; + + case 'd': + case 'i': + if (hash) + return formatError(idx, i); + break; + + case 'n': + if (hash || zero || precision || width || flags) + return formatError(idx, i); + break; + + default: + break; + } + + idx = i; + return specifier; // success +} + +/*******************************************/ + +static Expression *getNextPrintfArg(const Loc &loc, Expressions &args, size_t &n, + size_t gnu_m_count, bool &skip) +{ + if (n == args.length) + { + if (args.length < (n + 1) - gnu_m_count) + deprecation(loc, "more format specifiers than %d arguments", (int)n); + else + skip = true; + return NULL; + } + return args[n++]; +} + +static void errorPrintfFormat(const char *prefix, DString &slice, Expression *arg, + const char *texpect, Type *tactual) +{ + deprecation(arg->loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`", + prefix ? prefix : "", arg->toChars(), (int)slice.length, slice.ptr, texpect, tactual->toChars()); +} + +/****************************************** + * Check that arguments to a printf format string are compatible + * with that string. Issue errors for incompatibilities. + * + * Follows the C99 specification for printf. + * + * Takes a generous, rather than strict, view of compatiblity. + * For example, an unsigned value can be formatted with a signed specifier. + * + * Diagnosed incompatibilities are: + * + * 1. incompatible sizes which will cause argument misalignment + * 2. deferencing arguments that are not pointers + * 3. insufficient number of arguments + * 4. struct arguments + * 5. array and slice arguments + * 6. non-pointer arguments to `s` specifier + * 7. non-standard formats + * 8. undefined behavior per C99 + * + * Per the C Standard, extra arguments are ignored. + * + * No attempt is made to fix the arguments or the format string. + * + * Params: + * loc = location for error messages + * format = format string + * args = arguments to match with format string + * isVa_list = if a "v" function (format check only) + * + * Returns: + * `true` if errors occurred + * References: + * C99 7.19.6.1 + * http://www.cplusplus.com/reference/cstdio/printf/ + */ +bool checkPrintfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list) +{ + //printf("checkPrintFormat('%s')\n", format); + size_t n = 0; // index in args + size_t gnu_m_count = 0; // number of Format_GNU_m + const size_t format_length = strlen(format); + for (size_t i = 0; i < format_length;) + { + if (format[i] != '%') + { + ++i; + continue; + } + bool widthStar = false; + bool precisionStar = false; + size_t j = i; + const Format fmt = parsePrintfFormatSpecifier(format, j, widthStar, precisionStar); + DString slice = DString(j - i, format + i); + i = j; + + if (fmt == Format_percent) + continue; // "%%", no arguments + + if (isVa_list) + { + // format check only + if (fmt == Format_error) + deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr); + continue; + } + + if (fmt == Format_GNU_m) + ++gnu_m_count; + + if (widthStar) + { + bool skip = false; + Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip); + if (skip) + continue; + if (!e) + return true; + Type *t = e->type->toBasetype(); + if (t->ty != Tint32 && t->ty != Tuns32) + errorPrintfFormat("width ", slice, e, "int", t); + } + + if (precisionStar) + { + bool skip = false; + Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip); + if (skip) + continue; + if (!e) + return true; + Type *t = e->type->toBasetype(); + if (t->ty != Tint32 && t->ty != Tuns32) + errorPrintfFormat("precision ", slice, e, "int", t); + } + + bool skip = false; + Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip); + if (skip) + continue; + if (!e) + return true; + Type *t = e->type->toBasetype(); + Type *tnext = t->nextOf(); + const unsigned c_longsize = target.c.longsize; + const bool is64bit = global.params.is64bit; + + // Types which are promoted to int are allowed. + // Spec: C99 6.5.2.2.7 + switch (fmt) + { + case Format_u: // unsigned int + case Format_d: // int + if (t->ty != Tint32 && t->ty != Tuns32) + errorPrintfFormat(NULL, slice, e, "int", t); + break; + + case Format_hhu: // unsigned char + case Format_hhd: // signed char + if (t->ty != Tint32 && t->ty != Tuns32 && t->ty != Tint8 && t->ty != Tuns8) + errorPrintfFormat(NULL, slice, e, "byte", t); + break; + + case Format_hu: // unsigned short int + case Format_hd: // short int + if (t->ty != Tint32 && t->ty != Tuns32 && t->ty != Tint16 && t->ty != Tuns16) + errorPrintfFormat(NULL, slice, e, "short", t); + break; + + case Format_lu: // unsigned long int + case Format_ld: // long int + if (!(t->isintegral() && t->size() == c_longsize)) + errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "int" : "long"), t); + break; + + case Format_llu: // unsigned long long int + case Format_lld: // long long int + if (t->ty != Tint64 && t->ty != Tuns64) + errorPrintfFormat(NULL, slice, e, "long", t); + break; + + case Format_ju: // uintmax_t + case Format_jd: // intmax_t + if (t->ty != Tint64 && t->ty != Tuns64) + errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t", t); + break; + + case Format_zd: // size_t + if (!(t->isintegral() && t->size() == (is64bit ? 8 : 4))) + errorPrintfFormat(NULL, slice, e, "size_t", t); + break; + + case Format_td: // ptrdiff_t + if (!(t->isintegral() && t->size() == (is64bit ? 8 : 4))) + errorPrintfFormat(NULL, slice, e, "ptrdiff_t", t); + break; + + case Format_GNU_a: // Format_GNU_a is only for scanf + case Format_lg: + case Format_g: // double + if (t->ty != Tfloat64 && t->ty != Timaginary64) + errorPrintfFormat(NULL, slice, e, "double", t); + break; + + case Format_Lg: // long double + if (t->ty != Tfloat80 && t->ty != Timaginary80) + errorPrintfFormat(NULL, slice, e, "real", t); + break; + + case Format_p: // pointer + if (t->ty != Tpointer && t->ty != Tnull && t->ty != Tclass && t->ty != Tdelegate && t->ty != Taarray) + errorPrintfFormat(NULL, slice, e, "void*", t); + break; + + case Format_n: // pointer to int + if (!(t->ty == Tpointer && tnext->ty == Tint32)) + errorPrintfFormat(NULL, slice, e, "int*", t); + break; + + case Format_ln: // pointer to long int + if (!(t->ty == Tpointer && tnext->isintegral() && tnext->size() == c_longsize)) + errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "int*" : "long*"), t); + break; + + case Format_lln: // pointer to long long int + if (!(t->ty == Tpointer && tnext->ty == Tint64)) + errorPrintfFormat(NULL, slice, e, "long*", t); + break; + + case Format_hn: // pointer to short + if (!(t->ty == Tpointer && tnext->ty == Tint16)) + errorPrintfFormat(NULL, slice, e, "short*", t); + break; + + case Format_hhn: // pointer to signed char + if (!(t->ty == Tpointer && tnext->ty == Tint16)) + errorPrintfFormat(NULL, slice, e, "byte*", t); + break; + + case Format_jn: // pointer to intmax_t + if (!(t->ty == Tpointer && tnext->ty == Tint64)) + errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t*", t); + break; + + case Format_zn: // pointer to size_t + if (!(t->ty == Tpointer && tnext->ty == (is64bit ? Tuns64 : Tuns32))) + errorPrintfFormat(NULL, slice, e, "size_t*", t); + break; + + case Format_tn: // pointer to ptrdiff_t + if (!(t->ty == Tpointer && tnext->ty == (is64bit ? Tint64 : Tint32))) + errorPrintfFormat(NULL, slice, e, "ptrdiff_t*", t); + break; + + case Format_c: // char + if (t->ty != Tint32 && t->ty != Tuns32) + errorPrintfFormat(NULL, slice, e, "char", t); + break; + + case Format_lc: // wint_t + if (t->ty != Tint32 && t->ty != Tuns32) + errorPrintfFormat(NULL, slice, e, "wchar_t", t); + break; + + case Format_s: // pointer to char string + if (!(t->ty == Tpointer && (tnext->ty == Tchar || tnext->ty == Tint8 || tnext->ty == Tuns8))) + errorPrintfFormat(NULL, slice, e, "char*", t); + break; + + case Format_ls: // pointer to wchar_t string + { + if (!(t->ty == Tpointer && tnext == target.c.twchar_t)) + errorPrintfFormat(NULL, slice, e, "wchar_t*", t); + break; + } + case Format_error: + deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr); + break; + + case Format_GNU_m: + break; // not assert(0) because it may go through it if there are extra arguments + + case Format_percent: + default: + assert(0); + } + } + return false; +} + +/*******************************************/ + +static Expression *getNextScanfArg(const Loc &loc, Expressions &args, size_t &n, bool asterisk) +{ + if (n == args.length) + { + if (!asterisk) + deprecation(loc, "more format specifiers than %d arguments", (int)n); + return NULL; + } + return args[n++]; +} + +static void errorScanfFormat(const char *prefix, DString &slice, + Expression *arg, const char *texpect, Type *tactual) +{ + deprecation(arg->loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`", + prefix ? prefix : "", arg->toChars(), (int)slice.length, slice.ptr, texpect, tactual->toChars()); +} + +/****************************************** + * Check that arguments to a scanf format string are compatible + * with that string. Issue errors for incompatibilities. + * + * Follows the C99 specification for scanf. + * + * Takes a generous, rather than strict, view of compatiblity. + * For example, an unsigned value can be formatted with a signed specifier. + * + * Diagnosed incompatibilities are: + * + * 1. incompatible sizes which will cause argument misalignment + * 2. deferencing arguments that are not pointers + * 3. insufficient number of arguments + * 4. struct arguments + * 5. array and slice arguments + * 6. non-standard formats + * 7. undefined behavior per C99 + * + * Per the C Standard, extra arguments are ignored. + * + * No attempt is made to fix the arguments or the format string. + * + * Params: + * loc = location for error messages + * format = format string + * args = arguments to match with format string + * isVa_list = if a "v" function (format check only) + * + * Returns: + * `true` if errors occurred + * References: + * C99 7.19.6.2 + * http://www.cplusplus.com/reference/cstdio/scanf/ + */ +bool checkScanfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list) +{ + size_t n = 0; + const size_t format_length = strlen(format); + for (size_t i = 0; i < format_length;) + { + if (format[i] != '%') + { + ++i; + continue; + } + bool asterisk = false; + size_t j = i; + const Format fmt = parseScanfFormatSpecifier(format, j, asterisk); + DString slice = DString(j - i, format + i); + i = j; + + if (fmt == Format_percent || asterisk) + continue; // "%%", "%*": no arguments + + if (isVa_list) + { + // format check only + if (fmt == Format_error) + deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr); + continue; + } + + Expression *e = getNextScanfArg(loc, args, n, asterisk); + if (!e) + return true; + + Type *t = e->type->toBasetype(); + Type *tnext = t->nextOf(); + const unsigned c_longsize = target.c.longsize; + const bool is64bit = global.params.is64bit; + + switch (fmt) + { + case Format_n: + case Format_d: // pointer to int + if (!(t->ty == Tpointer && tnext->ty == Tint32)) + errorScanfFormat(NULL, slice, e, "int*", t); + break; + + case Format_hhn: + case Format_hhd: // pointer to signed char + if (!(t->ty == Tpointer && tnext->ty == Tint16)) + errorScanfFormat(NULL, slice, e, "byte*", t); + break; + + case Format_hn: + case Format_hd: // pointer to short + if (!(t->ty == Tpointer && tnext->ty == Tint16)) + errorScanfFormat(NULL, slice, e, "short*", t); + break; + + case Format_ln: + case Format_ld: // pointer to long int + if (!(t->ty == Tpointer && tnext->isintegral() && tnext->size() == c_longsize)) + errorScanfFormat(NULL, slice, e, (c_longsize == 4 ? "int*" : "long*"), t); + break; + + case Format_lln: + case Format_lld: // pointer to long long int + if (!(t->ty == Tpointer && tnext->ty == Tint64)) + errorScanfFormat(NULL, slice, e, "long*", t); + break; + + case Format_jn: + case Format_jd: // pointer to intmax_t + if (!(t->ty == Tpointer && tnext->ty == Tint64)) + errorScanfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t*", t); + break; + + case Format_zn: + case Format_zd: // pointer to size_t + if (!(t->ty == Tpointer && tnext->ty == (is64bit ? Tuns64 : Tuns32))) + errorScanfFormat(NULL, slice, e, "size_t*", t); + break; + + case Format_tn: + case Format_td: // pointer to ptrdiff_t + if (!(t->ty == Tpointer && tnext->ty == (is64bit ? Tint64 : Tint32))) + errorScanfFormat(NULL, slice, e, "ptrdiff_t*", t); + break; + + case Format_u: // pointer to unsigned int + if (!(t->ty == Tpointer && tnext->ty == Tuns32)) + errorScanfFormat(NULL, slice, e, "uint*", t); + break; + + case Format_hhu: // pointer to unsigned char + if (!(t->ty == Tpointer && tnext->ty == Tuns8)) + errorScanfFormat(NULL, slice, e, "ubyte*", t); + break; + + case Format_hu: // pointer to unsigned short int + if (!(t->ty == Tpointer && tnext->ty == Tuns16)) + errorScanfFormat(NULL, slice, e, "ushort*", t); + break; + + case Format_lu: // pointer to unsigned long int + if (!(t->ty == Tpointer && tnext->ty == (is64bit ? Tuns64 : Tuns32))) + errorScanfFormat(NULL, slice, e, (c_longsize == 4 ? "uint*" : "ulong*"), t); + break; + + case Format_llu: // pointer to unsigned long long int + if (!(t->ty == Tpointer && tnext->ty == Tuns64)) + errorScanfFormat(NULL, slice, e, "ulong*", t); + break; + + case Format_ju: // pointer to uintmax_t + if (!(t->ty == Tpointer && tnext->ty == (is64bit ? Tuns64 : Tuns32))) + errorScanfFormat(NULL, slice, e, "ulong*", t); + break; + + case Format_g: // pointer to float + if (!(t->ty == Tpointer && tnext->ty == Tfloat32)) + errorScanfFormat(NULL, slice, e, "float*", t); + break; + + case Format_lg: // pointer to double + if (!(t->ty == Tpointer && tnext->ty == Tfloat64)) + errorScanfFormat(NULL, slice, e, "double*", t); + break; + + case Format_Lg: // pointer to long double + if (!(t->ty == Tpointer && tnext->ty == Tfloat80)) + errorScanfFormat(NULL, slice, e, "real*", t); + break; + + case Format_GNU_a: + case Format_GNU_m: + case Format_c: + case Format_s: // pointer to char string + if (!(t->ty == Tpointer && (tnext->ty == Tchar || tnext->ty == Tint8 || tnext->ty == Tuns8))) + errorScanfFormat(NULL, slice, e, "char*", t); + break; + + case Format_lc: + case Format_ls: // pointer to wchar_t string + { + if (!(t->ty == Tpointer && tnext == target.c.twchar_t)) + errorScanfFormat(NULL, slice, e, "wchar_t*", t); + break; + } + case Format_p: // double pointer + if (!(t->ty == Tpointer && tnext->ty == Tpointer)) + errorScanfFormat(NULL, slice, e, "void**", t); + break; + + case Format_error: + deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr); + break; + + case Format_percent: + default: + assert(0); + } + } + return false; +} diff --git a/gcc/d/dmd/cppmangle.c b/gcc/d/dmd/cppmangle.c index a0e0b5fb993..baf64c5653e 100644 --- a/gcc/d/dmd/cppmangle.c +++ b/gcc/d/dmd/cppmangle.c @@ -806,6 +806,14 @@ public: writeBasicType(t, 'D', 'n'); } + void visit(TypeNoreturn *t) + { + if (t->isImmutable() || t->isShared()) + return error(t); + + writeBasicType(t, 0, 'v'); // mangle like `void` + } + void visit(TypeBasic *t) { if (t->isImmutable() || t->isShared()) @@ -1012,7 +1020,7 @@ public: if (t->isImmutable() || t->isShared()) return error(t); - /* __c_(u)long(long) get special mangling + /* __c_(u)long(long) and others get special mangling */ Identifier *id = t->sym->ident; //printf("enum id = '%s'\n", id->toChars()); @@ -1020,10 +1028,18 @@ public: return writeBasicType(t, 0, 'l'); else if (id == Id::__c_ulong) return writeBasicType(t, 0, 'm'); + else if (id == Id::__c_wchar_t) + return writeBasicType(t, 0, 'w'); else if (id == Id::__c_longlong) return writeBasicType(t, 0, 'x'); else if (id == Id::__c_ulonglong) return writeBasicType(t, 0, 'y'); + else if (id == Id::__c_complex_float) + return writeBasicType(t, 'C', 'f'); + else if (id == Id::__c_complex_double) + return writeBasicType(t, 'C', 'd'); + else if (id == Id::__c_complex_real) + return writeBasicType(t, 'C', 'e'); doSymbol(t); } diff --git a/gcc/d/dmd/ctfeexpr.c b/gcc/d/dmd/ctfeexpr.c index 1d669e595e5..a8e97833ad0 100644 --- a/gcc/d/dmd/ctfeexpr.c +++ b/gcc/d/dmd/ctfeexpr.c @@ -162,7 +162,7 @@ const char *CTFEExp::toChars() switch (op) { case TOKcantexp: return ""; - case TOKvoidexp: return ""; + case TOKvoidexp: return "cast(void)0"; case TOKbreak: return ""; case TOKcontinue: return ""; case TOKgoto: return ""; diff --git a/gcc/d/dmd/dcast.c b/gcc/d/dmd/dcast.c index 61e28fe8d13..4dd648bcc48 100644 --- a/gcc/d/dmd/dcast.c +++ b/gcc/d/dmd/dcast.c @@ -26,6 +26,7 @@ FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL); bool isCommutative(TOK op); MOD MODmerge(MOD mod1, MOD mod2); +void toAutoQualChars(const char **result, Type *t1, Type *t2); /* ==================== implicitCast ====================== */ @@ -90,8 +91,10 @@ Expression *implicitCastTo(Expression *e, Scope *sc, Type *t) //printf("type %p ty %d deco %p\n", type, type->ty, type->deco); //type = type->semantic(loc, sc); //printf("type %s t %s\n", type->deco, t->deco); + const char *ts[2]; + toAutoQualChars(ts, e->type, t); e->error("cannot implicitly convert expression (%s) of type %s to %s", - e->toChars(), e->type->toChars(), t->toChars()); + e->toChars(), ts[0], ts[1]); } } result = new ErrorExp(); diff --git a/gcc/d/dmd/dclass.c b/gcc/d/dmd/dclass.c index c7dbbbea5d3..3f33014da9f 100644 --- a/gcc/d/dmd/dclass.c +++ b/gcc/d/dmd/dclass.c @@ -277,15 +277,10 @@ Scope *ClassDeclaration::newScope(Scope *sc) Scope *sc2 = AggregateDeclaration::newScope(sc); if (isCOMclass()) { - if (global.params.isWindows) - sc2->linkage = LINKwindows; - else - { - /* This enables us to use COM objects under Linux and - * work with things like XPCOM - */ - sc2->linkage = LINKc; - } + /* This enables us to use COM objects under Linux and + * work with things like XPCOM + */ + sc2->linkage = target.systemLinkage(); } return sc2; } @@ -491,9 +486,10 @@ void ClassDeclaration::finalizeSize() assert(baseClass->sizeok == SIZEOKdone); alignsize = baseClass->alignsize; - structsize = baseClass->structsize; - if (isCPPclass() && global.params.isWindows) - structsize = (structsize + alignsize - 1) & ~(alignsize - 1); + if (classKind == ClassKind::cpp) + structsize = target.cpp.derivedClassOffset(baseClass); + else + structsize = baseClass->structsize; } else if (isInterfaceDeclaration()) { diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 81b563f4b0f..55c814288e0 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -118,7 +118,7 @@ struct Match FuncDeclaration *anyf; // pick a func, any func, to use for error recovery }; -void functionResolve(Match *m, Dsymbol *fd, Loc loc, Scope *sc, Objects *tiargs, Type *tthis, Expressions *fargs); +void functionResolve(Match *m, Dsymbol *fd, Loc loc, Scope *sc, Objects *tiargs, Type *tthis, Expressions *fargs, const char **pMessage = NULL); int overloadApply(Dsymbol *fstart, void *param, int (*fp)(void *, Dsymbol *)); void aliasSemantic(AliasDeclaration *ds, Scope *sc); @@ -551,6 +551,8 @@ void builtin_init(); #define FUNCFLAGreturnInprocess 0x10 // working on inferring 'return' for parameters #define FUNCFLAGinlineScanned 0x20 // function has been scanned for inline possibilities #define FUNCFLAGinferScope 0x40 // infer 'scope' for parameters +#define FUNCFLAGprintf 0x200 // is a printf-like function +#define FUNCFLAGscanf 0x400 // is a scanf-like function class FuncDeclaration : public Declaration { diff --git a/gcc/d/dmd/denum.c b/gcc/d/dmd/denum.c index ca1d3bb771c..bfd3b7274f4 100644 --- a/gcc/d/dmd/denum.c +++ b/gcc/d/dmd/denum.c @@ -22,6 +22,19 @@ #include "declaration.h" #include "init.h" +bool isSpecialEnumIdent(const Identifier *ident) +{ + return ident == Id::__c_long || + ident == Id::__c_ulong || + ident == Id::__c_longlong || + ident == Id::__c_ulonglong || + ident == Id::__c_long_double || + ident == Id::__c_wchar_t || + ident == Id::__c_complex_float || + ident == Id::__c_complex_double || + ident == Id::__c_complex_real; +} + /********************************* EnumDeclaration ****************************/ EnumDeclaration::EnumDeclaration(Loc loc, Identifier *id, Type *memtype) @@ -187,11 +200,7 @@ Lerrors: */ bool EnumDeclaration::isSpecial() const { - return (ident == Id::__c_long || - ident == Id::__c_ulong || - ident == Id::__c_longlong || - ident == Id::__c_ulonglong || - ident == Id::__c_long_double) && memtype; + return isSpecialEnumIdent(ident) && memtype; } Expression *EnumDeclaration::getDefaultValue(Loc loc) diff --git a/gcc/d/dmd/dimport.c b/gcc/d/dmd/dimport.c index 0d93ed80e58..7b63a186552 100644 --- a/gcc/d/dmd/dimport.c +++ b/gcc/d/dmd/dimport.c @@ -161,37 +161,75 @@ void Import::load(Scope *sc) if (mod && !mod->importedFrom) mod->importedFrom = sc ? sc->_module->importedFrom : Module::rootModule; if (!pkg) - pkg = mod; + { + if (mod && mod->isPackageFile) + { + // one level depth package.d file (import pkg; ./pkg/package.d) + // it's necessary to use the wrapping Package already created + pkg = mod->pkg; + } + else + pkg = mod; + } //printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg); } void Import::importAll(Scope *sc) { - if (!mod) + if (mod) return; // Already done + load(sc); + if (!mod) return; // Failed + + if (sc->stc & STCstatic) + isstatic = true; + mod->importAll(NULL); + if (mod->md && mod->md->isdeprecated) { - load(sc); - if (mod) // if successfully loaded module - { - mod->importAll(NULL); - - if (mod->md && mod->md->isdeprecated) - { - Expression *msg = mod->md->msg; - if (StringExp *se = msg ? msg->toStringExp() : NULL) - mod->deprecation(loc, "is deprecated - %s", se->string); - else - mod->deprecation(loc, "is deprecated"); - } + Expression *msg = mod->md->msg; + if (StringExp *se = msg ? msg->toStringExp() : NULL) + mod->deprecation(loc, "is deprecated - %s", se->string); + else + mod->deprecation(loc, "is deprecated"); + } + if (sc->explicitProtection) + protection = sc->protection; + if (!isstatic && !aliasId && !names.length) + sc->scopesym->importScope(mod, protection); + // Enable access to pkgs/mod as soon as posible, because compiler + // can traverse them before the import gets semantic (Issue: 21501) + if (!aliasId && !names.length) + addPackageAccess(sc->scopesym); +} - if (sc->explicitProtection) - protection = sc->protection; - if (!isstatic && !aliasId && !names.length) - { - sc->scopesym->importScope(mod, protection); - } +/******************************* + * Mark the imported packages as accessible from the current + * scope. This access check is necessary when using FQN b/c + * we're using a single global package tree. + * https://issues.dlang.org/show_bug.cgi?id=313 + */ +void Import::addPackageAccess(ScopeDsymbol *scopesym) +{ + //printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this); + if (packages) + { + // import a.b.c.d; + Package *p = pkg; // a + scopesym->addAccessiblePackage(p, protection); + for (size_t i = 1; i < packages->length; i++) // [b, c] + { + Identifier *id = (*packages)[i]; + p = (Package *) p->symtab->lookup(id); + // https://issues.dlang.org/show_bug.cgi?id=17991 + // An import of truly empty file/package can happen + // https://issues.dlang.org/show_bug.cgi?id=20151 + // Package in the path conflicts with a module name + if (p == NULL) + return; + scopesym->addAccessiblePackage(p, protection); } } + scopesym->addAccessiblePackage(mod, protection); // d } Dsymbol *Import::toAlias() diff --git a/gcc/d/dmd/dmangle.c b/gcc/d/dmd/dmangle.c index 303ae617874..83f4c18bee8 100644 --- a/gcc/d/dmd/dmangle.c +++ b/gcc/d/dmd/dmangle.c @@ -82,6 +82,8 @@ void initTypeMangle() mangleChar[Treturn] = "@"; mangleChar[Tvector] = "@"; mangleChar[Ttraits] = "@"; + mangleChar[Tmixin] = "@"; + mangleChar[Tnoreturn] = "@"; // becomes 'Nn' mangleChar[Tnull] = "n"; // same as TypeNone @@ -150,7 +152,7 @@ public: * using upper case letters for all digits but the last digit which uses * a lower case letter. * The decoder has to look up the referenced position to determine - * whether the back reference is an identifer (starts with a digit) + * whether the back reference is an identifier (starts with a digit) * or a type (starts with a letter). * * Params: @@ -414,6 +416,11 @@ public: visit((Type *)t); } + void visit(TypeNoreturn *) + { + buf->writestring("Nn"); + } + //////////////////////////////////////////////////////////////////////////// void mangleDecl(Declaration *sthis) @@ -1085,3 +1092,31 @@ void mangleToBuffer(TemplateInstance *ti, OutBuffer *buf) Mangler v(buf); v.mangleTemplateInstance(ti); } + +/********************************************** + * Convert a string representing a type (the deco) and + * return its equivalent Type. + * Params: + * deco = string containing the deco + * Returns: + * null for failed to convert + * Type for succeeded + */ + +Type *decoToType(const char *deco) +{ + if (!deco) + return NULL; + + //printf("decoToType(): %s\n", deco) + if (StringValue *sv = Type::stringtable.lookup(deco, strlen(deco))) + { + if (sv->ptrvalue) + { + Type *t = (Type *)sv->ptrvalue; + assert(t->deco); + return t; + } + } + return NULL; +} diff --git a/gcc/d/dmd/dmodule.c b/gcc/d/dmd/dmodule.c index ed01858f06b..472b2b9e7f8 100644 --- a/gcc/d/dmd/dmodule.c +++ b/gcc/d/dmd/dmodule.c @@ -52,6 +52,7 @@ Module::Module(const char *filename, Identifier *ident, int doDocComment, int do members = NULL; isDocFile = 0; isPackageFile = false; + pkg = NULL; needmoduleinfo = 0; selfimports = 0; rootimports = 0; @@ -685,15 +686,27 @@ Module *Module::parse() * * To avoid the conflict: * 1. If preceding package name insertion had occurred by Package::resolve, - * later package.d loading will change Package::isPkgMod to PKGmodule and set Package::mod. + * reuse the previous wrapping 'Package' if it exists * 2. Otherwise, 'package.d' wrapped by 'Package' is inserted to the internal tree in here. + * + * Then change Package::isPkgMod to PKGmodule and set Package::mod. + * + * Note that the 'wrapping Package' is the Package that contains package.d and other submodules, + * the one inserted to the symbol table. */ - Package *p = new Package(ident); + Dsymbol *ps = dst->lookup(ident); + Package *p = ps ? ps->isPackage() : NULL; + if (p == NULL) + { + p = new Package(ident); + p->tag = this->tag; // reuse the same package tag + p->symtab = new DsymbolTable(); + } + this->tag= p->tag; // reuse the 'older' package tag + this->pkg = p; p->parent = this->parent; p->isPkgMod = PKGmodule; p->mod = this; - p->tag = this->tag; // reuse the same package tag - p->symtab = new DsymbolTable(); s = p; } if (!dst->insert(s)) @@ -720,15 +733,9 @@ Module *Module::parse() } else if (Package *pkg = prev->isPackage()) { - if (pkg->isPkgMod == PKGunknown && isPackageFile) - { - /* If the previous inserted Package is not yet determined as package.d, - * link it to the actual module. - */ - pkg->isPkgMod = PKGmodule; - pkg->mod = this; - pkg->tag = this->tag; // reuse the same package tag - } + // 'package.d' loaded after a previous 'Package' insertion + if (isPackageFile) + amodules.push(this); // Add to global array of all modules else error(md ? md->loc : loc, "from file %s conflicts with package name %s", srcname, pkg->toChars()); diff --git a/gcc/d/dmd/dscope.c b/gcc/d/dmd/dscope.c index 65e6734b035..e56f3936ee0 100644 --- a/gcc/d/dmd/dscope.c +++ b/gcc/d/dmd/dscope.c @@ -24,6 +24,7 @@ #include "aggregate.h" #include "module.h" #include "id.h" +#include "target.h" #include "template.h" Scope *Scope::freelist = NULL; @@ -155,7 +156,8 @@ Scope *Scope::push() s->nofree = 0; s->fieldinit = saveFieldInit(); s->flags = (flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile | SCOPEconstraint | - SCOPEnoaccesscheck | SCOPEignoresymbolvisibility)); + SCOPEnoaccesscheck | SCOPEignoresymbolvisibility | + SCOPEprintf | SCOPEscanf)); s->lastdc = NULL; assert(this != s); @@ -637,7 +639,7 @@ const char *Scope::search_correct_C(Identifier *ident) else if (ident == Id::C_unsigned) tok = TOKuns32; else if (ident == Id::C_wchar_t) - tok = global.params.isWindows ? TOKwchar : TOKdchar; + tok = target.c.twchar_t->ty == Twchar ? TOKwchar : TOKdchar; else return NULL; return Token::toChars(tok); diff --git a/gcc/d/dmd/dsymbol.c b/gcc/d/dmd/dsymbol.c index 89c55576a55..f0c1cf6d93c 100644 --- a/gcc/d/dmd/dsymbol.c +++ b/gcc/d/dmd/dsymbol.c @@ -515,7 +515,7 @@ Dsymbol *Dsymbol::search_correct(Identifier *ident) * Returns: * symbol found, NULL if not */ -Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id) +Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id, int flags) { //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident->toChars()); Dsymbol *s = toAlias(); @@ -533,7 +533,7 @@ Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id) switch (id->dyncast()) { case DYNCAST_IDENTIFIER: - sm = s->search(loc, (Identifier *)id); + sm = s->search(loc, (Identifier *)id, flags); break; case DYNCAST_DSYMBOL: @@ -1801,31 +1801,3 @@ bool Prot::operator==(const Prot& other) const } return false; } - -/** - * Checks if parent defines different access restrictions than this one. - * - * Params: - * parent = protection attribute for scope that hosts this one - * - * Returns: - * 'true' if parent is already more restrictive than this one and thus - * no differentiation is needed. - */ -bool Prot::isSubsetOf(const Prot& parent) const -{ - if (this->kind != parent.kind) - return false; - - if (this->kind == Prot::package_) - { - if (!this->pkg) - return true; - if (!parent.pkg) - return false; - if (parent.pkg->isAncestorPackageOf(this->pkg)) - return true; - } - - return true; -} diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index 1ddfe06e6a1..4aabb5d3c6c 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -107,7 +107,6 @@ struct Prot bool isMoreRestrictiveThan(const Prot other) const; bool operator==(const Prot& other) const; - bool isSubsetOf(const Prot& other) const; }; // in hdrgen.c @@ -207,7 +206,7 @@ public: virtual void importAll(Scope *sc); virtual Dsymbol *search(const Loc &loc, Identifier *ident, int flags = IgnoreNone); Dsymbol *search_correct(Identifier *id); - Dsymbol *searchX(Loc loc, Scope *sc, RootObject *id); + Dsymbol *searchX(Loc loc, Scope *sc, RootObject *id, int flags); virtual bool overloadInsert(Dsymbol *s); virtual d_uns64 size(Loc loc); virtual bool isforwardRef(); diff --git a/gcc/d/dmd/dsymbolsem.c b/gcc/d/dmd/dsymbolsem.c index 5d5c9fca769..26e23e98587 100644 --- a/gcc/d/dmd/dsymbolsem.c +++ b/gcc/d/dmd/dsymbolsem.c @@ -42,6 +42,8 @@ VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); Initializer *inferType(Initializer *init, Scope *sc); void MODtoBuffer(OutBuffer *buf, MOD mod); bool reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0); +bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps); +bool symbolIsVisible(Scope *sc, Dsymbol *s); Objc *objc(); static unsigned setMangleOverride(Dsymbol *s, char *sym) @@ -1098,22 +1100,7 @@ public: scopesym->importScope(imp->mod, imp->protection); } - // Mark the imported packages as accessible from the current - // scope. This access check is necessary when using FQN b/c - // we're using a single global package tree. See Bugzilla 313. - if (imp->packages) - { - // import a.b.c.d; - Package *p = imp->pkg; // a - scopesym->addAccessiblePackage(p, imp->protection); - for (size_t i = 1; i < imp->packages->length; i++) // [b, c] - { - Identifier *id = (*imp->packages)[i]; - p = (Package *) p->symtab->lookup(id); - scopesym->addAccessiblePackage(p, imp->protection); - } - } - scopesym->addAccessiblePackage(imp->mod, imp->protection); // d + imp->addPackageAccess(scopesym); } dsymbolSemantic(imp->mod, NULL); @@ -1130,8 +1117,12 @@ public: { AliasDeclaration *ad = imp->aliasdecls[i]; //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), imp->aliases[i]->toChars(), imp->names[i]->toChars(), ad->_scope); - if (imp->mod->search(imp->loc, imp->names[i])) + Dsymbol *sym = imp->mod->search(imp->loc, imp->names[i], IgnorePrivateImports); + if (sym) { + if (!symbolIsVisible(sc, sym)) + imp->mod->error(imp->loc, "member `%s` is not visible from module `%s`", + imp->names[i]->toChars(), sc->_module->toChars()); dsymbolSemantic(ad, sc); // If the import declaration is in non-root module, // analysis of the aliased symbol is deferred. @@ -1141,7 +1132,7 @@ public: { Dsymbol *s = imp->mod->search_correct(imp->names[i]); if (s) - imp->mod->error(imp->loc, "import `%s` not found, did you mean %s `%s`?", imp->names[i]->toChars(), s->kind(), s->toChars()); + imp->mod->error(imp->loc, "import `%s` not found, did you mean %s `%s`?", imp->names[i]->toChars(), s->kind(), s->toPrettyChars()); else imp->mod->error(imp->loc, "import `%s` not found", imp->names[i]->toChars()); ad->type = Type::terror; @@ -1312,8 +1303,6 @@ public: e = expressionSemantic(e, sc); e = resolveProperties(sc, e); sc = sc->endCTFE(); - - // pragma(msg) is allowed to contain types as well as expressions e = ctfeInterpretForPragmaMsg(e); if (e->op == TOKerror) { @@ -1458,6 +1447,12 @@ public: } } } + else if (pd->ident == Id::printf || pd->ident == Id::scanf) + { + if (pd->args && pd->args->length != 0) + pd->error("takes no argument"); + goto Ldecl; + } else if (global.params.ignoreUnsupportedPragmas) { if (global.params.verbose) @@ -1547,13 +1542,14 @@ public: Dsymbols *compileIt(CompileDeclaration *cd) { //printf("CompileDeclaration::compileIt(loc = %d) %s\n", cd->loc.linnum, cd->exp->toChars()); - StringExp *se = semanticString(sc, cd->exp, "argument to mixin"); - if (!se) + OutBuffer buf; + if (expressionsToString(buf, sc, cd->exps)) return NULL; - se = se->toUTF8(sc); unsigned errors = global.errors; - Parser p(cd->loc, sc->_module, (utf8_t *)se->string, se->len, 0); + const size_t len = buf.length(); + const char *str = buf.extractChars(); + Parser p(cd->loc, sc->_module, (const utf8_t *)str, len, false); p.nextToken(); Dsymbols *d = p.parseDeclDefs(0); @@ -1562,7 +1558,7 @@ public: if (p.token.value != TOKeof) { - cd->exp->error("incomplete mixin declaration (%s)", se->toChars()); + cd->error("incomplete mixin declaration (%s)", str); return NULL; } return d; @@ -1637,7 +1633,7 @@ public: Scope *sc = m->_scope; // see if already got one from importAll() if (!sc) { - Scope::createGlobal(m); // create root scope + sc = Scope::createGlobal(m); // create root scope } //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage); @@ -1735,7 +1731,7 @@ public: // memtype is forward referenced, so try again later ed->_scope = scx ? scx : sc->copy(); ed->_scope->setNoFree(); - ed->_scope->_module->addDeferredSemantic(ed); + Module::addDeferredSemantic(ed); Module::dprogress = dprogress_save; //printf("\tdeferring %s\n", ed->toChars()); ed->semanticRun = PASSinit; @@ -2233,7 +2229,7 @@ public: //printf("forward reference - deferring\n"); tm->_scope = scx ? scx : sc->copy(); tm->_scope->setNoFree(); - tm->_scope->_module->addDeferredSemantic(tm); + Module::addDeferredSemantic(tm); return; } @@ -2457,6 +2453,23 @@ public: ns->semanticRun = PASSsemanticdone; } + +private: + static bool isPointerToChar(Parameter *p) + { + if (TypePointer *tptr = p->type->isTypePointer()) + { + return tptr->next->ty == Tchar; + } + return false; + } + + static bool isVa_list(Parameter *p, FuncDeclaration *funcdecl, Scope *sc) + { + return p->type->equals(target.va_listType(funcdecl->loc, sc)); + } + +public: void funcDeclarationSemantic(FuncDeclaration *funcdecl) { TypeFunction *f; @@ -2771,6 +2784,45 @@ public: if (funcdecl->isAbstract() && funcdecl->isFinalFunc()) funcdecl->error("cannot be both final and abstract"); + if (const unsigned pors = sc->flags & (SCOPEprintf | SCOPEscanf)) + { + /* printf/scanf-like functions must be of the form: + * extern (C/C++) T printf([parameters...], const(char)* format, ...); + * or: + * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); + */ + const size_t nparams = f->parameterList.length(); + if ((f->linkage == LINKc || f->linkage == LINKcpp) && + + ((f->parameterList.varargs == VARARGvariadic && + nparams >= 1 && + isPointerToChar(f->parameterList[nparams - 1])) || + (f->parameterList.varargs == VARARGnone && + nparams >= 2 && + isPointerToChar(f->parameterList[nparams - 2]) && + isVa_list(f->parameterList[nparams - 1], funcdecl, sc)) + ) + ) + { + funcdecl->flags |= (pors == SCOPEprintf) ? FUNCFLAGprintf : FUNCFLAGscanf; + } + else + { + const char *p = (pors == SCOPEprintf ? Id::printf : Id::scanf)->toChars(); + if (f->parameterList.varargs == VARARGvariadic) + { + funcdecl->error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, ...)`" + " not `%s`", + p, f->next->toChars(), funcdecl->toChars(), funcdecl->type->toChars()); + } + else + { + funcdecl->error("`pragma(%s)` functions must be `extern(C) %s %s([parameters...], const(char)*, va_list)`", + p, f->next->toChars(), funcdecl->toChars()); + } + } + } + id = parent->isInterfaceDeclaration(); if (id) { @@ -3831,7 +3883,7 @@ public: sd->_scope = scx ? scx : sc->copy(); sd->_scope->setNoFree(); - sd->_scope->_module->addDeferredSemantic(sd); + Module::addDeferredSemantic(sd); //printf("\tdeferring %s\n", sd->toChars()); return; @@ -4079,7 +4131,7 @@ public: { //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); if (tc->sym->_scope) - tc->sym->_scope->_module->addDeferredSemantic(tc->sym); + Module::addDeferredSemantic(tc->sym); cldec->baseok = BASEOKnone; } L7: ; @@ -4131,7 +4183,7 @@ public: { //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); if (tc->sym->_scope) - tc->sym->_scope->_module->addDeferredSemantic(tc->sym); + Module::addDeferredSemantic(tc->sym); cldec->baseok = BASEOKnone; } i++; @@ -4141,7 +4193,7 @@ public: // Forward referencee of one or more bases, try again later cldec->_scope = scx ? scx : sc->copy(); cldec->_scope->setNoFree(); - cldec->_scope->_module->addDeferredSemantic(cldec); + Module::addDeferredSemantic(cldec); //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, cldec->toChars()); return; } @@ -4254,8 +4306,8 @@ public: cldec->_scope = scx ? scx : sc->copy(); cldec->_scope->setNoFree(); if (tc->sym->_scope) - tc->sym->_scope->_module->addDeferredSemantic(tc->sym); - cldec->_scope->_module->addDeferredSemantic(cldec); + Module::addDeferredSemantic(tc->sym); + Module::addDeferredSemantic(cldec); //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, cldec->toChars()); return; } @@ -4359,7 +4411,7 @@ public: cldec->_scope = scx ? scx : sc->copy(); cldec->_scope->setNoFree(); - cldec->_scope->_module->addDeferredSemantic(cldec); + Module::addDeferredSemantic(cldec); //printf("\tdeferring %s\n", cldec->toChars()); return; } @@ -4628,7 +4680,7 @@ public: { //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); if (tc->sym->_scope) - tc->sym->_scope->_module->addDeferredSemantic(tc->sym); + Module::addDeferredSemantic(tc->sym); idec->baseok = BASEOKnone; } i++; @@ -4638,7 +4690,7 @@ public: // Forward referencee of one or more bases, try again later idec->_scope = scx ? scx : sc->copy(); idec->_scope->setNoFree(); - idec->_scope->_module->addDeferredSemantic(idec); + Module::addDeferredSemantic(idec); return; } idec->baseok = BASEOKdone; @@ -4682,8 +4734,8 @@ public: idec->_scope = scx ? scx : sc->copy(); idec->_scope->setNoFree(); if (tc->sym->_scope) - tc->sym->_scope->_module->addDeferredSemantic(tc->sym); - idec->_scope->_module->addDeferredSemantic(idec); + Module::addDeferredSemantic(tc->sym); + Module::addDeferredSemantic(idec); return; } } @@ -5335,6 +5387,7 @@ void aliasSemantic(AliasDeclaration *ds, Scope *sc) ds->userAttribDecl = sc->userAttribDecl; // TypeTraits needs to know if it's located in an AliasDeclaration + const unsigned oldflags = sc->flags; sc->flags |= SCOPEalias; if (ds->aliassym) @@ -5345,7 +5398,7 @@ void aliasSemantic(AliasDeclaration *ds, Scope *sc) { if (fd && fd->semanticRun >= PASSsemanticdone) { - sc->flags &= ~SCOPEalias; + sc->flags = oldflags; return; } @@ -5361,13 +5414,13 @@ void aliasSemantic(AliasDeclaration *ds, Scope *sc) ds->aliassym = NULL; ds->type = Type::terror; } - sc->flags &= ~SCOPEalias; + sc->flags = oldflags; return; } if (ds->aliassym->isTemplateInstance()) dsymbolSemantic(ds->aliassym, sc); - sc->flags &= ~SCOPEalias; + sc->flags = oldflags; return; } ds->inuse = 1; @@ -5472,7 +5525,7 @@ void aliasSemantic(AliasDeclaration *ds, Scope *sc) if (!ds->overloadInsert(sx)) ScopeDsymbol::multiplyDefined(Loc(), sx, ds); } - sc->flags &= ~SCOPEalias; + sc->flags = oldflags; } diff --git a/gcc/d/dmd/dtemplate.c b/gcc/d/dmd/dtemplate.c index b868e9ad388..208b064aafb 100644 --- a/gcc/d/dmd/dtemplate.c +++ b/gcc/d/dmd/dtemplate.c @@ -156,17 +156,14 @@ Dsymbol *getDsymbol(RootObject *oarg) if (ea) { // Try to convert Expression to symbol - if (ea->op == TOKvar) - sa = ((VarExp *)ea)->var; - else if (ea->op == TOKfunction) - { - if (((FuncExp *)ea)->td) - sa = ((FuncExp *)ea)->td; - else - sa = ((FuncExp *)ea)->fd; - } - else if (ea->op == TOKtemplate) - sa = ((TemplateExp *)ea)->td; + if (VarExp *ve = ea->isVarExp()) + sa = ve->var; + else if (FuncExp *fe = ea->isFuncExp()) + sa = fe->td ? (Dsymbol *)fe->td : (Dsymbol *)fe->fd; + else if (TemplateExp *te = ea->isTemplateExp()) + sa = te->td; + else if (ScopeExp *se = ea->isScopeExp()) + sa = se->sds; else sa = NULL; } @@ -536,6 +533,7 @@ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id, this->isstatic = true; this->previous = NULL; this->protection = Prot(Prot::undefined); + this->inuse = 0; this->instances = NULL; // Compute in advance for Ddoc's use @@ -770,7 +768,9 @@ MATCH TemplateDeclaration::matchWithInstance(Scope *sc, TemplateInstance *ti, Declaration *sparam; //printf("\targument [%d]\n", i); + inuse++; m2 = tp->matchArg(ti->loc, paramscope, ti->tiargs, i, parameters, dedtypes, &sparam); + inuse--; //printf("\tm2 = %d\n", m2); if (m2 == MATCHnomatch) @@ -1397,7 +1397,9 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( } else { + inuse++; oded = tparam->defaultArg(instLoc, paramscope); + inuse--; if (oded) (*dedargs)[i] = declareParameter(paramscope, tparam, oded); } @@ -1771,7 +1773,9 @@ Lmatch: } else { + inuse++; oded = tparam->defaultArg(instLoc, paramscope); + inuse--; if (!oded) { // if tuple parameter and @@ -1997,18 +2001,19 @@ bool TemplateDeclaration::isOverloadable() /************************************************* * Given function arguments, figure out which template function * to expand, and return matching result. - * Input: - * m matching result - * dstart the root of overloaded function templates - * loc instantiation location - * sc instantiation scope - * tiargs initial list of template arguments - * tthis if !NULL, the 'this' pointer argument - * fargs arguments to function + * Params: + * m = matching result + * dstart = the root of overloaded function templates + * loc = instantiation location + * sc = instantiation scope + * tiargs = initial list of template arguments + * tthis = if !NULL, the 'this' pointer argument + * fargs = arguments to function + * pMessage = address to store error message, or null */ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, - Objects *tiargs, Type *tthis, Expressions *fargs) + Objects *tiargs, Type *tthis, Expressions *fargs, const char **pMessage) { struct ParamDeduce { @@ -2018,6 +2023,7 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, Type *tthis; Objects *tiargs; Expressions *fargs; + const char **pMessage; // result Match *m; int property; // 0: unintialized @@ -2093,7 +2099,7 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, else return 0; // MATCHnomatch } - MATCH mfa = tf->callMatch(tthis_fd, fargs); + MATCH mfa = tf->callMatch(tthis_fd, fargs, 0, pMessage); //printf("test1: mfa = %d\n", mfa); if (mfa > MATCHnomatch) { @@ -2180,8 +2186,12 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, int applyTemplate(TemplateDeclaration *td) { //printf("applyTemplate()\n"); - // skip duplicates - if (td == td_best) + if (td->inuse) + { + td->error(loc, "recursive template expansion"); + return 1; + } + if (td == td_best) // skip duplicates return 0; if (!sc) @@ -2431,6 +2441,7 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, p.tthis = tthis; p.tiargs = tiargs; p.fargs = fargs; + p.pMessage = pMessage; // result p.m = m; @@ -5165,6 +5176,16 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, * template X(T) {} // T => sa */ } + else if (ta && ta->ty != Tident) + { + /* Match any type that's not a TypeIdentifier to alias parameters, + * but prefer type parameter. + * template X(alias a) { } // a == ta + * + * TypeIdentifiers are excluded because they might be not yet resolved aliases. + */ + m = MATCHconvert; + } else goto Lnomatch; } @@ -5485,12 +5506,15 @@ RootObject *TemplateValueParameter::defaultArg(Loc instLoc, Scope *sc) if (e) { e = e->syntaxCopy(); + unsigned olderrs = global.errors; if ((e = expressionSemantic(e, sc)) == NULL) return NULL; if ((e = resolveProperties(sc, e)) == NULL) return NULL; e = e->resolveLoc(instLoc, sc); // use the instantiated loc e = e->optimize(WANTvalue); + if (global.errors != olderrs) + e = new ErrorExp(); } return e; } @@ -6049,6 +6073,7 @@ bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f if (ta) { //printf("type %s\n", ta->toChars()); + // It might really be an Expression or an Alias ta->resolve(loc, sc, &ea, &ta, &sa, (flags & 1) != 0); if (ea) goto Lexpr; @@ -6270,6 +6295,7 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) } unsigned errs = global.errors; + TemplateDeclaration *td_last = NULL; struct ParamBest { @@ -6291,7 +6317,11 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) return 0; - + if (td->inuse) + { + td->error(ti->loc, "recursive template expansion"); + return 1; + } if (td == td_best) // skip duplicates return 0; @@ -6349,8 +6379,6 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) /* Since there can be multiple TemplateDeclaration's with the same * name, look for the best match. */ - TemplateDeclaration *td_last = NULL; - OverloadSet *tovers = tempdecl->isOverloadSet(); size_t overs_dim = tovers ? tovers->a.length : 1; for (size_t oi = 0; oi < overs_dim; oi++) @@ -6359,7 +6387,9 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) p.td_best = NULL; p.td_ambig = NULL; p.m_best = MATCHnomatch; - overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamBest::fp); + + Dsymbol *dstart = tovers ? tovers->a[oi] : tempdecl; + overloadApply(dstart, &p, &ParamBest::fp); if (p.td_ambig) { @@ -6481,8 +6511,11 @@ bool TemplateInstance::needsTypeInference(Scope *sc, int flag) { TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) - { return 0; + if (td->inuse) + { + td->error(ti->loc, "recursive template expansion"); + return 1; } /* If any of the overloaded template declarations need inference, @@ -7128,6 +7161,68 @@ void unSpeculative(Scope *sc, RootObject *o) unSpeculative(sc, ti); } +/** + Returns: true if the instances' innards are discardable. + + The idea of this function is to see if the template instantiation + can be 100% replaced with its eponymous member. All other members + can be discarded, even in the compiler to free memory (for example, + the template could be expanded in a region allocator, deemed trivial, + the end result copied back out independently and the entire region freed), + and can be elided entirely from the binary. + + The current implementation affects code that generally looks like: + + --- + template foo(args...) { + some_basic_type_or_string helper() { .... } + enum foo = helper(); + } + --- + + since it was the easiest starting point of implementation but it can and + should be expanded more later. +*/ +static bool isDiscardable(TemplateInstance *ti) +{ + if (ti->aliasdecl == NULL) + return false; + + VarDeclaration *v = ti->aliasdecl->isVarDeclaration(); + if (v == NULL) + return false; + + if (!(v->storage_class & STCmanifest)) + return false; + + // Currently only doing basic types here because it is the easiest proof-of-concept + // implementation with minimal risk of side effects, but it could likely be + // expanded to any type that already exists outside this particular instance. + if (!(v->type->equals(Type::tstring) || (v->type->isTypeBasic() != NULL))) + return false; + + // Static ctors and dtors, even in an eponymous enum template, are still run, + // so if any of them are in here, we'd better not assume it is trivial lest + // we break useful code + for (size_t i = 0; i < ti->members->length; i++) + { + Dsymbol *member = (*ti->members)[i]; + if (member->hasStaticCtorOrDtor()) + return false; + if (member->isStaticDtorDeclaration()) + return false; + if (member->isStaticCtorDeclaration()) + return false; + } + + // but if it passes through this gauntlet... it should be fine. D code will + // see only the eponymous member, outside stuff can never access it, even through + // reflection; the outside world ought to be none the wiser. Even dmd should be + // able to simply free the memory of everything except the final result. + + return true; +} + /*********************************************** * Returns true if this is not instantiated in non-root module, and * is a part of non-speculative instantiatiation. @@ -7137,38 +7232,6 @@ void unSpeculative(Scope *sc, RootObject *o) */ bool TemplateInstance::needsCodegen() { - // Now -allInst is just for the backward compatibility. - if (global.params.allInst) - { - //printf("%s minst = %s, enclosing (%s)->isNonRoot = %d\n", - // toPrettyChars(), minst ? minst->toChars() : NULL, - // enclosing ? enclosing->toPrettyChars() : NULL, enclosing && enclosing->inNonRoot()); - if (enclosing) - { - // Bugzilla 14588: If the captured context is not a function - // (e.g. class), the instance layout determination is guaranteed, - // because the semantic/semantic2 pass will be executed - // even for non-root instances. - if (!enclosing->isFuncDeclaration()) - return true; - - // Bugzilla 14834: If the captured context is a function, - // this excessive instantiation may cause ODR violation, because - // -allInst and others doesn't guarantee the semantic3 execution - // for that function. - - // If the enclosing is also an instantiated function, - // we have to rely on the ancestor's needsCodegen() result. - if (TemplateInstance *ti = enclosing->isInstantiated()) - return ti->needsCodegen(); - - // Bugzilla 13415: If and only if the enclosing scope needs codegen, - // this nested templates would also need code generation. - return !enclosing->inNonRoot(); - } - return true; - } - if (!minst) { // If this is a speculative instantiation, @@ -7185,6 +7248,10 @@ bool TemplateInstance::needsCodegen() if (tinst && tinst->needsCodegen()) { minst = tinst->minst; // cache result + if (global.params.allInst && minst) + { + return true; + } assert(minst); assert(minst->isRoot() || minst->rootImports()); return true; @@ -7192,6 +7259,10 @@ bool TemplateInstance::needsCodegen() if (tnext && (tnext->needsCodegen() || tnext->minst)) { minst = tnext->minst; // cache result + if (global.params.allInst && minst) + { + return true; + } assert(minst); return minst->isRoot() || minst->rootImports(); } @@ -7200,6 +7271,16 @@ bool TemplateInstance::needsCodegen() return false; } + if (global.params.allInst) + { + return true; + } + + if (isDiscardable(this)) + { + return false; + } + /* Even when this is reached to the codegen pass, * a non-root nested template should not generate code, * due to avoid ODR violation. @@ -7221,14 +7302,7 @@ bool TemplateInstance::needsCodegen() return false; } - /* The issue is that if the importee is compiled with a different -debug - * setting than the importer, the importer may believe it exists - * in the compiled importee when it does not, when the instantiation - * is behind a conditional debug declaration. - */ - // workaround for Bugzilla 11239 - if (global.params.useUnitTests || - global.params.debuglevel) + if (global.params.useUnitTests) { // Prefer instantiations from root modules, to maximize link-ability. if (minst->isRoot()) diff --git a/gcc/d/dmd/expression.c b/gcc/d/dmd/expression.c index c90392d16a2..2592b38d961 100644 --- a/gcc/d/dmd/expression.c +++ b/gcc/d/dmd/expression.c @@ -43,110 +43,10 @@ VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false); char *MODtoChars(MOD mod); bool MODimplicitConv(MOD modfrom, MOD modto); -MOD MODmerge(MOD mod1, MOD mod2); void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod); Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg); - -/************************************************************* - * Given var, we need to get the - * right 'this' pointer if var is in an outer class, but our - * existing 'this' pointer is in an inner class. - * Input: - * e1 existing 'this' - * ad struct or class we need the correct 'this' for - * var the specific member of ad we're accessing - */ - -Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad, - Expression *e1, Declaration *var, int flag = 0) -{ - //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars()); - L1: - Type *t = e1->type->toBasetype(); - //printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars()); - - /* If e1 is not the 'this' pointer for ad - */ - if (ad && - !(t->ty == Tpointer && t->nextOf()->ty == Tstruct && - ((TypeStruct *)t->nextOf())->sym == ad) - && - !(t->ty == Tstruct && - ((TypeStruct *)t)->sym == ad) - ) - { - ClassDeclaration *cd = ad->isClassDeclaration(); - ClassDeclaration *tcd = t->isClassHandle(); - - /* e1 is the right this if ad is a base class of e1 - */ - if (!cd || !tcd || - !(tcd == cd || cd->isBaseOf(tcd, NULL)) - ) - { - /* Only classes can be inner classes with an 'outer' - * member pointing to the enclosing class instance - */ - if (tcd && tcd->isNested()) - { - /* e1 is the 'this' pointer for an inner class: tcd. - * Rewrite it as the 'this' pointer for the outer class. - */ - - e1 = new DotVarExp(loc, e1, tcd->vthis); - e1->type = tcd->vthis->type; - e1->type = e1->type->addMod(t->mod); - // Do not call checkNestedRef() - //e1 = expressionSemantic(e1, sc); - - // Skip up over nested functions, and get the enclosing - // class type. - int n = 0; - Dsymbol *s; - for (s = tcd->toParent(); - s && s->isFuncDeclaration(); - s = s->toParent()) - { - FuncDeclaration *f = s->isFuncDeclaration(); - if (f->vthis) - { - //printf("rewriting e1 to %s's this\n", f->toChars()); - n++; - e1 = new VarExp(loc, f->vthis); - } - else - { - e1->error("need `this` of type %s to access member %s" - " from static function %s", - ad->toChars(), var->toChars(), f->toChars()); - e1 = new ErrorExp(); - return e1; - } - } - if (s && s->isClassDeclaration()) - { - e1->type = s->isClassDeclaration()->type; - e1->type = e1->type->addMod(t->mod); - if (n > 1) - e1 = expressionSemantic(e1, sc); - } - else - e1 = expressionSemantic(e1, sc); - goto L1; - } - - /* Can't find a path from e1 to ad - */ - if (flag) - return NULL; - e1->error("this for %s needs to be type %s not type %s", - var->toChars(), ad->toChars(), t->toChars()); - return new ErrorExp(); - } - } - return e1; -} +void toAutoQualChars(const char **result, Type *t1, Type *t2); /***************************************** * Determine if 'this' is available. @@ -230,817 +130,112 @@ bool isNeedThisScope(Scope *sc, Declaration *d) return true; } -/*************************************** - * Pull out any properties. +/****************************** + * check e is exp.opDispatch!(tiargs) or not + * It's used to switch to UFCS the semantic analysis path */ -Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) +bool isDotOpDispatch(Expression *e) { - //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token::toChars(e1->op), e1->toChars(), e2 ? e2->toChars() : NULL); - Loc loc = e1->loc; - - OverloadSet *os; - Dsymbol *s; - Objects *tiargs; - Type *tthis; - if (e1->op == TOKdot) - { - DotExp *de = (DotExp *)e1; - if (de->e2->op == TOKoverloadset) - { - tiargs = NULL; - tthis = de->e1->type; - os = ((OverExp *)de->e2)->vars; - goto Los; - } - } - else if (e1->op == TOKoverloadset) - { - tiargs = NULL; - tthis = NULL; - os = ((OverExp *)e1)->vars; - Los: - assert(os); - FuncDeclaration *fd = NULL; - if (e2) - { - e2 = expressionSemantic(e2, sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); + return e->op == TOKdotti && + ((DotTemplateInstanceExp *)e)->ti->name == Id::opDispatch; +} - Expressions a; - a.push(e2); +/**************************************** + * Expand tuples. + * Input: + * exps aray of Expressions + * Output: + * exps rewritten in place + */ - for (size_t i = 0; i < os->a.length; i++) - { - FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, &a, 1); - if (f) - { - if (f->errors) - return new ErrorExp(); - fd = f; - assert(fd->type->ty == Tfunction); - } - } - if (fd) - { - Expression *e = new CallExp(loc, e1, e2); - return expressionSemantic(e, sc); - } - } - { - for (size_t i = 0; i < os->a.length; i++) - { - FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, NULL, 1); - if (f) - { - if (f->errors) - return new ErrorExp(); - fd = f; - assert(fd->type->ty == Tfunction); - TypeFunction *tf = (TypeFunction *)fd->type; - if (!tf->isref && e2) - goto Leproplvalue; - } - } - if (fd) - { - Expression *e = new CallExp(loc, e1); - if (e2) - e = new AssignExp(loc, e, e2); - return expressionSemantic(e, sc); - } - } - if (e2) - goto Leprop; - } - else if (e1->op == TOKdotti) - { - DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; - if (!dti->findTempDecl(sc)) - goto Leprop; - if (!dti->ti->semanticTiargs(sc)) - goto Leprop; - tiargs = dti->ti->tiargs; - tthis = dti->e1->type; - if ((os = dti->ti->tempdecl->isOverloadSet()) != NULL) - goto Los; - if ((s = dti->ti->tempdecl) != NULL) - goto Lfd; - } - else if (e1->op == TOKdottd) - { - DotTemplateExp *dte = (DotTemplateExp *)e1; - s = dte->td; - tiargs = NULL; - tthis = dte->e1->type; - goto Lfd; - } - else if (e1->op == TOKscope) - { - s = ((ScopeExp *)e1)->sds; - TemplateInstance *ti = s->isTemplateInstance(); - if (ti && !ti->semanticRun && ti->tempdecl) - { - //assert(ti->needsTypeInference(sc)); - if (!ti->semanticTiargs(sc)) - goto Leprop; - tiargs = ti->tiargs; - tthis = NULL; - if ((os = ti->tempdecl->isOverloadSet()) != NULL) - goto Los; - if ((s = ti->tempdecl) != NULL) - goto Lfd; - } - } - else if (e1->op == TOKtemplate) - { - s = ((TemplateExp *)e1)->td; - tiargs = NULL; - tthis = NULL; - goto Lfd; - } - else if (e1->op == TOKdotvar && e1->type && e1->type->toBasetype()->ty == Tfunction) - { - DotVarExp *dve = (DotVarExp *)e1; - s = dve->var->isFuncDeclaration(); - tiargs = NULL; - tthis = dve->e1->type; - goto Lfd; - } - else if (e1->op == TOKvar && e1->type && e1->type->toBasetype()->ty == Tfunction) +void expandTuples(Expressions *exps) +{ + //printf("expandTuples()\n"); + if (exps) { - s = ((VarExp *)e1)->var->isFuncDeclaration(); - tiargs = NULL; - tthis = NULL; - Lfd: - assert(s); - if (e2) + for (size_t i = 0; i < exps->length; i++) { - e2 = expressionSemantic(e2, sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - - Expressions a; - a.push(e2); + Expression *arg = (*exps)[i]; + if (!arg) + continue; - FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1); - if (fd && fd->type) - { - if (fd->errors) - return new ErrorExp(); - assert(fd->type->ty == Tfunction); - Expression *e = new CallExp(loc, e1, e2); - return expressionSemantic(e, sc); - } - } - { - FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, NULL, 1); - if (fd && fd->type) + // Look for tuple with 0 members + if (arg->op == TOKtype) { - if (fd->errors) - return new ErrorExp(); - assert(fd->type->ty == Tfunction); - TypeFunction *tf = (TypeFunction *)fd->type; - if (!e2 || tf->isref) + TypeExp *e = (TypeExp *)arg; + if (e->type->toBasetype()->ty == Ttuple) { - Expression *e = new CallExp(loc, e1); - if (e2) - e = new AssignExp(loc, e, e2); - return expressionSemantic(e, sc); - } - } - } - if (FuncDeclaration *fd = s->isFuncDeclaration()) - { - // Keep better diagnostic message for invalid property usage of functions - assert(fd->type->ty == Tfunction); - Expression *e = new CallExp(loc, e1, e2); - return expressionSemantic(e, sc); - } - if (e2) - goto Leprop; - } - if (e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e1; - VarDeclaration *v = ve->var->isVarDeclaration(); - if (v && ve->checkPurity(sc, v)) - return new ErrorExp(); - } - if (e2) - return NULL; - - if (e1->type && - e1->op != TOKtype) // function type is not a property - { - /* Look for e1 being a lazy parameter; rewrite as delegate call - */ - if (e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e1; + TypeTuple *tt = (TypeTuple *)e->type->toBasetype(); - if (ve->var->storage_class & STClazy) - { - Expression *e = new CallExp(loc, e1); - return expressionSemantic(e, sc); + if (!tt->arguments || tt->arguments->length == 0) + { + exps->remove(i); + if (i == exps->length) + return; + i--; + continue; + } + } } - } - else if (e1->op == TOKdotvar) - { - // Check for reading overlapped pointer field in @safe code. - if (checkUnsafeAccess(sc, e1, true, true)) - return new ErrorExp(); - } - else if (e1->op == TOKdot) - { - e1->error("expression has no value"); - return new ErrorExp(); - } - else if (e1->op == TOKcall) - { - CallExp *ce = (CallExp *)e1; - // Check for reading overlapped pointer field in @safe code. - if (checkUnsafeAccess(sc, ce->e1, true, true)) - return new ErrorExp(); - } - } - - if (!e1->type) - { - error(loc, "cannot resolve type for %s", e1->toChars()); - e1 = new ErrorExp(); - } - return e1; - -Leprop: - error(loc, "not a property %s", e1->toChars()); - return new ErrorExp(); - -Leproplvalue: - error(loc, "%s is not an lvalue", e1->toChars()); - return new ErrorExp(); -} - -Expression *resolveProperties(Scope *sc, Expression *e) -{ - //printf("resolveProperties(%s)\n", e->toChars()); - - e = resolvePropertiesX(sc, e); - if (e->checkRightThis(sc)) - return new ErrorExp(); - return e; -} - -/****************************** - * Check the tail CallExp is really property function call. - */ -static bool checkPropertyCall(Expression *e) -{ - while (e->op == TOKcomma) - e = ((CommaExp *)e)->e2; - if (e->op == TOKcall) - { - CallExp *ce = (CallExp *)e; - TypeFunction *tf; - if (ce->f) - { - tf = (TypeFunction *)ce->f->type; - /* If a forward reference to ce->f, try to resolve it - */ - if (!tf->deco && ce->f->semanticRun < PASSsemanticdone) + // Inline expand all the tuples + while (arg->op == TOKtuple) { - dsymbolSemantic(ce->f, NULL); - tf = (TypeFunction *)ce->f->type; + TupleExp *te = (TupleExp *)arg; + exps->remove(i); // remove arg + exps->insert(i, te->exps); // replace with tuple contents + if (i == exps->length) + return; // empty tuple, no more arguments + (*exps)[i] = Expression::combine(te->e0, (*exps)[i]); + arg = (*exps)[i]; } } - else if (ce->e1->type->ty == Tfunction) - tf = (TypeFunction *)ce->e1->type; - else if (ce->e1->type->ty == Tdelegate) - tf = (TypeFunction *)ce->e1->type->nextOf(); - else if (ce->e1->type->ty == Tpointer && ce->e1->type->nextOf()->ty == Tfunction) - tf = (TypeFunction *)ce->e1->type->nextOf(); - else - assert(0); } - return false; } -/****************************** - * If e1 is a property function (template), resolve it. +/**************************************** + * Expand alias this tuples. */ -Expression *resolvePropertiesOnly(Scope *sc, Expression *e1) +TupleDeclaration *isAliasThisTuple(Expression *e) { - //printf("e1 = %s %s\n", Token::toChars(e1->op), e1->toChars()); - OverloadSet *os; - FuncDeclaration *fd; - TemplateDeclaration *td; + if (!e->type) + return NULL; - if (e1->op == TOKdot) - { - DotExp *de = (DotExp *)e1; - if (de->e2->op == TOKoverloadset) - { - os = ((OverExp *)de->e2)->vars; - goto Los; - } - } - else if (e1->op == TOKoverloadset) - { - os = ((OverExp *)e1)->vars; - Los: - assert(os); - for (size_t i = 0; i < os->a.length; i++) - { - Dsymbol *s = os->a[i]; - fd = s->isFuncDeclaration(); - td = s->isTemplateDeclaration(); - if (fd) - { - if (((TypeFunction *)fd->type)->isproperty) - return resolveProperties(sc, e1); - } - else if (td && td->onemember && - (fd = td->onemember->isFuncDeclaration()) != NULL) - { - if (((TypeFunction *)fd->type)->isproperty || - (fd->storage_class2 & STCproperty) || - (td->_scope->stc & STCproperty)) - { - return resolveProperties(sc, e1); - } - } - } - } - else if (e1->op == TOKdotti) - { - DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; - if (dti->ti->tempdecl && (td = dti->ti->tempdecl->isTemplateDeclaration()) != NULL) - goto Ltd; - } - else if (e1->op == TOKdottd) - { - td = ((DotTemplateExp *)e1)->td; - goto Ltd; - } - else if (e1->op == TOKscope) - { - Dsymbol *s = ((ScopeExp *)e1)->sds; - TemplateInstance *ti = s->isTemplateInstance(); - if (ti && !ti->semanticRun && ti->tempdecl) - { - if ((td = ti->tempdecl->isTemplateDeclaration()) != NULL) - goto Ltd; - } - } - else if (e1->op == TOKtemplate) + Type *t = e->type->toBasetype(); +Lagain: + if (Dsymbol *s = t->toDsymbol(NULL)) { - td = ((TemplateExp *)e1)->td; - Ltd: - assert(td); - if (td->onemember && - (fd = td->onemember->isFuncDeclaration()) != NULL) + AggregateDeclaration *ad = s->isAggregateDeclaration(); + if (ad) { - if (((TypeFunction *)fd->type)->isproperty || - (fd->storage_class2 & STCproperty) || - (td->_scope->stc & STCproperty)) + s = ad->aliasthis; + if (s && s->isVarDeclaration()) { - return resolveProperties(sc, e1); + TupleDeclaration *td = s->isVarDeclaration()->toAlias()->isTupleDeclaration(); + if (td && td->isexp) + return td; } - } - } - else if (e1->op == TOKdotvar && e1->type->ty == Tfunction) - { - DotVarExp *dve = (DotVarExp *)e1; - fd = dve->var->isFuncDeclaration(); - goto Lfd; - } - else if (e1->op == TOKvar && e1->type->ty == Tfunction && - (sc->intypeof || !((VarExp *)e1)->var->needThis())) - { - fd = ((VarExp *)e1)->var->isFuncDeclaration(); - Lfd: - assert(fd); - if (((TypeFunction *)fd->type)->isproperty) - return resolveProperties(sc, e1); - } - return e1; -} - - -// TODO: merge with Scope::search::searchScopes() -static Dsymbol *searchScopes(Scope *sc, Loc loc, Identifier *ident, int flags) -{ - Dsymbol *s = NULL; - for (Scope *scx = sc; scx; scx = scx->enclosing) - { - if (!scx->scopesym) - continue; - if (scx->scopesym->isModule()) - flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed - s = scx->scopesym->search(loc, ident, flags); - if (s) - { - // overload set contains only module scope symbols. - if (s->isOverloadSet()) - break; - // selective/renamed imports also be picked up - if (AliasDeclaration *ad = s->isAliasDeclaration()) + if (Type *att = t->aliasthisOf()) { - if (ad->_import) - break; + t = att; + goto Lagain; } - // See only module scope symbols for UFCS target. - Dsymbol *p = s->toParent2(); - if (p && p->isModule()) - break; } - s = NULL; - - // Stop when we hit a module, but keep going if that is not just under the global scope - if (scx->scopesym->isModule() && !(scx->enclosing && !scx->enclosing->enclosing)) - break; } - return s; + return NULL; } -/****************************** - * Find symbol in accordance with the UFCS name look up rule - */ - -static Expression *searchUFCS(Scope *sc, UnaExp *ue, Identifier *ident) +int expandAliasThisTuples(Expressions *exps, size_t starti) { - //printf("searchUFCS(ident = %s)\n", ident->toChars()); - Loc loc = ue->loc; - int flags = 0; - Dsymbol *s = NULL; - - if (sc->flags & SCOPEignoresymbolvisibility) - flags |= IgnoreSymbolVisibility; - - // First look in local scopes - s = searchScopes(sc, loc, ident, flags | SearchLocalsOnly); - if (!s) - { - // Second look in imported modules - s = searchScopes(sc, loc, ident, flags | SearchImportsOnly); - } - - if (!s) - return ue->e1->type->Type::getProperty(loc, ident, 0); + if (!exps || exps->length == 0) + return -1; - FuncDeclaration *f = s->isFuncDeclaration(); - if (f) + for (size_t u = starti; u < exps->length; u++) { - TemplateDeclaration *td = getFuncTemplateDecl(f); - if (td) - { - if (td->overroot) - td = td->overroot; - s = td; - } - } - - if (ue->op == TOKdotti) - { - DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ue; - TemplateInstance *ti = new TemplateInstance(loc, s->ident); - ti->tiargs = dti->ti->tiargs; // for better diagnostic message - if (!ti->updateTempDecl(sc, s)) - return new ErrorExp(); - return new ScopeExp(loc, ti); - } - else - { - //printf("-searchUFCS() %s\n", s->toChars()); - return new DsymbolExp(loc, s); - } -} - -/****************************** - * check e is exp.opDispatch!(tiargs) or not - * It's used to switch to UFCS the semantic analysis path - */ - -bool isDotOpDispatch(Expression *e) -{ - return e->op == TOKdotti && - ((DotTemplateInstanceExp *)e)->ti->name == Id::opDispatch; -} - -/****************************** - * Pull out callable entity with UFCS. - */ - -Expression *resolveUFCS(Scope *sc, CallExp *ce) -{ - Loc loc = ce->loc; - Expression *eleft; - Expression *e; - - if (ce->e1->op == TOKdotid) - { - DotIdExp *die = (DotIdExp *)ce->e1; - Identifier *ident = die->ident; - - Expression *ex = semanticX(die, sc); - if (ex != die) - { - ce->e1 = ex; - return NULL; - } - eleft = die->e1; - - Type *t = eleft->type->toBasetype(); - if (t->ty == Tarray || t->ty == Tsarray || - t->ty == Tnull || (t->isTypeBasic() && t->ty != Tvoid)) - { - /* Built-in types and arrays have no callable properties, so do shortcut. - * It is necessary in: e.init() - */ - } - else if (t->ty == Taarray) - { - if (ident == Id::remove) - { - /* Transform: - * aa.remove(arg) into delete aa[arg] - */ - if (!ce->arguments || ce->arguments->length != 1) - { - ce->error("expected key as argument to aa.remove()"); - return new ErrorExp(); - } - if (!eleft->type->isMutable()) - { - ce->error("cannot remove key from %s associative array %s", - MODtoChars(t->mod), eleft->toChars()); - return new ErrorExp(); - } - Expression *key = (*ce->arguments)[0]; - key = expressionSemantic(key, sc); - key = resolveProperties(sc, key); - - TypeAArray *taa = (TypeAArray *)t; - key = key->implicitCastTo(sc, taa->index); - - if (key->checkValue()) - return new ErrorExp(); - - semanticTypeInfo(sc, taa->index); - - return new RemoveExp(loc, eleft, key); - } - } - else - { - if (Expression *ey = semanticY(die, sc, 1)) - { - if (ey->op == TOKerror) - return ey; - ce->e1 = ey; - if (isDotOpDispatch(ey)) - { - unsigned errors = global.startGagging(); - e = expressionSemantic(ce->syntaxCopy(), sc); - if (!global.endGagging(errors)) - return e; - /* fall down to UFCS */ - } - else - return NULL; - } - } - e = searchUFCS(sc, die, ident); - } - else if (ce->e1->op == TOKdotti) - { - DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ce->e1; - if (Expression *ey = semanticY(dti, sc, 1)) - { - ce->e1 = ey; - return NULL; - } - eleft = dti->e1; - e = searchUFCS(sc, dti, dti->ti->name); - } - else - return NULL; - - // Rewrite - ce->e1 = e; - if (!ce->arguments) - ce->arguments = new Expressions(); - ce->arguments->shift(eleft); - - return NULL; -} - -/****************************** - * Pull out property with UFCS. - */ - -Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL) -{ - Loc loc = e1->loc; - Expression *eleft; - Expression *e; - - if (e1->op == TOKdotid) - { - DotIdExp *die = (DotIdExp *)e1; - eleft = die->e1; - e = searchUFCS(sc, die, die->ident); - } - else if (e1->op == TOKdotti) - { - DotTemplateInstanceExp *dti; - dti = (DotTemplateInstanceExp *)e1; - eleft = dti->e1; - e = searchUFCS(sc, dti, dti->ti->name); - } - else - return NULL; - - if (e == NULL) - return NULL; - - // Rewrite - if (e2) - { - // run semantic without gagging - e2 = expressionSemantic(e2, sc); - - /* f(e1) = e2 - */ - Expression *ex = e->copy(); - Expressions *a1 = new Expressions(); - a1->setDim(1); - (*a1)[0] = eleft; - ex = new CallExp(loc, ex, a1); - ex = trySemantic(ex, sc); - - /* f(e1, e2) - */ - Expressions *a2 = new Expressions(); - a2->setDim(2); - (*a2)[0] = eleft; - (*a2)[1] = e2; - e = new CallExp(loc, e, a2); - if (ex) - { // if fallback setter exists, gag errors - e = trySemantic(e, sc); - if (!e) - { checkPropertyCall(ex); - ex = new AssignExp(loc, ex, e2); - return expressionSemantic(ex, sc); - } - } - else - { // strict setter prints errors if fails - e = expressionSemantic(e, sc); - } - checkPropertyCall(e); - return e; - } - else - { - /* f(e1) - */ - Expressions *arguments = new Expressions(); - arguments->setDim(1); - (*arguments)[0] = eleft; - e = new CallExp(loc, e, arguments); - e = expressionSemantic(e, sc); - checkPropertyCall(e); - return expressionSemantic(e, sc); - } -} - -/****************************** - * Perform semantic() on an array of Expressions. - */ - -bool arrayExpressionSemantic(Expressions *exps, Scope *sc, bool preserveErrors) -{ - bool err = false; - if (exps) - { - for (size_t i = 0; i < exps->length; i++) - { - Expression *e = (*exps)[i]; - if (e) - { - e = expressionSemantic(e, sc); - if (e->op == TOKerror) - err = true; - if (preserveErrors || e->op != TOKerror) - (*exps)[i] = e; - } - } - } - return err; -} - -/**************************************** - * Expand tuples. - * Input: - * exps aray of Expressions - * Output: - * exps rewritten in place - */ - -void expandTuples(Expressions *exps) -{ - //printf("expandTuples()\n"); - if (exps) - { - for (size_t i = 0; i < exps->length; i++) - { - Expression *arg = (*exps)[i]; - if (!arg) - continue; - - // Look for tuple with 0 members - if (arg->op == TOKtype) - { - TypeExp *e = (TypeExp *)arg; - if (e->type->toBasetype()->ty == Ttuple) - { - TypeTuple *tt = (TypeTuple *)e->type->toBasetype(); - - if (!tt->arguments || tt->arguments->length == 0) - { - exps->remove(i); - if (i == exps->length) - return; - i--; - continue; - } - } - } - - // Inline expand all the tuples - while (arg->op == TOKtuple) - { - TupleExp *te = (TupleExp *)arg; - exps->remove(i); // remove arg - exps->insert(i, te->exps); // replace with tuple contents - if (i == exps->length) - return; // empty tuple, no more arguments - (*exps)[i] = Expression::combine(te->e0, (*exps)[i]); - arg = (*exps)[i]; - } - } - } -} - -/**************************************** - * Expand alias this tuples. - */ - -TupleDeclaration *isAliasThisTuple(Expression *e) -{ - if (!e->type) - return NULL; - - Type *t = e->type->toBasetype(); -Lagain: - if (Dsymbol *s = t->toDsymbol(NULL)) - { - AggregateDeclaration *ad = s->isAggregateDeclaration(); - if (ad) - { - s = ad->aliasthis; - if (s && s->isVarDeclaration()) - { - TupleDeclaration *td = s->isVarDeclaration()->toAlias()->isTupleDeclaration(); - if (td && td->isexp) - return td; - } - if (Type *att = t->aliasthisOf()) - { - t = att; - goto Lagain; - } - } - } - return NULL; -} - -int expandAliasThisTuples(Expressions *exps, size_t starti) -{ - if (!exps || exps->length == 0) - return -1; - - for (size_t u = starti; u < exps->length; u++) - { - Expression *exp = (*exps)[u]; - TupleDeclaration *td = isAliasThisTuple(exp); + Expression *exp = (*exps)[u]; + TupleDeclaration *td = isAliasThisTuple(exp); if (td) { exps->remove(u); @@ -1061,907 +256,130 @@ int expandAliasThisTuples(Expressions *exps, size_t starti) } } - return -1; -} - -/**************************************** - * The common type is determined by applying ?: to each pair. - * Output: - * exps[] properties resolved, implicitly cast to common type, rewritten in place - * *pt if pt is not NULL, set to the common type - * Returns: - * true a semantic error was detected - */ - -bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt) -{ - /* Still have a problem with: - * ubyte[][] = [ cast(ubyte[])"hello", [1]]; - * which works if the array literal is initialized top down with the ubyte[][] - * type, but fails with this function doing bottom up typing. - */ - //printf("arrayExpressionToCommonType()\n"); - IntegerExp integerexp(0); - CondExp condexp(Loc(), &integerexp, NULL, NULL); - - Type *t0 = NULL; - Expression *e0 = NULL; // dead-store to prevent spurious warning - size_t j0 = ~0; // dead-store to prevent spurious warning - bool foundType = false; - - for (size_t i = 0; i < exps->length; i++) - { - Expression *e = (*exps)[i]; - if (!e) - continue; - - e = resolveProperties(sc, e); - if (!e->type) - { - e->error("%s has no value", e->toChars()); - t0 = Type::terror; - continue; - } - if (e->op == TOKtype) - { - foundType = true; // do not break immediately, there might be more errors - e->checkValue(); // report an error "type T has no value" - t0 = Type::terror; - continue; - } - if (e->type->ty == Tvoid) - { - // void expressions do not concur to the determination of the common - // type. - continue; - } - if (checkNonAssignmentArrayOp(e)) - { - t0 = Type::terror; - continue; - } - - e = doCopyOrMove(sc, e); - - if (!foundType && t0 && !t0->equals(e->type)) - { - /* This applies ?: to merge the types. It's backwards; - * ?: should call this function to merge types. - */ - condexp.type = NULL; - condexp.e1 = e0; - condexp.e2 = e; - condexp.loc = e->loc; - Expression *ex = expressionSemantic(&condexp, sc); - if (ex->op == TOKerror) - e = ex; - else - { - (*exps)[j0] = condexp.e1; - e = condexp.e2; - } - } - j0 = i; - e0 = e; - t0 = e->type; - if (e->op != TOKerror) - (*exps)[i] = e; - } - - if (!t0) - t0 = Type::tvoid; // [] is typed as void[] - else if (t0->ty != Terror) - { - for (size_t i = 0; i < exps->length; i++) - { - Expression *e = (*exps)[i]; - if (!e) - continue; - - e = e->implicitCastTo(sc, t0); - //assert(e->op != TOKerror); - if (e->op == TOKerror) - { - /* Bugzilla 13024: a workaround for the bug in typeMerge - - * it should paint e1 and e2 by deduced common type, - * but doesn't in this particular case. - */ - t0 = Type::terror; - break; - } - (*exps)[i] = e; - } - } - if (pt) - *pt = t0; - - return (t0 == Type::terror); -} - -/**************************************** - * Get TemplateDeclaration enclosing FuncDeclaration. - */ - -TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s) -{ - FuncDeclaration *f = s->isFuncDeclaration(); - if (f && f->parent) - { - TemplateInstance *ti = f->parent->isTemplateInstance(); - if (ti && !ti->isTemplateMixin() && - ti->tempdecl && ((TemplateDeclaration *)ti->tempdecl)->onemember && - ti->tempdecl->ident == f->ident) - { - return (TemplateDeclaration *)ti->tempdecl; - } - } - return NULL; -} - -/************************************************ - * If we want the value of this expression, but do not want to call - * the destructor on it. - */ - -Expression *valueNoDtor(Expression *e) -{ - if (e->op == TOKcall) - { - /* The struct value returned from the function is transferred - * so do not call the destructor on it. - * Recognize: - * ((S _ctmp = S.init), _ctmp).this(...) - * and make sure the destructor is not called on _ctmp - * BUG: if e is a CommaExp, we should go down the right side. - */ - CallExp *ce = (CallExp *)e; - if (ce->e1->op == TOKdotvar) - { - DotVarExp *dve = (DotVarExp *)ce->e1; - if (dve->var->isCtorDeclaration()) - { - // It's a constructor call - if (dve->e1->op == TOKcomma) - { - CommaExp *comma = (CommaExp *)dve->e1; - if (comma->e2->op == TOKvar) - { - VarExp *ve = (VarExp *)comma->e2; - VarDeclaration *ctmp = ve->var->isVarDeclaration(); - if (ctmp) - { - ctmp->storage_class |= STCnodtor; - assert(!ce->isLvalue()); - } - } - } - } - } - } - else if (e->op == TOKvar) - { - VarDeclaration *vtmp = ((VarExp *)e)->var->isVarDeclaration(); - if (vtmp && vtmp->storage_class & STCrvalue) - { - vtmp->storage_class |= STCnodtor; - } - } - return e; -} - -/******************************************** - * Issue an error if default construction is disabled for type t. - * Default construction is required for arrays and 'out' parameters. - * Returns: - * true an error was issued - */ -bool checkDefCtor(Loc loc, Type *t) -{ - t = t->baseElemOf(); - if (t->ty == Tstruct) - { - StructDeclaration *sd = ((TypeStruct *)t)->sym; - if (sd->noDefaultCtor) - { - sd->error(loc, "default construction is disabled"); - return true; - } - } - return false; -} - -/********************************************* - * If e is an instance of a struct, and that struct has a copy constructor, - * rewrite e as: - * (tmp = e),tmp - * Input: - * sc just used to specify the scope of created temporary variable - */ -Expression *callCpCtor(Scope *sc, Expression *e) -{ - Type *tv = e->type->baseElemOf(); - if (tv->ty == Tstruct) - { - StructDeclaration *sd = ((TypeStruct *)tv)->sym; - if (sd->postblit) - { - /* Create a variable tmp, and replace the argument e with: - * (tmp = e),tmp - * and let AssignExp() handle the construction. - * This is not the most efficent, ideally tmp would be constructed - * directly onto the stack. - */ - VarDeclaration *tmp = copyToTemp(STCrvalue, "__copytmp", e); - tmp->storage_class |= STCnodtor; - dsymbolSemantic(tmp, sc); - Expression *de = new DeclarationExp(e->loc, tmp); - Expression *ve = new VarExp(e->loc, tmp); - de->type = Type::tvoid; - ve->type = e->type; - e = Expression::combine(de, ve); - } - } - return e; -} - -/************************************************ - * Handle the postblit call on lvalue, or the move of rvalue. - */ -Expression *doCopyOrMove(Scope *sc, Expression *e) -{ - if (e->op == TOKquestion) - { - CondExp *ce = (CondExp *)e; - ce->e1 = doCopyOrMove(sc, ce->e1); - ce->e2 = doCopyOrMove(sc, ce->e2); - } - else - { - e = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); - } - return e; -} - -/**************************************** - * Now that we know the exact type of the function we're calling, - * the arguments[] need to be adjusted: - * 1. implicitly convert argument to the corresponding parameter type - * 2. add default arguments for any missing arguments - * 3. do default promotions on arguments corresponding to ... - * 4. add hidden _arguments[] argument - * 5. call copy constructor for struct value arguments - * Input: - * tf type of the function - * fd the function being called, NULL if called indirectly - * Output: - * *prettype return type of function - * *peprefix expression to execute before arguments[] are evaluated, NULL if none - * Returns: - * true errors happened - */ - -bool functionParameters(Loc loc, Scope *sc, TypeFunction *tf, - Type *tthis, Expressions *arguments, FuncDeclaration *fd, Type **prettype, Expression **peprefix) -{ - //printf("functionParameters()\n"); - assert(arguments); - assert(fd || tf->next); - size_t nargs = arguments ? arguments->length : 0; - size_t nparams = tf->parameterList.length(); - unsigned olderrors = global.errors; - bool err = false; - *prettype = Type::terror; - Expression *eprefix = NULL; - *peprefix = NULL; - - if (nargs > nparams && tf->parameterList.varargs == VARARGnone) - { - error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", (ulonglong)nparams, (ulonglong)nargs, tf->toChars()); - return true; - } - - // If inferring return type, and semantic3() needs to be run if not already run - if (!tf->next && fd->inferRetType) - { - fd->functionSemantic(); - } - else if (fd && fd->parent) - { - TemplateInstance *ti = fd->parent->isTemplateInstance(); - if (ti && ti->tempdecl) - { - fd->functionSemantic3(); - } - } - bool isCtorCall = fd && fd->needThis() && fd->isCtorDeclaration(); - - size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) - - /* If the function return type has wildcards in it, we'll need to figure out the actual type - * based on the actual argument types. - */ - MOD wildmatch = 0; - if (tthis && tf->isWild() && !isCtorCall) - { - Type *t = tthis; - if (t->isImmutable()) - wildmatch = MODimmutable; - else if (t->isWildConst()) - wildmatch = MODwildconst; - else if (t->isWild()) - wildmatch = MODwild; - else if (t->isConst()) - wildmatch = MODconst; - else - wildmatch = MODmutable; - } - - int done = 0; - for (size_t i = 0; i < n; i++) - { - Expression *arg; - - if (i < nargs) - arg = (*arguments)[i]; - else - arg = NULL; - - if (i < nparams) - { - Parameter *p = tf->parameterList[i]; - - if (!arg) - { - if (!p->defaultArg) - { - if (tf->parameterList.varargs == VARARGtypesafe && i + 1 == nparams) - goto L2; - error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs); - return true; - } - arg = p->defaultArg; - arg = inlineCopy(arg, sc); - // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ - arg = arg->resolveLoc(loc, sc); - arguments->push(arg); - nargs++; - } - - if (tf->parameterList.varargs == VARARGtypesafe && i + 1 == nparams) - { - //printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars()); - { - MATCH m; - if ((m = arg->implicitConvTo(p->type)) > MATCHnomatch) - { - if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf()) >= m) - goto L2; - else if (nargs != nparams) - { error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs); - return true; - } - goto L1; - } - } - L2: - Type *tb = p->type->toBasetype(); - Type *tret = p->isLazyArray(); - switch (tb->ty) - { - case Tsarray: - case Tarray: - { - /* Create a static array variable v of type arg->type: - * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; - * - * The array literal in the initializer of the hidden variable - * is now optimized. See Bugzilla 2356. - */ - Type *tbn = ((TypeArray *)tb)->next; - - Expressions *elements = new Expressions(); - elements->setDim(nargs - i); - for (size_t u = 0; u < elements->length; u++) - { - Expression *a = (*arguments)[i + u]; - if (tret && a->implicitConvTo(tret)) - { - a = a->implicitCastTo(sc, tret); - a = a->optimize(WANTvalue); - a = toDelegate(a, a->type, sc); - } - else - a = a->implicitCastTo(sc, tbn); - (*elements)[u] = a; - } - // Bugzilla 14395: Convert to a static array literal, or its slice. - arg = new ArrayLiteralExp(loc, tbn->sarrayOf(nargs - i), elements); - if (tb->ty == Tarray) - { - arg = new SliceExp(loc, arg, NULL, NULL); - arg->type = p->type; - } - break; - } - case Tclass: - { - /* Set arg to be: - * new Tclass(arg0, arg1, ..., argn) - */ - Expressions *args = new Expressions(); - args->setDim(nargs - i); - for (size_t u = i; u < nargs; u++) - (*args)[u - i] = (*arguments)[u]; - arg = new NewExp(loc, NULL, NULL, p->type, args); - break; - } - default: - if (!arg) - { - error(loc, "not enough arguments"); - return true; - } - break; - } - arg = expressionSemantic(arg, sc); - //printf("\targ = '%s'\n", arg->toChars()); - arguments->setDim(i + 1); - (*arguments)[i] = arg; - nargs = i + 1; - done = 1; - } - - L1: - if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) - { - bool isRef = (p->storageClass & (STCref | STCout)) != 0; - if (unsigned char wm = arg->type->deduceWild(p->type, isRef)) - { - if (wildmatch) - wildmatch = MODmerge(wildmatch, wm); - else - wildmatch = wm; - //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p->type->toChars(), arg->type->toChars(), wm, wildmatch); - } - } - } - if (done) - break; - } - if ((wildmatch == MODmutable || wildmatch == MODimmutable) && - tf->next->hasWild() && - (tf->isref || !tf->next->implicitConvTo(tf->next->immutableOf()))) - { - if (fd) - { - /* If the called function may return the reference to - * outer inout data, it should be rejected. - * - * void foo(ref inout(int) x) { - * ref inout(int) bar(inout(int)) { return x; } - * struct S { ref inout(int) bar() inout { return x; } } - * bar(int.init) = 1; // bad! - * S().bar() = 1; // bad! - * } - */ - Dsymbol *s = NULL; - if (fd->isThis() || fd->isNested()) - s = fd->toParent2(); - for (; s; s = s->toParent2()) - { - if (AggregateDeclaration *ad = s->isAggregateDeclaration()) - { - if (ad->isNested()) - continue; - break; - } - if (FuncDeclaration *ff = s->isFuncDeclaration()) - { - if (((TypeFunction *)ff->type)->iswild) - goto Linouterr; - - if (ff->isNested() || ff->isThis()) - continue; - } - break; - } - } - else if (tf->isWild()) - { - Linouterr: - const char *s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch); - error(loc, "modify inout to %s is not allowed inside inout function", s); - return true; - } - } - - assert(nargs >= nparams); - for (size_t i = 0; i < nargs; i++) - { - Expression *arg = (*arguments)[i]; - assert(arg); - if (i < nparams) - { - Parameter *p = tf->parameterList[i]; - - if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) - { - Type *tprm = p->type; - if (p->type->hasWild()) - tprm = p->type->substWildTo(wildmatch); - if (!tprm->equals(arg->type)) - { - //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars()); - arg = arg->implicitCastTo(sc, tprm); - arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0); - } - } - if (p->storageClass & STCref) - { - arg = arg->toLvalue(sc, arg); - - // Look for mutable misaligned pointer, etc., in @safe mode - err |= checkUnsafeAccess(sc, arg, false, true); - } - else if (p->storageClass & STCout) - { - Type *t = arg->type; - if (!t->isMutable() || !t->isAssignable()) // check blit assignable - { - arg->error("cannot modify struct %s with immutable members", arg->toChars()); - err = true; - } - else - { - // Look for misaligned pointer, etc., in @safe mode - err |= checkUnsafeAccess(sc, arg, false, true); - err |= checkDefCtor(arg->loc, t); // t must be default constructible - } - arg = arg->toLvalue(sc, arg); - } - else if (p->storageClass & STClazy) - { - // Convert lazy argument to a delegate - if (p->type->ty == Tvoid) - arg = toDelegate(arg, p->type, sc); - else - arg = toDelegate(arg, arg->type, sc); - } - - //printf("arg: %s\n", arg->toChars()); - //printf("type: %s\n", arg->type->toChars()); - if (tf->parameterEscapes(p)) - { - /* Argument value can escape from the called function. - * Check arg to see if it matters. - */ - if (global.params.vsafe) - err |= checkParamArgumentEscape(sc, fd, p->ident, arg, false); - } - else - { - /* Argument value cannot escape from the called function. - */ - Expression *a = arg; - if (a->op == TOKcast) - a = ((CastExp *)a)->e1; - - if (a->op == TOKfunction) - { - /* Function literals can only appear once, so if this - * appearance was scoped, there cannot be any others. - */ - FuncExp *fe = (FuncExp *)a; - fe->fd->tookAddressOf = 0; - } - else if (a->op == TOKdelegate) - { - /* For passing a delegate to a scoped parameter, - * this doesn't count as taking the address of it. - * We only worry about 'escaping' references to the function. - */ - DelegateExp *de = (DelegateExp *)a; - if (de->e1->op == TOKvar) - { VarExp *ve = (VarExp *)de->e1; - FuncDeclaration *f = ve->var->isFuncDeclaration(); - if (f) - { f->tookAddressOf--; - //printf("tookAddressOf = %d\n", f->tookAddressOf); - } - } - } - } - arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0); - } - else - { - // These will be the trailing ... arguments - - // If not D linkage, do promotions - if (tf->linkage != LINKd) - { - // Promote bytes, words, etc., to ints - arg = integralPromotions(arg, sc); - - // Promote floats to doubles - switch (arg->type->ty) - { - case Tfloat32: - arg = arg->castTo(sc, Type::tfloat64); - break; - - case Timaginary32: - arg = arg->castTo(sc, Type::timaginary64); - break; - } - - if (tf->parameterList.varargs == VARARGvariadic) - { - const char *p = tf->linkage == LINKc ? "extern(C)" : "extern(C++)"; - if (arg->type->ty == Tarray) - { - arg->error("cannot pass dynamic arrays to %s vararg functions", p); - err = true; - } - if (arg->type->ty == Tsarray) - { - arg->error("cannot pass static arrays to %s vararg functions", p); - err = true; - } - } - } - - // Do not allow types that need destructors - if (arg->type->needsDestruction()) - { - arg->error("cannot pass types that need destruction as variadic arguments"); - err = true; - } - - // Convert static arrays to dynamic arrays - // BUG: I don't think this is right for D2 - Type *tb = arg->type->toBasetype(); - if (tb->ty == Tsarray) - { - TypeSArray *ts = (TypeSArray *)tb; - Type *ta = ts->next->arrayOf(); - if (ts->size(arg->loc) == 0) - arg = new NullExp(arg->loc, ta); - else - arg = arg->castTo(sc, ta); - } - if (tb->ty == Tstruct) - { - //arg = callCpCtor(sc, arg); - } - - // Give error for overloaded function addresses - if (arg->op == TOKsymoff) - { SymOffExp *se = (SymOffExp *)arg; - if (se->hasOverloads && - !se->var->isFuncDeclaration()->isUnique()) - { arg->error("function %s is overloaded", arg->toChars()); - err = true; - } - } - if (arg->checkValue()) - err = true; - arg = arg->optimize(WANTvalue); - } - (*arguments)[i] = arg; - } - - /* Remaining problems: - * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is - * implemented by calling a function) we'll defer this for now. - * 2. value structs (or static arrays of them) that need to be copy constructed - * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the - * function gets called (functions normally destroy their parameters) - * 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned - * up properly. Pushing arguments on the stack then cannot fail. - */ - if (1) - { - /* TODO: tackle problem 1) - */ - const bool leftToRight = true; // TODO: something like !fd.isArrayOp - if (!leftToRight) - assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity + return -1; +} - const ptrdiff_t start = (leftToRight ? 0 : (ptrdiff_t)nargs - 1); - const ptrdiff_t end = (leftToRight ? (ptrdiff_t)nargs : -1); - const ptrdiff_t step = (leftToRight ? 1 : -1); +/**************************************** + * Get TemplateDeclaration enclosing FuncDeclaration. + */ - /* Compute indices of last throwing argument and first arg needing destruction. - * Used to not set up destructors unless an arg needs destruction on a throw - * in a later argument. - */ - ptrdiff_t lastthrow = -1; - ptrdiff_t firstdtor = -1; - for (ptrdiff_t i = start; i != end; i += step) +TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s) +{ + FuncDeclaration *f = s->isFuncDeclaration(); + if (f && f->parent) + { + TemplateInstance *ti = f->parent->isTemplateInstance(); + if (ti && !ti->isTemplateMixin() && + ti->tempdecl && ((TemplateDeclaration *)ti->tempdecl)->onemember && + ti->tempdecl->ident == f->ident) { - Expression *arg = (*arguments)[i]; - if (canThrow(arg, sc->func, false)) - lastthrow = i; - if (firstdtor == -1 && arg->type->needsDestruction()) - { - Parameter *p = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[i]); - if (!(p && (p->storageClass & (STClazy | STCref | STCout)))) - firstdtor = i; - } + return (TemplateDeclaration *)ti->tempdecl; } + } + return NULL; +} - /* Does problem 3) apply to this call? - */ - const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0 - && (lastthrow - firstdtor) * step > 0); +/************************************************ + * If we want the value of this expression, but do not want to call + * the destructor on it. + */ - /* If so, initialize 'eprefix' by declaring the gate +Expression *valueNoDtor(Expression *e) +{ + if (e->op == TOKcall) + { + /* The struct value returned from the function is transferred + * so do not call the destructor on it. + * Recognize: + * ((S _ctmp = S.init), _ctmp).this(...) + * and make sure the destructor is not called on _ctmp + * BUG: if e is a CommaExp, we should go down the right side. */ - VarDeclaration *gate = NULL; - if (needsPrefix) - { - // eprefix => bool __gate [= false] - Identifier *idtmp = Identifier::generateId("__gate"); - gate = new VarDeclaration(loc, Type::tbool, idtmp, NULL); - gate->storage_class |= STCtemp | STCctfe | STCvolatile; - dsymbolSemantic(gate, sc); - - Expression *ae = new DeclarationExp(loc, gate); - eprefix = expressionSemantic(ae, sc); - } - - for (ptrdiff_t i = start; i != end; i += step) + CallExp *ce = (CallExp *)e; + if (ce->e1->op == TOKdotvar) { - Expression *arg = (*arguments)[i]; - - Parameter *parameter = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[i]); - const bool isRef = (parameter && (parameter->storageClass & (STCref | STCout))); - const bool isLazy = (parameter && (parameter->storageClass & STClazy)); - - /* Skip lazy parameters - */ - if (isLazy) - continue; - - /* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg. - * Declare a temporary variable for this arg and append that declaration to 'eprefix', - * which will implicitly take care of potential problem 2) for this arg. - * 'eprefix' will therefore finally contain all args up to and including the last - * potentially throwing arg, excluding all lazy parameters. - */ - if (gate) + DotVarExp *dve = (DotVarExp *)ce->e1; + if (dve->var->isCtorDeclaration()) { - const bool needsDtor = (!isRef && arg->type->needsDestruction() && i != lastthrow); - - /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor) - */ - VarDeclaration *tmp = copyToTemp(0, - needsDtor ? "__pfx" : "__pfy", - !isRef ? arg : arg->addressOf()); - dsymbolSemantic(tmp, sc); - - /* Modify the destructor so it only runs if gate==false, i.e., - * only if there was a throw while constructing the args - */ - if (!needsDtor) + // It's a constructor call + if (dve->e1->op == TOKcomma) { - if (tmp->edtor) + CommaExp *comma = (CommaExp *)dve->e1; + if (comma->e2->op == TOKvar) { - assert(i == lastthrow); - tmp->edtor = NULL; + VarExp *ve = (VarExp *)comma->e2; + VarDeclaration *ctmp = ve->var->isVarDeclaration(); + if (ctmp) + { + ctmp->storage_class |= STCnodtor; + assert(!ce->isLvalue()); + } } } - else - { - // edtor => (__gate || edtor) - assert(tmp->edtor); - Expression *e = tmp->edtor; - e = new LogicalExp(e->loc, TOKoror, new VarExp(e->loc, gate), e); - tmp->edtor = expressionSemantic(e, sc); - //printf("edtor: %s\n", tmp->edtor->toChars()); - } - - // eprefix => (eprefix, auto __pfx/y = arg) - DeclarationExp *ae = new DeclarationExp(loc, tmp); - eprefix = Expression::combine(eprefix, expressionSemantic(ae, sc)); - - // arg => __pfx/y - arg = new VarExp(loc, tmp); - arg = expressionSemantic(arg, sc); - if (isRef) - { - arg = new PtrExp(loc, arg); - arg = expressionSemantic(arg, sc); - } - - /* Last throwing arg? Then finalize eprefix => (eprefix, gate = true), - * i.e., disable the dtors right after constructing the last throwing arg. - * From now on, the callee will take care of destructing the args because - * the args are implicitly moved into function parameters. - * - * Set gate to null to let the next iterations know they don't need to - * append to eprefix anymore. - */ - if (i == lastthrow) - { - Expression *e = new AssignExp(gate->loc, new VarExp(gate->loc, gate), new IntegerExp(gate->loc, 1, Type::tbool)); - eprefix = Expression::combine(eprefix, expressionSemantic(e, sc)); - gate = NULL; - } - } - else - { - /* No gate, no prefix to append to. - * Handle problem 2) by calling the copy constructor for value structs - * (or static arrays of them) if appropriate. - */ - Type *tv = arg->type->baseElemOf(); - if (!isRef && tv->ty == Tstruct) - arg = doCopyOrMove(sc, arg); } - - (*arguments)[i] = arg; } } - //if (eprefix) printf("eprefix: %s\n", eprefix->toChars()); - - // If D linkage and variadic, add _arguments[] as first argument - if (tf->isDstyleVariadic()) + else if (e->op == TOKvar) { - assert(arguments->length >= nparams); - - Parameters *args = new Parameters; - args->setDim(arguments->length - nparams); - for (size_t i = 0; i < arguments->length - nparams; i++) + VarDeclaration *vtmp = ((VarExp *)e)->var->isVarDeclaration(); + if (vtmp && vtmp->storage_class & STCrvalue) { - Parameter *arg = new Parameter(STCin, (*arguments)[nparams + i]->type, NULL, NULL, NULL); - (*args)[i] = arg; + vtmp->storage_class |= STCnodtor; } - - TypeTuple *tup = new TypeTuple(args); - Expression *e = new TypeidExp(loc, tup); - e = expressionSemantic(e, sc); - arguments->insert(0, e); } + return e; +} - Type *tret = tf->next; - if (isCtorCall) +/********************************************* + * If e is an instance of a struct, and that struct has a copy constructor, + * rewrite e as: + * (tmp = e),tmp + * Input: + * sc just used to specify the scope of created temporary variable + */ +Expression *callCpCtor(Scope *sc, Expression *e) +{ + Type *tv = e->type->baseElemOf(); + if (tv->ty == Tstruct) { - //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd->toChars(), fd->type->toChars(), - // wildmatch, tf->isWild(), fd->isolateReturn()); - if (!tthis) - { - assert(sc->intypeof || global.errors); - tthis = fd->isThis()->type->addMod(fd->type->mod); - } - if (tf->isWild() && !fd->isolateReturn()) + StructDeclaration *sd = ((TypeStruct *)tv)->sym; + if (sd->postblit) { - if (wildmatch) - tret = tret->substWildTo(wildmatch); - int offset; - if (!tret->implicitConvTo(tthis) && - !(MODimplicitConv(tret->mod, tthis->mod) && tret->isBaseOf(tthis, &offset) && offset == 0)) - { - const char* s1 = tret ->isNaked() ? " mutable" : tret ->modToChars(); - const char* s2 = tthis->isNaked() ? " mutable" : tthis->modToChars(); - ::error(loc, "inout constructor %s creates%s object, not%s", - fd->toPrettyChars(), s1, s2); - err = true; - } + /* Create a variable tmp, and replace the argument e with: + * (tmp = e),tmp + * and let AssignExp() handle the construction. + * This is not the most efficent, ideally tmp would be constructed + * directly onto the stack. + */ + VarDeclaration *tmp = copyToTemp(STCrvalue, "__copytmp", e); + tmp->storage_class |= STCnodtor; + dsymbolSemantic(tmp, sc); + Expression *de = new DeclarationExp(e->loc, tmp); + Expression *ve = new VarExp(e->loc, tmp); + de->type = Type::tvoid; + ve->type = e->type; + e = Expression::combine(de, ve); } - tret = tthis; } - else if (wildmatch && tret) + return e; +} + +/************************************************ + * Handle the postblit call on lvalue, or the move of rvalue. + */ +Expression *doCopyOrMove(Scope *sc, Expression *e) +{ + if (e->op == TOKquestion) + { + CondExp *ce = (CondExp *)e; + ce->e1 = doCopyOrMove(sc, ce->e1); + ce->e2 = doCopyOrMove(sc, ce->e2); + } + else { - /* Adjust function return type based on wildmatch - */ - //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars()); - tret = tret->substWildTo(wildmatch); + e = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); } - *prettype = tret; - *peprefix = eprefix; - return (err || olderrors != global.errors); + return e; } /******************************** Expression **************************/ @@ -5288,8 +3706,10 @@ MATCH FuncExp::matchType(Type *to, Scope *sc, FuncExp **presult, int flag) } else if (!flag) { + const char *ts[2]; + toAutoQualChars(ts, tx, to); error("cannot implicitly convert expression (%s) of type %s to %s", - toChars(), tx->toChars(), to->toChars()); + toChars(), ts[0], ts[1]); } return m; } @@ -5644,11 +4064,17 @@ Expression *BinExp::incompatibleTypes() error("incompatible types for ((%s) %s (%s)): cannot use `%s` with types", e1->toChars(), Token::toChars(thisOp), e2->toChars(), Token::toChars(op)); } + else if (e1->type->equals(e2->type)) + { + error("incompatible types for ((%s) %s (%s)): both operands are of type `%s`", + e1->toChars(), Token::toChars(thisOp), e2->toChars(), e1->type->toChars()); + } else { + const char *ts[2]; + toAutoQualChars(ts, e1->type, e2->type); error("incompatible types for ((%s) %s (%s)): `%s` and `%s`", - e1->toChars(), Token::toChars(thisOp), e2->toChars(), - e1->type->toChars(), e2->type->toChars()); + e1->toChars(), Token::toChars(thisOp), e2->toChars(), ts[0], ts[1]); } return new ErrorExp(); } @@ -5693,9 +4119,36 @@ Expression *BinAssignExp::modifiableLvalue(Scope *sc, Expression *) /************************************************************/ -CompileExp::CompileExp(Loc loc, Expression *e) - : UnaExp(loc, TOKmixin, sizeof(CompileExp), e) +CompileExp::CompileExp(Loc loc, Expressions *exps) + : Expression(loc, TOKmixin, sizeof(CompileExp)) +{ + this->exps = exps; +} + +Expression *CompileExp::syntaxCopy() +{ + return new CompileExp(loc, arraySyntaxCopy(exps)); +} + +bool CompileExp::equals(RootObject *o) { + if (this == o) + return true; + if (o && o->dyncast() == DYNCAST_EXPRESSION && ((Expression *)o)->op == TOKmixin) + { + CompileExp *ce = (CompileExp *)o; + if (exps->length != ce->exps->length) + return false; + for (size_t i = 0; i < exps->length; i++) + { + Expression *e1 = (*exps)[i]; + Expression *e2 = (*ce->exps)[i]; + if (e1 != e2 && (!e1 || !e2 || !e1->equals(e2))) + return false; + } + return true; + } + return false; } /************************************************************/ @@ -6362,64 +4815,6 @@ ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1) { } -Expression *opAssignToOp(Loc loc, TOK op, Expression *e1, Expression *e2) -{ Expression *e; - - switch (op) - { - case TOKaddass: e = new AddExp(loc, e1, e2); break; - case TOKminass: e = new MinExp(loc, e1, e2); break; - case TOKmulass: e = new MulExp(loc, e1, e2); break; - case TOKdivass: e = new DivExp(loc, e1, e2); break; - case TOKmodass: e = new ModExp(loc, e1, e2); break; - case TOKandass: e = new AndExp(loc, e1, e2); break; - case TOKorass: e = new OrExp (loc, e1, e2); break; - case TOKxorass: e = new XorExp(loc, e1, e2); break; - case TOKshlass: e = new ShlExp(loc, e1, e2); break; - case TOKshrass: e = new ShrExp(loc, e1, e2); break; - case TOKushrass: e = new UshrExp(loc, e1, e2); break; - default: assert(0); - } - return e; -} - -/********************* - * Rewrite: - * array.length op= e2 - * as: - * array.length = array.length op e2 - * or: - * auto tmp = &array; - * (*tmp).length = (*tmp).length op e2 - */ - -Expression *ArrayLengthExp::rewriteOpAssign(BinExp *exp) -{ - Expression *e; - - assert(exp->e1->op == TOKarraylength); - ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1; - if (ale->e1->op == TOKvar) - { - e = opAssignToOp(exp->loc, exp->op, ale, exp->e2); - e = new AssignExp(exp->loc, ale->syntaxCopy(), e); - } - else - { - /* auto tmp = &array; - * (*tmp).length = (*tmp).length op e2 - */ - VarDeclaration *tmp = copyToTemp(0, "__arraylength", new AddrExp(ale->loc, ale->e1)); - - Expression *e1 = new ArrayLengthExp(ale->loc, new PtrExp(ale->loc, new VarExp(ale->loc, tmp))); - Expression *elvalue = e1->syntaxCopy(); - e = opAssignToOp(exp->loc, exp->op, e1, exp->e2); - e = new AssignExp(exp->loc, elvalue, e); - e = new CommaExp(exp->loc, new DeclarationExp(ale->loc, tmp), e); - } - return e; -} - /*********************** IntervalExp ********************************/ // Mainly just a placeholder @@ -7248,203 +5643,6 @@ Expression *PrettyFuncInitExp::resolveLoc(Loc loc, Scope *sc) return e; } -/****************************************************************/ - -Expression *extractOpDollarSideEffect(Scope *sc, UnaExp *ue) -{ - Expression *e0; - Expression *e1 = Expression::extractLast(ue->e1, &e0); - // Bugzilla 12585: Extract the side effect part if ue->e1 is comma. - - if (!isTrivialExp(e1)) - { - /* Even if opDollar is needed, 'e1' should be evaluate only once. So - * Rewrite: - * e1.opIndex( ... use of $ ... ) - * e1.opSlice( ... use of $ ... ) - * as: - * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...) - * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...) - */ - e1 = extractSideEffect(sc, "__dop", &e0, e1, false); - assert(e1->op == TOKvar); - VarExp *ve = (VarExp *)e1; - ve->var->storage_class |= STCexptemp; // lifetime limited to expression - } - ue->e1 = e1; - return e0; -} - -/************************************** - * Runs semantic on ae->arguments. Declares temporary variables - * if '$' was used. - */ -Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0) -{ - assert(!ae->lengthVar); - - *pe0 = NULL; - - AggregateDeclaration *ad = isAggregate(ae->e1->type); - Dsymbol *slice = search_function(ad, Id::slice); - //printf("slice = %s %s\n", slice->kind(), slice->toChars()); - - for (size_t i = 0; i < ae->arguments->length; i++) - { - if (i == 0) - *pe0 = extractOpDollarSideEffect(sc, ae); - - Expression *e = (*ae->arguments)[i]; - if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration())) - { - Lfallback: - if (ae->arguments->length == 1) - return NULL; - ae->error("multi-dimensional slicing requires template opSlice"); - return new ErrorExp(); - } - //printf("[%d] e = %s\n", i, e->toChars()); - - // Create scope for '$' variable for this dimension - ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); - sym->loc = ae->loc; - sym->parent = sc->scopesym; - sc = sc->push(sym); - ae->lengthVar = NULL; // Create it only if required - ae->currentDimension = i; // Dimension for $, if required - - e = expressionSemantic(e, sc); - e = resolveProperties(sc, e); - - if (ae->lengthVar && sc->func) - { - // If $ was used, declare it now - Expression *de = new DeclarationExp(ae->loc, ae->lengthVar); - de = expressionSemantic(de, sc); - *pe0 = Expression::combine(*pe0, de); - } - sc = sc->pop(); - - if (e->op == TOKinterval) - { - IntervalExp *ie = (IntervalExp *)e; - - Objects *tiargs = new Objects(); - Expression *edim = new IntegerExp(ae->loc, i, Type::tsize_t); - edim = expressionSemantic(edim, sc); - tiargs->push(edim); - - Expressions *fargs = new Expressions(); - fargs->push(ie->lwr); - fargs->push(ie->upr); - - unsigned xerrors = global.startGagging(); - sc = sc->push(); - FuncDeclaration *fslice = resolveFuncCall(ae->loc, sc, slice, tiargs, ae->e1->type, fargs, 1); - sc = sc->pop(); - global.endGagging(xerrors); - if (!fslice) - goto Lfallback; - - e = new DotTemplateInstanceExp(ae->loc, ae->e1, slice->ident, tiargs); - e = new CallExp(ae->loc, e, fargs); - e = expressionSemantic(e, sc); - } - - if (!e->type) - { - ae->error("%s has no value", e->toChars()); - e = new ErrorExp(); - } - if (e->op == TOKerror) - return e; - - (*ae->arguments)[i] = e; - } - - return ae; -} - -/*********************************************************** - * Resolve `exp` as a compile-time known string. - * Params: - * sc = scope - * exp = Expression which expected as a string - * s = What the string is expected for, will be used in error diagnostic. - * Returns: - * String literal, or `null` if error happens. - */ -StringExp *semanticString(Scope *sc, Expression *exp, const char *s) -{ - sc = sc->startCTFE(); - exp = expressionSemantic(exp, sc); - exp = resolveProperties(sc, exp); - sc = sc->endCTFE(); - - if (exp->op == TOKerror) - return NULL; - - Expression *e = exp; - if (exp->type->isString()) - { - e = e->ctfeInterpret(); - if (e->op == TOKerror) - return NULL; - } - - StringExp *se = e->toStringExp(); - if (!se) - { - exp->error("string expected for %s, not (%s) of type %s", - s, exp->toChars(), exp->type->toChars()); - return NULL; - } - return se; -} - -/************************************** - * Runs semantic on se->lwr and se->upr. Declares a temporary variable - * if '$' was used. - */ -Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0) -{ - //assert(!ae->lengthVar); - if (!ie) - return ae; - - VarDeclaration *lengthVar = ae->lengthVar; - - // create scope for '$' - ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); - sym->loc = ae->loc; - sym->parent = sc->scopesym; - sc = sc->push(sym); - - for (size_t i = 0; i < 2; ++i) - { - Expression *e = i == 0 ? ie->lwr : ie->upr; - e = expressionSemantic(e, sc); - e = resolveProperties(sc, e); - if (!e->type) - { - ae->error("%s has no value", e->toChars()); - return new ErrorExp(); - } - (i == 0 ? ie->lwr : ie->upr) = e; - } - - if (lengthVar != ae->lengthVar && sc->func) - { - // If $ was used, declare it now - Expression *de = new DeclarationExp(ae->loc, ae->lengthVar); - de = expressionSemantic(de, sc); - *pe0 = Expression::combine(*pe0, de); - } - sc = sc->pop(); - - return ae; -} - Expression *BinExp::reorderSettingAAElem(Scope *sc) { BinExp *be = this; diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index f0ae2805df4..2ed8fac373e 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -883,10 +883,14 @@ public: /****************************************************************/ -class CompileExp : public UnaExp +class CompileExp : public Expression { public: - CompileExp(Loc loc, Expression *e); + Expressions *exps; + + CompileExp(Loc loc, Expressions *exps); + Expression *syntaxCopy(); + bool equals(RootObject *o); void accept(Visitor *v) { v->visit(this); } }; @@ -1124,7 +1128,6 @@ class ArrayLengthExp : public UnaExp public: ArrayLengthExp(Loc loc, Expression *e1); - static Expression *rewriteOpAssign(BinExp *exp); void accept(Visitor *v) { v->visit(this); } }; diff --git a/gcc/d/dmd/expressionsem.c b/gcc/d/dmd/expressionsem.c index 7cccd956966..4a37d0fcba2 100644 --- a/gcc/d/dmd/expressionsem.c +++ b/gcc/d/dmd/expressionsem.c @@ -47,26 +47,1252 @@ bool symbolIsVisible(Scope *sc, Dsymbol *s); VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expression *e, bool alwaysCopy = false); Type *getTypeInfoType(Loc loc, Type *t, Scope *sc); +char *MODtoChars(MOD mod); bool MODimplicitConv(MOD modfrom, MOD modto); +MOD MODmerge(MOD mod1, MOD mod2); MATCH MODmethodConv(MOD modfrom, MOD modto); void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod); void unSpeculative(Scope *sc, RootObject *o); -bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt); -bool checkDefCtor(Loc loc, Type *t); bool isDotOpDispatch(Expression *e); -bool functionParameters(Loc loc, Scope *sc, TypeFunction *tf, Type *tthis, Expressions *arguments, FuncDeclaration *fd, Type **prettype, Expression **peprefix); -Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad, Expression *e1, Declaration *var, int flag = 0); bool isNeedThisScope(Scope *sc, Declaration *d); -Expression *resolveUFCS(Scope *sc, CallExp *ce); bool checkUnsafeAccess(Scope *sc, Expression *e, bool readonly, bool printmsg); bool isSafeCast(Expression *e, Type *tfrom, Type *tto); FuncDeclaration *isFuncAddress(Expression *e, bool *hasOverloads = NULL); Expression *callCpCtor(Scope *sc, Expression *e); Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); -Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL); -Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL); + +bool checkPrintfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list); +bool checkScanfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list); + +/******************************************************** + * Perform semantic analysis and CTFE on expressions to produce + * a string. + * Params: + * buf = append generated string to buffer + * sc = context + * exps = array of Expressions + * Returns: + * true on error + */ +bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps) +{ + if (!exps) + return false; + + for (size_t i = 0; i < exps->length; i++) + { + Expression *ex = (*exps)[i]; + if (!ex) + continue; + Scope *sc2 = sc->startCTFE(); + Expression *e2 = expressionSemantic(ex, sc2); + Expression *e3 = resolveProperties(sc2, e2); + sc2->endCTFE(); + + // allowed to contain types as well as expressions + Expression *e4 = ctfeInterpretForPragmaMsg(e3); + if (!e4 || e4->op == TOKerror) + return true; + + // expand tuple + if (TupleExp *te = e4->isTupleExp()) + { + if (expressionsToString(buf, sc, te->exps)) + return true; + continue; + } + // char literals exp `.toStringExp` return `null` but we cant override it + // because in most contexts we don't want the conversion to succeed. + IntegerExp *ie = e4->isIntegerExp(); + const TY ty = (ie && ie->type) ? ie->type->ty : (TY)Terror; + if (ty == Tchar || ty == Twchar || ty == Tdchar) + { + TypeSArray *tsa = new TypeSArray(ie->type, new IntegerExp(ex->loc, 1, Type::tint32)); + e4 = new ArrayLiteralExp(ex->loc, tsa, ie); + } + + if (StringExp *se = e4->toStringExp()) + buf.writestring(se->toUTF8(sc)->toPtr()); + else + buf.writestring(e4->toChars()); + } + return false; +} + +/*********************************************************** + * Resolve `exp` as a compile-time known string. + * Params: + * sc = scope + * exp = Expression which expected as a string + * s = What the string is expected for, will be used in error diagnostic. + * Returns: + * String literal, or `null` if error happens. + */ +StringExp *semanticString(Scope *sc, Expression *exp, const char *s) +{ + sc = sc->startCTFE(); + exp = expressionSemantic(exp, sc); + exp = resolveProperties(sc, exp); + sc = sc->endCTFE(); + + if (exp->op == TOKerror) + return NULL; + + Expression *e = exp; + if (exp->type->isString()) + { + e = e->ctfeInterpret(); + if (e->op == TOKerror) + return NULL; + } + + StringExp *se = e->toStringExp(); + if (!se) + { + exp->error("string expected for %s, not (%s) of type %s", + s, exp->toChars(), exp->type->toChars()); + return NULL; + } + return se; +} + +/****************************************************************/ + +static Expression *extractOpDollarSideEffect(Scope *sc, UnaExp *ue) +{ + Expression *e0; + Expression *e1 = Expression::extractLast(ue->e1, &e0); + // Bugzilla 12585: Extract the side effect part if ue->e1 is comma. + + if (!isTrivialExp(e1)) + { + /* Even if opDollar is needed, 'e1' should be evaluate only once. So + * Rewrite: + * e1.opIndex( ... use of $ ... ) + * e1.opSlice( ... use of $ ... ) + * as: + * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...) + * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...) + */ + e1 = extractSideEffect(sc, "__dop", &e0, e1, false); + assert(e1->op == TOKvar); + VarExp *ve = (VarExp *)e1; + ve->var->storage_class |= STCexptemp; // lifetime limited to expression + } + ue->e1 = e1; + return e0; +} + +/************************************** + * Runs semantic on ae->arguments. Declares temporary variables + * if '$' was used. + */ +Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0) +{ + assert(!ae->lengthVar); + + *pe0 = NULL; + + AggregateDeclaration *ad = isAggregate(ae->e1->type); + Dsymbol *slice = search_function(ad, Id::slice); + //printf("slice = %s %s\n", slice->kind(), slice->toChars()); + + for (size_t i = 0; i < ae->arguments->length; i++) + { + if (i == 0) + *pe0 = extractOpDollarSideEffect(sc, ae); + + Expression *e = (*ae->arguments)[i]; + if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration())) + { + Lfallback: + if (ae->arguments->length == 1) + return NULL; + ae->error("multi-dimensional slicing requires template opSlice"); + return new ErrorExp(); + } + //printf("[%d] e = %s\n", i, e->toChars()); + + // Create scope for '$' variable for this dimension + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); + sym->loc = ae->loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + ae->lengthVar = NULL; // Create it only if required + ae->currentDimension = i; // Dimension for $, if required + + e = expressionSemantic(e, sc); + e = resolveProperties(sc, e); + + if (ae->lengthVar && sc->func) + { + // If $ was used, declare it now + Expression *de = new DeclarationExp(ae->loc, ae->lengthVar); + de = expressionSemantic(de, sc); + *pe0 = Expression::combine(*pe0, de); + } + sc = sc->pop(); + + if (e->op == TOKinterval) + { + IntervalExp *ie = (IntervalExp *)e; + + Objects *tiargs = new Objects(); + Expression *edim = new IntegerExp(ae->loc, i, Type::tsize_t); + edim = expressionSemantic(edim, sc); + tiargs->push(edim); + + Expressions *fargs = new Expressions(); + fargs->push(ie->lwr); + fargs->push(ie->upr); + + unsigned xerrors = global.startGagging(); + sc = sc->push(); + FuncDeclaration *fslice = resolveFuncCall(ae->loc, sc, slice, tiargs, ae->e1->type, fargs, 1); + sc = sc->pop(); + global.endGagging(xerrors); + if (!fslice) + goto Lfallback; + + e = new DotTemplateInstanceExp(ae->loc, ae->e1, slice->ident, tiargs); + e = new CallExp(ae->loc, e, fargs); + e = expressionSemantic(e, sc); + } + + if (!e->type) + { + ae->error("%s has no value", e->toChars()); + e = new ErrorExp(); + } + if (e->op == TOKerror) + return e; + + (*ae->arguments)[i] = e; + } + + return ae; +} + +/************************************** + * Runs semantic on se->lwr and se->upr. Declares a temporary variable + * if '$' was used. + */ +Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0) +{ + //assert(!ae->lengthVar); + if (!ie) + return ae; + + VarDeclaration *lengthVar = ae->lengthVar; + + // create scope for '$' + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); + sym->loc = ae->loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + + for (size_t i = 0; i < 2; ++i) + { + Expression *e = i == 0 ? ie->lwr : ie->upr; + e = expressionSemantic(e, sc); + e = resolveProperties(sc, e); + if (!e->type) + { + ae->error("%s has no value", e->toChars()); + return new ErrorExp(); + } + (i == 0 ? ie->lwr : ie->upr) = e; + } + + if (lengthVar != ae->lengthVar && sc->func) + { + // If $ was used, declare it now + Expression *de = new DeclarationExp(ae->loc, ae->lengthVar); + de = expressionSemantic(de, sc); + *pe0 = Expression::combine(*pe0, de); + } + sc = sc->pop(); + + return ae; +} + +/****************************** + * Perform semantic() on an array of Expressions. + */ + +bool arrayExpressionSemantic(Expressions *exps, Scope *sc, bool preserveErrors) +{ + bool err = false; + if (exps) + { + for (size_t i = 0; i < exps->length; i++) + { + Expression *e = (*exps)[i]; + if (e) + { + e = expressionSemantic(e, sc); + if (e->op == TOKerror) + err = true; + if (preserveErrors || e->op != TOKerror) + (*exps)[i] = e; + } + } + } + return err; +} + +/****************************** + * Check the tail CallExp is really property function call. + */ +static bool checkPropertyCall(Expression *e) +{ + while (e->op == TOKcomma) + e = ((CommaExp *)e)->e2; + + if (e->op == TOKcall) + { + CallExp *ce = (CallExp *)e; + TypeFunction *tf; + if (ce->f) + { + tf = (TypeFunction *)ce->f->type; + /* If a forward reference to ce->f, try to resolve it + */ + if (!tf->deco && ce->f->semanticRun < PASSsemanticdone) + { + dsymbolSemantic(ce->f, NULL); + tf = (TypeFunction *)ce->f->type; + } + } + else if (ce->e1->type->ty == Tfunction) + tf = (TypeFunction *)ce->e1->type; + else if (ce->e1->type->ty == Tdelegate) + tf = (TypeFunction *)ce->e1->type->nextOf(); + else if (ce->e1->type->ty == Tpointer && ce->e1->type->nextOf()->ty == Tfunction) + tf = (TypeFunction *)ce->e1->type->nextOf(); + else + assert(0); + } + return false; +} + +// TODO: merge with Scope::search::searchScopes() +static Dsymbol *searchScopes(Scope *sc, Loc loc, Identifier *ident, int flags) +{ + Dsymbol *s = NULL; + for (Scope *scx = sc; scx; scx = scx->enclosing) + { + if (!scx->scopesym) + continue; + if (scx->scopesym->isModule()) + flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed + s = scx->scopesym->search(loc, ident, flags); + if (s) + { + // overload set contains only module scope symbols. + if (s->isOverloadSet()) + break; + // selective/renamed imports also be picked up + if (AliasDeclaration *ad = s->isAliasDeclaration()) + { + if (ad->_import) + break; + } + // See only module scope symbols for UFCS target. + Dsymbol *p = s->toParent2(); + if (p && p->isModule()) + break; + } + s = NULL; + + // Stop when we hit a module, but keep going if that is not just under the global scope + if (scx->scopesym->isModule() && !(scx->enclosing && !scx->enclosing->enclosing)) + break; + } + return s; +} + +/****************************** + * Find symbol in accordance with the UFCS name look up rule + */ + +static Expression *searchUFCS(Scope *sc, UnaExp *ue, Identifier *ident) +{ + //printf("searchUFCS(ident = %s)\n", ident->toChars()); + Loc loc = ue->loc; + int flags = 0; + Dsymbol *s = NULL; + + if (sc->flags & SCOPEignoresymbolvisibility) + flags |= IgnoreSymbolVisibility; + + // First look in local scopes + s = searchScopes(sc, loc, ident, flags | SearchLocalsOnly); + if (!s) + { + // Second look in imported modules + s = searchScopes(sc, loc, ident, flags | SearchImportsOnly); + } + + if (!s) + return ue->e1->type->Type::getProperty(loc, ident, 0); + + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) + { + TemplateDeclaration *td = getFuncTemplateDecl(f); + if (td) + { + if (td->overroot) + td = td->overroot; + s = td; + } + } + + if (ue->op == TOKdotti) + { + DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ue; + TemplateInstance *ti = new TemplateInstance(loc, s->ident); + ti->tiargs = dti->ti->tiargs; // for better diagnostic message + if (!ti->updateTempDecl(sc, s)) + return new ErrorExp(); + return new ScopeExp(loc, ti); + } + else + { + //printf("-searchUFCS() %s\n", s->toChars()); + return new DsymbolExp(loc, s); + } +} + +/****************************** + * Pull out callable entity with UFCS. + */ + +static Expression *resolveUFCS(Scope *sc, CallExp *ce) +{ + Loc loc = ce->loc; + Expression *eleft; + Expression *e; + + if (ce->e1->op == TOKdotid) + { + DotIdExp *die = (DotIdExp *)ce->e1; + Identifier *ident = die->ident; + + Expression *ex = semanticX(die, sc); + if (ex != die) + { + ce->e1 = ex; + return NULL; + } + eleft = die->e1; + + Type *t = eleft->type->toBasetype(); + if (t->ty == Tarray || t->ty == Tsarray || + t->ty == Tnull || (t->isTypeBasic() && t->ty != Tvoid)) + { + /* Built-in types and arrays have no callable properties, so do shortcut. + * It is necessary in: e.init() + */ + } + else if (t->ty == Taarray) + { + if (ident == Id::remove) + { + /* Transform: + * aa.remove(arg) into delete aa[arg] + */ + if (!ce->arguments || ce->arguments->length != 1) + { + ce->error("expected key as argument to aa.remove()"); + return new ErrorExp(); + } + if (!eleft->type->isMutable()) + { + ce->error("cannot remove key from %s associative array %s", + MODtoChars(t->mod), eleft->toChars()); + return new ErrorExp(); + } + Expression *key = (*ce->arguments)[0]; + key = expressionSemantic(key, sc); + key = resolveProperties(sc, key); + + TypeAArray *taa = (TypeAArray *)t; + key = key->implicitCastTo(sc, taa->index); + + if (key->checkValue()) + return new ErrorExp(); + + semanticTypeInfo(sc, taa->index); + + return new RemoveExp(loc, eleft, key); + } + } + else + { + if (Expression *ey = semanticY(die, sc, 1)) + { + if (ey->op == TOKerror) + return ey; + ce->e1 = ey; + if (isDotOpDispatch(ey)) + { + unsigned errors = global.startGagging(); + e = expressionSemantic(ce->syntaxCopy(), sc); + if (!global.endGagging(errors)) + return e; + /* fall down to UFCS */ + } + else + return NULL; + } + } + e = searchUFCS(sc, die, ident); + } + else if (ce->e1->op == TOKdotti) + { + DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ce->e1; + if (Expression *ey = semanticY(dti, sc, 1)) + { + ce->e1 = ey; + return NULL; + } + eleft = dti->e1; + e = searchUFCS(sc, dti, dti->ti->name); + } + else + return NULL; + + // Rewrite + ce->e1 = e; + if (!ce->arguments) + ce->arguments = new Expressions(); + ce->arguments->shift(eleft); + + return NULL; +} + +/****************************** + * Pull out property with UFCS. + */ + +static Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NULL) +{ + Loc loc = e1->loc; + Expression *eleft; + Expression *e; + + if (e1->op == TOKdotid) + { + DotIdExp *die = (DotIdExp *)e1; + eleft = die->e1; + e = searchUFCS(sc, die, die->ident); + } + else if (e1->op == TOKdotti) + { + DotTemplateInstanceExp *dti; + dti = (DotTemplateInstanceExp *)e1; + eleft = dti->e1; + e = searchUFCS(sc, dti, dti->ti->name); + } + else + return NULL; + + if (e == NULL) + return NULL; + + // Rewrite + if (e2) + { + // run semantic without gagging + e2 = expressionSemantic(e2, sc); + + /* f(e1) = e2 + */ + Expression *ex = e->copy(); + Expressions *a1 = new Expressions(); + a1->setDim(1); + (*a1)[0] = eleft; + ex = new CallExp(loc, ex, a1); + ex = trySemantic(ex, sc); + + /* f(e1, e2) + */ + Expressions *a2 = new Expressions(); + a2->setDim(2); + (*a2)[0] = eleft; + (*a2)[1] = e2; + e = new CallExp(loc, e, a2); + if (ex) + { // if fallback setter exists, gag errors + e = trySemantic(e, sc); + if (!e) + { checkPropertyCall(ex); + ex = new AssignExp(loc, ex, e2); + return expressionSemantic(ex, sc); + } + } + else + { // strict setter prints errors if fails + e = expressionSemantic(e, sc); + } + checkPropertyCall(e); + return e; + } + else + { + /* f(e1) + */ + Expressions *arguments = new Expressions(); + arguments->setDim(1); + (*arguments)[0] = eleft; + e = new CallExp(loc, e, arguments); + e = expressionSemantic(e, sc); + checkPropertyCall(e); + return expressionSemantic(e, sc); + } +} + +/****************************** + * If e1 is a property function (template), resolve it. + */ + +Expression *resolvePropertiesOnly(Scope *sc, Expression *e1) +{ + //printf("e1 = %s %s\n", Token::toChars(e1->op), e1->toChars()); + OverloadSet *os; + FuncDeclaration *fd; + TemplateDeclaration *td; + + if (e1->op == TOKdot) + { + DotExp *de = (DotExp *)e1; + if (de->e2->op == TOKoverloadset) + { + os = ((OverExp *)de->e2)->vars; + goto Los; + } + } + else if (e1->op == TOKoverloadset) + { + os = ((OverExp *)e1)->vars; + Los: + assert(os); + for (size_t i = 0; i < os->a.length; i++) + { + Dsymbol *s = os->a[i]; + fd = s->isFuncDeclaration(); + td = s->isTemplateDeclaration(); + if (fd) + { + if (((TypeFunction *)fd->type)->isproperty) + return resolveProperties(sc, e1); + } + else if (td && td->onemember && + (fd = td->onemember->isFuncDeclaration()) != NULL) + { + if (((TypeFunction *)fd->type)->isproperty || + (fd->storage_class2 & STCproperty) || + (td->_scope->stc & STCproperty)) + { + return resolveProperties(sc, e1); + } + } + } + } + else if (e1->op == TOKdotti) + { + DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; + if (dti->ti->tempdecl && (td = dti->ti->tempdecl->isTemplateDeclaration()) != NULL) + goto Ltd; + } + else if (e1->op == TOKdottd) + { + td = ((DotTemplateExp *)e1)->td; + goto Ltd; + } + else if (e1->op == TOKscope) + { + Dsymbol *s = ((ScopeExp *)e1)->sds; + TemplateInstance *ti = s->isTemplateInstance(); + if (ti && !ti->semanticRun && ti->tempdecl) + { + if ((td = ti->tempdecl->isTemplateDeclaration()) != NULL) + goto Ltd; + } + } + else if (e1->op == TOKtemplate) + { + td = ((TemplateExp *)e1)->td; + Ltd: + assert(td); + if (td->onemember && + (fd = td->onemember->isFuncDeclaration()) != NULL) + { + if (((TypeFunction *)fd->type)->isproperty || + (fd->storage_class2 & STCproperty) || + (td->_scope->stc & STCproperty)) + { + return resolveProperties(sc, e1); + } + } + } + else if (e1->op == TOKdotvar && e1->type->ty == Tfunction) + { + DotVarExp *dve = (DotVarExp *)e1; + fd = dve->var->isFuncDeclaration(); + goto Lfd; + } + else if (e1->op == TOKvar && e1->type->ty == Tfunction && + (sc->intypeof || !((VarExp *)e1)->var->needThis())) + { + fd = ((VarExp *)e1)->var->isFuncDeclaration(); + Lfd: + assert(fd); + if (((TypeFunction *)fd->type)->isproperty) + return resolveProperties(sc, e1); + } + return e1; +} + +/************************************************************* + * Given var, we need to get the + * right 'this' pointer if var is in an outer class, but our + * existing 'this' pointer is in an inner class. + * Input: + * e1 existing 'this' + * ad struct or class we need the correct 'this' for + * var the specific member of ad we're accessing + */ + +static Expression *getRightThis(Loc loc, Scope *sc, AggregateDeclaration *ad, + Expression *e1, Declaration *var, int flag = 0) +{ + //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1->toChars(), ad->toChars(), var->toChars()); + L1: + Type *t = e1->type->toBasetype(); + //printf("e1->type = %s, var->type = %s\n", e1->type->toChars(), var->type->toChars()); + + /* If e1 is not the 'this' pointer for ad + */ + if (ad && + !(t->ty == Tpointer && t->nextOf()->ty == Tstruct && + ((TypeStruct *)t->nextOf())->sym == ad) + && + !(t->ty == Tstruct && + ((TypeStruct *)t)->sym == ad) + ) + { + ClassDeclaration *cd = ad->isClassDeclaration(); + ClassDeclaration *tcd = t->isClassHandle(); + + /* e1 is the right this if ad is a base class of e1 + */ + if (!cd || !tcd || + !(tcd == cd || cd->isBaseOf(tcd, NULL)) + ) + { + /* Only classes can be inner classes with an 'outer' + * member pointing to the enclosing class instance + */ + if (tcd && tcd->isNested()) + { + /* e1 is the 'this' pointer for an inner class: tcd. + * Rewrite it as the 'this' pointer for the outer class. + */ + + e1 = new DotVarExp(loc, e1, tcd->vthis); + e1->type = tcd->vthis->type; + e1->type = e1->type->addMod(t->mod); + // Do not call checkNestedRef() + //e1 = expressionSemantic(e1, sc); + + // Skip up over nested functions, and get the enclosing + // class type. + int n = 0; + Dsymbol *s; + for (s = tcd->toParent(); + s && s->isFuncDeclaration(); + s = s->toParent()) + { + FuncDeclaration *f = s->isFuncDeclaration(); + if (f->vthis) + { + //printf("rewriting e1 to %s's this\n", f->toChars()); + n++; + e1 = new VarExp(loc, f->vthis); + } + else + { + e1->error("need `this` of type %s to access member %s" + " from static function %s", + ad->toChars(), var->toChars(), f->toChars()); + e1 = new ErrorExp(); + return e1; + } + } + if (s && s->isClassDeclaration()) + { + e1->type = s->isClassDeclaration()->type; + e1->type = e1->type->addMod(t->mod); + if (n > 1) + e1 = expressionSemantic(e1, sc); + } + else + e1 = expressionSemantic(e1, sc); + goto L1; + } + + /* Can't find a path from e1 to ad + */ + if (flag) + return NULL; + e1->error("this for %s needs to be type %s not type %s", + var->toChars(), ad->toChars(), t->toChars()); + return new ErrorExp(); + } + } + return e1; +} + +/*************************************** + * Pull out any properties. + */ + +static Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) +{ + //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token::toChars(e1->op), e1->toChars(), e2 ? e2->toChars() : NULL); + Loc loc = e1->loc; + + OverloadSet *os; + Dsymbol *s; + Objects *tiargs; + Type *tthis; + if (e1->op == TOKdot) + { + DotExp *de = (DotExp *)e1; + if (de->e2->op == TOKoverloadset) + { + tiargs = NULL; + tthis = de->e1->type; + os = ((OverExp *)de->e2)->vars; + goto Los; + } + } + else if (e1->op == TOKoverloadset) + { + tiargs = NULL; + tthis = NULL; + os = ((OverExp *)e1)->vars; + Los: + assert(os); + FuncDeclaration *fd = NULL; + if (e2) + { + e2 = expressionSemantic(e2, sc); + if (e2->op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); + + Expressions a; + a.push(e2); + + for (size_t i = 0; i < os->a.length; i++) + { + FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, &a, 1); + if (f) + { + if (f->errors) + return new ErrorExp(); + fd = f; + assert(fd->type->ty == Tfunction); + } + } + if (fd) + { + Expression *e = new CallExp(loc, e1, e2); + return expressionSemantic(e, sc); + } + } + { + for (size_t i = 0; i < os->a.length; i++) + { + FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, NULL, 1); + if (f) + { + if (f->errors) + return new ErrorExp(); + fd = f; + assert(fd->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)fd->type; + if (!tf->isref && e2) + goto Leproplvalue; + } + } + if (fd) + { + Expression *e = new CallExp(loc, e1); + if (e2) + e = new AssignExp(loc, e, e2); + return expressionSemantic(e, sc); + } + } + if (e2) + goto Leprop; + } + else if (e1->op == TOKdotti) + { + DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; + if (!dti->findTempDecl(sc)) + goto Leprop; + if (!dti->ti->semanticTiargs(sc)) + goto Leprop; + tiargs = dti->ti->tiargs; + tthis = dti->e1->type; + if ((os = dti->ti->tempdecl->isOverloadSet()) != NULL) + goto Los; + if ((s = dti->ti->tempdecl) != NULL) + goto Lfd; + } + else if (e1->op == TOKdottd) + { + DotTemplateExp *dte = (DotTemplateExp *)e1; + s = dte->td; + tiargs = NULL; + tthis = dte->e1->type; + goto Lfd; + } + else if (e1->op == TOKscope) + { + s = ((ScopeExp *)e1)->sds; + TemplateInstance *ti = s->isTemplateInstance(); + if (ti && !ti->semanticRun && ti->tempdecl) + { + //assert(ti->needsTypeInference(sc)); + if (!ti->semanticTiargs(sc)) + goto Leprop; + tiargs = ti->tiargs; + tthis = NULL; + if ((os = ti->tempdecl->isOverloadSet()) != NULL) + goto Los; + if ((s = ti->tempdecl) != NULL) + goto Lfd; + } + } + else if (e1->op == TOKtemplate) + { + s = ((TemplateExp *)e1)->td; + tiargs = NULL; + tthis = NULL; + goto Lfd; + } + else if (e1->op == TOKdotvar && e1->type && e1->type->toBasetype()->ty == Tfunction) + { + DotVarExp *dve = (DotVarExp *)e1; + s = dve->var->isFuncDeclaration(); + tiargs = NULL; + tthis = dve->e1->type; + goto Lfd; + } + else if (e1->op == TOKvar && e1->type && e1->type->toBasetype()->ty == Tfunction) + { + s = ((VarExp *)e1)->var->isFuncDeclaration(); + tiargs = NULL; + tthis = NULL; + Lfd: + assert(s); + if (e2) + { + e2 = expressionSemantic(e2, sc); + if (e2->op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); + + Expressions a; + a.push(e2); + + FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1); + if (fd && fd->type) + { + if (fd->errors) + return new ErrorExp(); + assert(fd->type->ty == Tfunction); + Expression *e = new CallExp(loc, e1, e2); + return expressionSemantic(e, sc); + } + } + { + FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, NULL, 1); + if (fd && fd->type) + { + if (fd->errors) + return new ErrorExp(); + assert(fd->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)fd->type; + if (!e2 || tf->isref) + { + Expression *e = new CallExp(loc, e1); + if (e2) + e = new AssignExp(loc, e, e2); + return expressionSemantic(e, sc); + } + } + } + if (FuncDeclaration *fd = s->isFuncDeclaration()) + { + // Keep better diagnostic message for invalid property usage of functions + assert(fd->type->ty == Tfunction); + Expression *e = new CallExp(loc, e1, e2); + return expressionSemantic(e, sc); + } + if (e2) + goto Leprop; + } + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v && ve->checkPurity(sc, v)) + return new ErrorExp(); + } + if (e2) + return NULL; + + if (e1->type && + e1->op != TOKtype) // function type is not a property + { + /* Look for e1 being a lazy parameter; rewrite as delegate call + */ + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + + if (ve->var->storage_class & STClazy) + { + Expression *e = new CallExp(loc, e1); + return expressionSemantic(e, sc); + } + } + else if (e1->op == TOKdotvar) + { + // Check for reading overlapped pointer field in @safe code. + if (checkUnsafeAccess(sc, e1, true, true)) + return new ErrorExp(); + } + else if (e1->op == TOKdot) + { + e1->error("expression has no value"); + return new ErrorExp(); + } + else if (e1->op == TOKcall) + { + CallExp *ce = (CallExp *)e1; + // Check for reading overlapped pointer field in @safe code. + if (checkUnsafeAccess(sc, ce->e1, true, true)) + return new ErrorExp(); + } + } + + if (!e1->type) + { + error(loc, "cannot resolve type for %s", e1->toChars()); + e1 = new ErrorExp(); + } + return e1; + +Leprop: + error(loc, "not a property %s", e1->toChars()); + return new ErrorExp(); + +Leproplvalue: + error(loc, "%s is not an lvalue", e1->toChars()); + return new ErrorExp(); +} + +Expression *resolveProperties(Scope *sc, Expression *e) +{ + //printf("resolveProperties(%s)\n", e->toChars()); + + e = resolvePropertiesX(sc, e); + if (e->checkRightThis(sc)) + return new ErrorExp(); + return e; +} + +/**************************************** + * The common type is determined by applying ?: to each pair. + * Output: + * exps[] properties resolved, implicitly cast to common type, rewritten in place + * *pt if pt is not NULL, set to the common type + * Returns: + * true a semantic error was detected + */ + +static bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt) +{ + /* Still have a problem with: + * ubyte[][] = [ cast(ubyte[])"hello", [1]]; + * which works if the array literal is initialized top down with the ubyte[][] + * type, but fails with this function doing bottom up typing. + */ + //printf("arrayExpressionToCommonType()\n"); + IntegerExp integerexp(0); + CondExp condexp(Loc(), &integerexp, NULL, NULL); + + Type *t0 = NULL; + Expression *e0 = NULL; // dead-store to prevent spurious warning + size_t j0 = ~0; // dead-store to prevent spurious warning + bool foundType = false; + + for (size_t i = 0; i < exps->length; i++) + { + Expression *e = (*exps)[i]; + if (!e) + continue; + + e = resolveProperties(sc, e); + if (!e->type) + { + e->error("%s has no value", e->toChars()); + t0 = Type::terror; + continue; + } + if (e->op == TOKtype) + { + foundType = true; // do not break immediately, there might be more errors + e->checkValue(); // report an error "type T has no value" + t0 = Type::terror; + continue; + } + if (e->type->ty == Tvoid) + { + // void expressions do not concur to the determination of the common + // type. + continue; + } + if (checkNonAssignmentArrayOp(e)) + { + t0 = Type::terror; + continue; + } + + e = doCopyOrMove(sc, e); + + if (!foundType && t0 && !t0->equals(e->type)) + { + /* This applies ?: to merge the types. It's backwards; + * ?: should call this function to merge types. + */ + condexp.type = NULL; + condexp.e1 = e0; + condexp.e2 = e; + condexp.loc = e->loc; + Expression *ex = expressionSemantic(&condexp, sc); + if (ex->op == TOKerror) + e = ex; + else + { + (*exps)[j0] = condexp.e1; + e = condexp.e2; + } + } + j0 = i; + e0 = e; + t0 = e->type; + if (e->op != TOKerror) + (*exps)[i] = e; + } + + if (!t0) + t0 = Type::tvoid; // [] is typed as void[] + else if (t0->ty != Terror) + { + for (size_t i = 0; i < exps->length; i++) + { + Expression *e = (*exps)[i]; + if (!e) + continue; + + e = e->implicitCastTo(sc, t0); + //assert(e->op != TOKerror); + if (e->op == TOKerror) + { + /* Bugzilla 13024: a workaround for the bug in typeMerge - + * it should paint e1 and e2 by deduced common type, + * but doesn't in this particular case. + */ + t0 = Type::terror; + break; + } + (*exps)[i] = e; + } + } + if (pt) + *pt = t0; + + return (t0 == Type::terror); +} + +static Expression *opAssignToOp(Loc loc, TOK op, Expression *e1, Expression *e2) +{ Expression *e; + + switch (op) + { + case TOKaddass: e = new AddExp(loc, e1, e2); break; + case TOKminass: e = new MinExp(loc, e1, e2); break; + case TOKmulass: e = new MulExp(loc, e1, e2); break; + case TOKdivass: e = new DivExp(loc, e1, e2); break; + case TOKmodass: e = new ModExp(loc, e1, e2); break; + case TOKandass: e = new AndExp(loc, e1, e2); break; + case TOKorass: e = new OrExp (loc, e1, e2); break; + case TOKxorass: e = new XorExp(loc, e1, e2); break; + case TOKshlass: e = new ShlExp(loc, e1, e2); break; + case TOKshrass: e = new ShrExp(loc, e1, e2); break; + case TOKushrass: e = new UshrExp(loc, e1, e2); break; + default: assert(0); + } + return e; +} + +/********************* + * Rewrite: + * array.length op= e2 + * as: + * array.length = array.length op e2 + * or: + * auto tmp = &array; + * (*tmp).length = (*tmp).length op e2 + */ + +static Expression *rewriteOpAssign(BinExp *exp) +{ + Expression *e; + + assert(exp->e1->op == TOKarraylength); + ArrayLengthExp *ale = (ArrayLengthExp *)exp->e1; + if (ale->e1->op == TOKvar) + { + e = opAssignToOp(exp->loc, exp->op, ale, exp->e2); + e = new AssignExp(exp->loc, ale->syntaxCopy(), e); + } + else + { + /* auto tmp = &array; + * (*tmp).length = (*tmp).length op e2 + */ + VarDeclaration *tmp = copyToTemp(0, "__arraylength", new AddrExp(ale->loc, ale->e1)); + + Expression *e1 = new ArrayLengthExp(ale->loc, new PtrExp(ale->loc, new VarExp(ale->loc, tmp))); + Expression *elvalue = e1->syntaxCopy(); + e = opAssignToOp(exp->loc, exp->op, e1, exp->e2); + e = new AssignExp(exp->loc, elvalue, e); + e = new CommaExp(exp->loc, new DeclarationExp(ale->loc, tmp), e); + } + return e; +} /**************************************** * Preprocess arguments to function. @@ -76,39 +1302,728 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) * true a semantic error occurred */ -static bool preFunctionParameters(Scope *sc, Expressions *exps) -{ - bool err = false; - if (exps) +static bool preFunctionParameters(Scope *sc, Expressions *exps) +{ + bool err = false; + if (exps) + { + expandTuples(exps); + + for (size_t i = 0; i < exps->length; i++) + { + Expression *arg = (*exps)[i]; + + arg = resolveProperties(sc, arg); + if (arg->op == TOKtype) + { + arg->error("cannot pass type %s as a function argument", arg->toChars()); + arg = new ErrorExp(); + err = true; + } + else if (arg->type->toBasetype()->ty == Tfunction) + { + arg->error("cannot pass type %s as a function argument", arg->toChars()); + arg = new ErrorExp(); + err = true; + } + else if (checkNonAssignmentArrayOp(arg)) + { + arg = new ErrorExp(); + err = true; + } + (*exps)[i] = arg; + } + } + return err; +} + +/******************************************** + * Issue an error if default construction is disabled for type t. + * Default construction is required for arrays and 'out' parameters. + * Returns: + * true an error was issued + */ +static bool checkDefCtor(Loc loc, Type *t) +{ + t = t->baseElemOf(); + if (t->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *)t)->sym; + if (sd->noDefaultCtor) + { + sd->error(loc, "default construction is disabled"); + return true; + } + } + return false; +} + +/**************************************** + * Now that we know the exact type of the function we're calling, + * the arguments[] need to be adjusted: + * 1. implicitly convert argument to the corresponding parameter type + * 2. add default arguments for any missing arguments + * 3. do default promotions on arguments corresponding to ... + * 4. add hidden _arguments[] argument + * 5. call copy constructor for struct value arguments + * Input: + * tf type of the function + * fd the function being called, NULL if called indirectly + * Output: + * *prettype return type of function + * *peprefix expression to execute before arguments[] are evaluated, NULL if none + * Returns: + * true errors happened + */ + +static bool functionParameters(Loc loc, Scope *sc, TypeFunction *tf, + Type *tthis, Expressions *arguments, FuncDeclaration *fd, Type **prettype, Expression **peprefix) +{ + //printf("functionParameters()\n"); + assert(arguments); + assert(fd || tf->next); + size_t nargs = arguments ? arguments->length : 0; + size_t nparams = tf->parameterList.length(); + unsigned olderrors = global.errors; + bool err = false; + *prettype = Type::terror; + Expression *eprefix = NULL; + *peprefix = NULL; + + if (nargs > nparams && tf->parameterList.varargs == VARARGnone) + { + error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", (ulonglong)nparams, (ulonglong)nargs, tf->toChars()); + return true; + } + + // If inferring return type, and semantic3() needs to be run if not already run + if (!tf->next && fd->inferRetType) + { + fd->functionSemantic(); + } + else if (fd && fd->parent) + { + TemplateInstance *ti = fd->parent->isTemplateInstance(); + if (ti && ti->tempdecl) + { + fd->functionSemantic3(); + } + } + bool isCtorCall = fd && fd->needThis() && fd->isCtorDeclaration(); + + size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) + + /* If the function return type has wildcards in it, we'll need to figure out the actual type + * based on the actual argument types. + */ + MOD wildmatch = 0; + if (tthis && tf->isWild() && !isCtorCall) + { + Type *t = tthis; + if (t->isImmutable()) + wildmatch = MODimmutable; + else if (t->isWildConst()) + wildmatch = MODwildconst; + else if (t->isWild()) + wildmatch = MODwild; + else if (t->isConst()) + wildmatch = MODconst; + else + wildmatch = MODmutable; + } + + int done = 0; + for (size_t i = 0; i < n; i++) + { + Expression *arg; + + if (i < nargs) + arg = (*arguments)[i]; + else + arg = NULL; + + if (i < nparams) + { + Parameter *p = tf->parameterList[i]; + + if (!arg) + { + if (!p->defaultArg) + { + if (tf->parameterList.varargs == VARARGtypesafe && i + 1 == nparams) + goto L2; + error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs); + return true; + } + arg = p->defaultArg; + arg = inlineCopy(arg, sc); + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg->resolveLoc(loc, sc); + arguments->push(arg); + nargs++; + } + + if (tf->parameterList.varargs == VARARGtypesafe && i + 1 == nparams) + { + //printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars()); + { + MATCH m; + if ((m = arg->implicitConvTo(p->type)) > MATCHnomatch) + { + if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf()) >= m) + goto L2; + else if (nargs != nparams) + { error(loc, "expected %llu function arguments, not %llu", (ulonglong)nparams, (ulonglong)nargs); + return true; + } + goto L1; + } + } + L2: + Type *tb = p->type->toBasetype(); + Type *tret = p->isLazyArray(); + switch (tb->ty) + { + case Tsarray: + case Tarray: + { + /* Create a static array variable v of type arg->type: + * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; + * + * The array literal in the initializer of the hidden variable + * is now optimized. See Bugzilla 2356. + */ + Type *tbn = ((TypeArray *)tb)->next; + + Expressions *elements = new Expressions(); + elements->setDim(nargs - i); + for (size_t u = 0; u < elements->length; u++) + { + Expression *a = (*arguments)[i + u]; + if (tret && a->implicitConvTo(tret)) + { + a = a->implicitCastTo(sc, tret); + a = a->optimize(WANTvalue); + a = toDelegate(a, a->type, sc); + } + else + a = a->implicitCastTo(sc, tbn); + (*elements)[u] = a; + } + // Bugzilla 14395: Convert to a static array literal, or its slice. + arg = new ArrayLiteralExp(loc, tbn->sarrayOf(nargs - i), elements); + if (tb->ty == Tarray) + { + arg = new SliceExp(loc, arg, NULL, NULL); + arg->type = p->type; + } + break; + } + case Tclass: + { + /* Set arg to be: + * new Tclass(arg0, arg1, ..., argn) + */ + Expressions *args = new Expressions(); + args->setDim(nargs - i); + for (size_t u = i; u < nargs; u++) + (*args)[u - i] = (*arguments)[u]; + arg = new NewExp(loc, NULL, NULL, p->type, args); + break; + } + default: + if (!arg) + { + error(loc, "not enough arguments"); + return true; + } + break; + } + arg = expressionSemantic(arg, sc); + //printf("\targ = '%s'\n", arg->toChars()); + arguments->setDim(i + 1); + (*arguments)[i] = arg; + nargs = i + 1; + done = 1; + } + + L1: + if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) + { + bool isRef = (p->storageClass & (STCref | STCout)) != 0; + if (unsigned char wm = arg->type->deduceWild(p->type, isRef)) + { + if (wildmatch) + wildmatch = MODmerge(wildmatch, wm); + else + wildmatch = wm; + //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p->type->toChars(), arg->type->toChars(), wm, wildmatch); + } + } + } + if (done) + break; + } + if ((wildmatch == MODmutable || wildmatch == MODimmutable) && + tf->next->hasWild() && + (tf->isref || !tf->next->implicitConvTo(tf->next->immutableOf()))) { - expandTuples(exps); + if (fd) + { + /* If the called function may return the reference to + * outer inout data, it should be rejected. + * + * void foo(ref inout(int) x) { + * ref inout(int) bar(inout(int)) { return x; } + * struct S { ref inout(int) bar() inout { return x; } } + * bar(int.init) = 1; // bad! + * S().bar() = 1; // bad! + * } + */ + Dsymbol *s = NULL; + if (fd->isThis() || fd->isNested()) + s = fd->toParent2(); + for (; s; s = s->toParent2()) + { + if (AggregateDeclaration *ad = s->isAggregateDeclaration()) + { + if (ad->isNested()) + continue; + break; + } + if (FuncDeclaration *ff = s->isFuncDeclaration()) + { + if (((TypeFunction *)ff->type)->iswild) + goto Linouterr; - for (size_t i = 0; i < exps->length; i++) + if (ff->isNested() || ff->isThis()) + continue; + } + break; + } + } + else if (tf->isWild()) { - Expression *arg = (*exps)[i]; + Linouterr: + const char *s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch); + error(loc, "modify inout to %s is not allowed inside inout function", s); + return true; + } + } - arg = resolveProperties(sc, arg); - if (arg->op == TOKtype) + assert(nargs >= nparams); + for (size_t i = 0; i < nargs; i++) + { + Expression *arg = (*arguments)[i]; + assert(arg); + if (i < nparams) + { + Parameter *p = tf->parameterList[i]; + + if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) { - arg->error("cannot pass type %s as a function argument", arg->toChars()); - arg = new ErrorExp(); + Type *tprm = p->type; + if (p->type->hasWild()) + tprm = p->type->substWildTo(wildmatch); + if (!tprm->equals(arg->type)) + { + //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars()); + arg = arg->implicitCastTo(sc, tprm); + arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0); + } + } + if (p->storageClass & STCref) + { + arg = arg->toLvalue(sc, arg); + + // Look for mutable misaligned pointer, etc., in @safe mode + err |= checkUnsafeAccess(sc, arg, false, true); + } + else if (p->storageClass & STCout) + { + Type *t = arg->type; + if (!t->isMutable() || !t->isAssignable()) // check blit assignable + { + arg->error("cannot modify struct %s with immutable members", arg->toChars()); + err = true; + } + else + { + // Look for misaligned pointer, etc., in @safe mode + err |= checkUnsafeAccess(sc, arg, false, true); + err |= checkDefCtor(arg->loc, t); // t must be default constructible + } + arg = arg->toLvalue(sc, arg); + } + else if (p->storageClass & STClazy) + { + // Convert lazy argument to a delegate + if (p->type->ty == Tvoid) + arg = toDelegate(arg, p->type, sc); + else + arg = toDelegate(arg, arg->type, sc); + } + + //printf("arg: %s\n", arg->toChars()); + //printf("type: %s\n", arg->type->toChars()); + if (tf->parameterEscapes(p)) + { + /* Argument value can escape from the called function. + * Check arg to see if it matters. + */ + if (global.params.vsafe) + err |= checkParamArgumentEscape(sc, fd, p->ident, arg, false); + } + else + { + /* Argument value cannot escape from the called function. + */ + Expression *a = arg; + if (a->op == TOKcast) + a = ((CastExp *)a)->e1; + + if (a->op == TOKfunction) + { + /* Function literals can only appear once, so if this + * appearance was scoped, there cannot be any others. + */ + FuncExp *fe = (FuncExp *)a; + fe->fd->tookAddressOf = 0; + } + else if (a->op == TOKdelegate) + { + /* For passing a delegate to a scoped parameter, + * this doesn't count as taking the address of it. + * We only worry about 'escaping' references to the function. + */ + DelegateExp *de = (DelegateExp *)a; + if (de->e1->op == TOKvar) + { VarExp *ve = (VarExp *)de->e1; + FuncDeclaration *f = ve->var->isFuncDeclaration(); + if (f) + { f->tookAddressOf--; + //printf("tookAddressOf = %d\n", f->tookAddressOf); + } + } + } + } + arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0); + } + else + { + // These will be the trailing ... arguments + + // If not D linkage, do promotions + if (tf->linkage != LINKd) + { + // Promote bytes, words, etc., to ints + arg = integralPromotions(arg, sc); + + // Promote floats to doubles + switch (arg->type->ty) + { + case Tfloat32: + arg = arg->castTo(sc, Type::tfloat64); + break; + + case Timaginary32: + arg = arg->castTo(sc, Type::timaginary64); + break; + } + + if (tf->parameterList.varargs == VARARGvariadic) + { + const char *p = tf->linkage == LINKc ? "extern(C)" : "extern(C++)"; + if (arg->type->ty == Tarray) + { + arg->error("cannot pass dynamic arrays to %s vararg functions", p); + err = true; + } + if (arg->type->ty == Tsarray) + { + arg->error("cannot pass static arrays to %s vararg functions", p); + err = true; + } + } + } + + // Do not allow types that need destructors + if (arg->type->needsDestruction()) + { + arg->error("cannot pass types that need destruction as variadic arguments"); err = true; } - else if (arg->type->toBasetype()->ty == Tfunction) + + // Convert static arrays to dynamic arrays + // BUG: I don't think this is right for D2 + Type *tb = arg->type->toBasetype(); + if (tb->ty == Tsarray) { - arg->error("cannot pass type %s as a function argument", arg->toChars()); - arg = new ErrorExp(); + TypeSArray *ts = (TypeSArray *)tb; + Type *ta = ts->next->arrayOf(); + if (ts->size(arg->loc) == 0) + arg = new NullExp(arg->loc, ta); + else + arg = arg->castTo(sc, ta); + } + if (tb->ty == Tstruct) + { + //arg = callCpCtor(sc, arg); + } + + // Give error for overloaded function addresses + if (arg->op == TOKsymoff) + { SymOffExp *se = (SymOffExp *)arg; + if (se->hasOverloads && + !se->var->isFuncDeclaration()->isUnique()) + { arg->error("function %s is overloaded", arg->toChars()); + err = true; + } + } + if (arg->checkValue()) err = true; + arg = arg->optimize(WANTvalue); + } + (*arguments)[i] = arg; + } + + /* If calling C scanf(), printf(), or any variants, check the format string against the arguments + */ + const bool isVa_list = tf->parameterList.varargs == VARARGnone; + if (fd && (fd->flags & FUNCFLAGprintf)) + { + if (StringExp *se = (*arguments)[nparams - 1 - isVa_list]->isStringExp()) + { + Expressions argslice; + argslice.reserve(nargs - nparams); + for (size_t i = nparams; i < nargs; i++) + argslice.push((*arguments)[i]); + checkPrintfFormat(se->loc, se->toPtr(), argslice, isVa_list); + } + } + else if (fd && (fd->flags & FUNCFLAGscanf)) + { + if (StringExp *se = (*arguments)[nparams - 1 - isVa_list]->isStringExp()) + { + Expressions argslice; + argslice.reserve(nargs - nparams); + for (size_t i = nparams; i < nargs; i++) + argslice.push((*arguments)[i]); + checkPrintfFormat(se->loc, se->toPtr(), argslice, isVa_list); + } + } + + /* Remaining problems: + * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is + * implemented by calling a function) we'll defer this for now. + * 2. value structs (or static arrays of them) that need to be copy constructed + * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the + * function gets called (functions normally destroy their parameters) + * 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned + * up properly. Pushing arguments on the stack then cannot fail. + */ + if (1) + { + /* TODO: tackle problem 1) + */ + const bool leftToRight = true; // TODO: something like !fd.isArrayOp + if (!leftToRight) + assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity + + const ptrdiff_t start = (leftToRight ? 0 : (ptrdiff_t)nargs - 1); + const ptrdiff_t end = (leftToRight ? (ptrdiff_t)nargs : -1); + const ptrdiff_t step = (leftToRight ? 1 : -1); + + /* Compute indices of last throwing argument and first arg needing destruction. + * Used to not set up destructors unless an arg needs destruction on a throw + * in a later argument. + */ + ptrdiff_t lastthrow = -1; + ptrdiff_t firstdtor = -1; + for (ptrdiff_t i = start; i != end; i += step) + { + Expression *arg = (*arguments)[i]; + if (canThrow(arg, sc->func, false)) + lastthrow = i; + if (firstdtor == -1 && arg->type->needsDestruction()) + { + Parameter *p = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[i]); + if (!(p && (p->storageClass & (STClazy | STCref | STCout)))) + firstdtor = i; } - else if (checkNonAssignmentArrayOp(arg)) + } + + /* Does problem 3) apply to this call? + */ + const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0 + && (lastthrow - firstdtor) * step > 0); + + /* If so, initialize 'eprefix' by declaring the gate + */ + VarDeclaration *gate = NULL; + if (needsPrefix) + { + // eprefix => bool __gate [= false] + Identifier *idtmp = Identifier::generateId("__gate"); + gate = new VarDeclaration(loc, Type::tbool, idtmp, NULL); + gate->storage_class |= STCtemp | STCctfe | STCvolatile; + dsymbolSemantic(gate, sc); + + Expression *ae = new DeclarationExp(loc, gate); + eprefix = expressionSemantic(ae, sc); + } + + for (ptrdiff_t i = start; i != end; i += step) + { + Expression *arg = (*arguments)[i]; + + Parameter *parameter = (i >= (ptrdiff_t)nparams ? NULL : tf->parameterList[i]); + const bool isRef = (parameter && (parameter->storageClass & (STCref | STCout))); + const bool isLazy = (parameter && (parameter->storageClass & STClazy)); + + /* Skip lazy parameters + */ + if (isLazy) + continue; + + /* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg. + * Declare a temporary variable for this arg and append that declaration to 'eprefix', + * which will implicitly take care of potential problem 2) for this arg. + * 'eprefix' will therefore finally contain all args up to and including the last + * potentially throwing arg, excluding all lazy parameters. + */ + if (gate) { - arg = new ErrorExp(); + const bool needsDtor = (!isRef && arg->type->needsDestruction() && i != lastthrow); + + /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor) + */ + VarDeclaration *tmp = copyToTemp(0, + needsDtor ? "__pfx" : "__pfy", + !isRef ? arg : arg->addressOf()); + dsymbolSemantic(tmp, sc); + + /* Modify the destructor so it only runs if gate==false, i.e., + * only if there was a throw while constructing the args + */ + if (!needsDtor) + { + if (tmp->edtor) + { + assert(i == lastthrow); + tmp->edtor = NULL; + } + } + else + { + // edtor => (__gate || edtor) + assert(tmp->edtor); + Expression *e = tmp->edtor; + e = new LogicalExp(e->loc, TOKoror, new VarExp(e->loc, gate), e); + tmp->edtor = expressionSemantic(e, sc); + //printf("edtor: %s\n", tmp->edtor->toChars()); + } + + // eprefix => (eprefix, auto __pfx/y = arg) + DeclarationExp *ae = new DeclarationExp(loc, tmp); + eprefix = Expression::combine(eprefix, expressionSemantic(ae, sc)); + + // arg => __pfx/y + arg = new VarExp(loc, tmp); + arg = expressionSemantic(arg, sc); + if (isRef) + { + arg = new PtrExp(loc, arg); + arg = expressionSemantic(arg, sc); + } + + /* Last throwing arg? Then finalize eprefix => (eprefix, gate = true), + * i.e., disable the dtors right after constructing the last throwing arg. + * From now on, the callee will take care of destructing the args because + * the args are implicitly moved into function parameters. + * + * Set gate to null to let the next iterations know they don't need to + * append to eprefix anymore. + */ + if (i == lastthrow) + { + Expression *e = new AssignExp(gate->loc, new VarExp(gate->loc, gate), new IntegerExp(gate->loc, 1, Type::tbool)); + eprefix = Expression::combine(eprefix, expressionSemantic(e, sc)); + gate = NULL; + } + } + else + { + /* No gate, no prefix to append to. + * Handle problem 2) by calling the copy constructor for value structs + * (or static arrays of them) if appropriate. + */ + Type *tv = arg->type->baseElemOf(); + if (!isRef && tv->ty == Tstruct) + arg = doCopyOrMove(sc, arg); + } + + (*arguments)[i] = arg; + } + } + //if (eprefix) printf("eprefix: %s\n", eprefix->toChars()); + + // If D linkage and variadic, add _arguments[] as first argument + if (tf->isDstyleVariadic()) + { + assert(arguments->length >= nparams); + + Parameters *args = new Parameters; + args->setDim(arguments->length - nparams); + for (size_t i = 0; i < arguments->length - nparams; i++) + { + Parameter *arg = new Parameter(STCin, (*arguments)[nparams + i]->type, NULL, NULL, NULL); + (*args)[i] = arg; + } + + TypeTuple *tup = new TypeTuple(args); + Expression *e = new TypeidExp(loc, tup); + e = expressionSemantic(e, sc); + arguments->insert(0, e); + } + + Type *tret = tf->next; + if (isCtorCall) + { + //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd->toChars(), fd->type->toChars(), + // wildmatch, tf->isWild(), fd->isolateReturn()); + if (!tthis) + { + assert(sc->intypeof || global.errors); + tthis = fd->isThis()->type->addMod(fd->type->mod); + } + if (tf->isWild() && !fd->isolateReturn()) + { + if (wildmatch) + tret = tret->substWildTo(wildmatch); + int offset; + if (!tret->implicitConvTo(tthis) && + !(MODimplicitConv(tret->mod, tthis->mod) && tret->isBaseOf(tthis, &offset) && offset == 0)) + { + const char* s1 = tret ->isNaked() ? " mutable" : tret ->modToChars(); + const char* s2 = tthis->isNaked() ? " mutable" : tthis->modToChars(); + ::error(loc, "inout constructor %s creates%s object, not%s", + fd->toPrettyChars(), s1, s2); err = true; } - (*exps)[i] = arg; } + tret = tthis; } - return err; + else if (wildmatch && tret) + { + /* Adjust function return type based on wildmatch + */ + //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars()); + tret = tret->substWildTo(wildmatch); + } + *prettype = tret; + *peprefix = eprefix; + return (err || olderrors != global.errors); } /** @@ -134,6 +2049,8 @@ Package *resolveIsPackage(Dsymbol *sym) } pkg = imp->pkg; } + else if (Module *mod = sym->isModule()) + pkg = mod->isPackageFile ? mod->pkg : sym->isPackage(); else pkg = sym->isPackage(); if (pkg) @@ -141,6 +2058,25 @@ Package *resolveIsPackage(Dsymbol *sym) return pkg; } +static Module *loadStdMath() +{ + static Import *impStdMath = NULL; + if (!impStdMath) + { + Identifiers *a = new Identifiers(); + a->push(Id::std); + Import *s = new Import(Loc(), a, Id::math, NULL, false); + s->load(NULL); + if (s->mod) + { + s->mod->importAll(NULL); + dsymbolSemantic(s->mod, NULL); + } + impStdMath = s; + } + return impStdMath->mod; +} + class ExpressionSemanticVisitor : public Visitor { public: @@ -1938,7 +3874,7 @@ public: void visit(HaltExp *e) { - e->type = Type::tvoid; + e->type = Type::tnoreturn; result = e; } @@ -1959,7 +3895,9 @@ public: Type *tded = NULL; if (e->tok2 == TOKpackage || e->tok2 == TOKmodule) // These is() expressions are special because they can work on modules, not just types. { + const unsigned oldErrors = global.startGagging(); Dsymbol *sym = e->targ->toDsymbol(sc); + global.endGagging(oldErrors); if (sym == NULL) goto Lno; Package *p = resolveIsPackage(sym); @@ -2292,7 +4230,7 @@ public: if (exp->e1->op == TOKarraylength) { // arr.length op= e2; - e = ArrayLengthExp::rewriteOpAssign(exp); + e = rewriteOpAssign(exp); e = expressionSemantic(e, sc); result = e; return; @@ -2382,27 +4320,39 @@ public: result = ((BinExp *)e)->reorderSettingAAElem(sc); } - void visit(CompileExp *exp) +private: + Expression *compileIt(CompileExp *exp) { - StringExp *se = semanticString(sc, exp->e1, "argument to mixin"); - if (!se) - return setError(); - se = se->toUTF8(sc); + OutBuffer buf; + if (expressionsToString(buf, sc, exp->exps)) + return NULL; + unsigned errors = global.errors; - Parser p(exp->loc, sc->_module, (utf8_t *)se->string, se->len, 0); + const size_t len = buf.length(); + const char *str = buf.extractChars(); + Parser p(exp->loc, sc->_module, (const utf8_t *)str, len, false); p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); + Expression *e = p.parseExpression(); - if (p.errors) - { - assert(global.errors != errors); // should have caught all these cases - return setError(); - } + if (global.errors != errors) + return NULL; + if (p.token.value != TOKeof) { - exp->error("incomplete mixin expression (%s)", se->toChars()); - return setError(); + exp->error("incomplete mixin expression (%s)", str); + return NULL; } + return e; + } + +public: + void visit(CompileExp *exp) + { + //printf("CompileExp::semantic('%s')\n", exp->toChars()); + Expression *e = compileIt(exp); + if (!e) + return setError(); result = expressionSemantic(e, sc); } @@ -2508,6 +4458,8 @@ public: if (exp->e1->isBool(false)) { + /* This is an `assert(0)` which means halt program execution + */ FuncDeclaration *fd = sc->parent->isFuncDeclaration(); if (fd) fd->hasReturnExp |= 4; @@ -2525,8 +4477,10 @@ public: result = e; return; } + exp->type = Type::tnoreturn; } - exp->type = Type::tvoid; + else + exp->type = Type::tvoid; result = exp; } @@ -3484,7 +5438,8 @@ public: return setError(); } - if (!tf->callMatch(NULL, exp->arguments)) + const char *failMessage = NULL; + if (!tf->callMatch(NULL, exp->arguments, 0, &failMessage)) { OutBuffer buf; @@ -3495,10 +5450,11 @@ public: tthis->modToBuffer(&buf); //printf("tf = %s, args = %s\n", tf->deco, (*exp->arguments)[0]->type->deco); - ::error(exp->loc, "%s %s %s is not callable using argument types %s", + ::error(exp->loc, "%s `%s%s` is not callable using argument types `%s`", p, exp->e1->toChars(), parametersTypeToChars(tf->parameterList), buf.peekChars()); - + if (failMessage) + errorSupplemental(exp->loc, failMessage); return setError(); } @@ -3553,13 +5509,14 @@ public: assert(exp->f); tiargs = NULL; - if (ve->hasOverloads) + if (exp->f->overnext) exp->f = resolveFuncCall(exp->loc, sc, exp->f, tiargs, NULL, exp->arguments, 2); else { exp->f = exp->f->toAliasFunc(); TypeFunction *tf = (TypeFunction *)exp->f->type; - if (!tf->callMatch(NULL, exp->arguments)) + const char *failMessage = NULL; + if (!tf->callMatch(NULL, exp->arguments, 0, &failMessage)) { OutBuffer buf; @@ -3568,10 +5525,11 @@ public: buf.writeByte(')'); //printf("tf = %s, args = %s\n", tf->deco, (*exp->arguments)[0]->type->deco); - ::error(exp->loc, "%s %s is not callable using argument types %s", - exp->e1->toChars(), parametersTypeToChars(tf->parameterList), + ::error(exp->loc, "%s `%s%s` is not callable using argument types `%s`", + exp->f->kind(), exp->e1->toChars(), parametersTypeToChars(tf->parameterList), buf.peekChars()); - + if (failMessage) + errorSupplemental(exp->loc, failMessage); exp->f = NULL; } } @@ -3954,6 +5912,10 @@ public: exp->e1 = exp->e1->castTo(sc, exp->type->pointerTo()); break; + case Tnull: + exp->type = Type::tnoreturn; // typeof(*null) is bottom type + break; + default: exp->error("can only * a pointer, not a `%s`", exp->e1->type->toChars()); /* fall through */ @@ -7109,25 +9071,6 @@ public: result = exp; } - Module *loadStdMath() - { - static Import *impStdMath = NULL; - if (!impStdMath) - { - Identifiers *a = new Identifiers(); - a->push(Id::std); - Import *s = new Import(Loc(), a, Id::math, NULL, false); - s->load(NULL); - if (s->mod) - { - s->mod->importAll(NULL); - dsymbolSemantic(s->mod, NULL); - } - impStdMath = s; - } - return impStdMath->mod; - } - void visit(PowExp *exp) { if (exp->type) @@ -7579,7 +9522,7 @@ public: exp->error("%s is not an expression", exp->e2->toChars()); return setError(); } - if (e1x->op == TOKerror) + if (e1x->op == TOKerror || e1x->type->ty == Tnoreturn) { result = e1x; return; @@ -8032,10 +9975,18 @@ public: Type *t1 = exp->e1->type; Type *t2 = exp->e2->type; + if (t1->ty == Tnoreturn) + { + exp->type = t2; + } + else if (t2->ty == Tnoreturn) + { + exp->type = t1; + } // If either operand is void the result is void, we have to cast both // the expression to void so that we explicitly discard the expression // value if any (bugzilla 16598) - if (t1->ty == Tvoid || t2->ty == Tvoid) + else if (t1->ty == Tvoid || t2->ty == Tvoid) { exp->type = Type::tvoid; exp->e1 = exp->e1->castTo(sc, exp->type); diff --git a/gcc/d/dmd/func.c b/gcc/d/dmd/func.c index 7a216a97941..b8e1e318376 100644 --- a/gcc/d/dmd/func.c +++ b/gcc/d/dmd/func.c @@ -1473,7 +1473,8 @@ FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s, memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; - functionResolve(&m, s, loc, sc, tiargs, tthis, fargs); + const char *failMessage = NULL; + functionResolve(&m, s, loc, sc, tiargs, tthis, fargs, &failMessage); if (m.last > MATCHnomatch && m.lastf) { @@ -1555,20 +1556,23 @@ FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s, ::error(loc, "none of the overloads of `%s` are callable using a %sobject, candidates are:", fd->ident->toChars(), thisBuf.peekChars()); else - ::error(loc, "%smethod %s is not callable using a %sobject", + ::error(loc, "%smethod `%s` is not callable using a %sobject", funcBuf.peekChars(), fd->toPrettyChars(), thisBuf.peekChars()); } else { //printf("tf = %s, args = %s\n", tf->deco, (*fargs)[0]->type->deco); if (hasOverloads) - ::error(loc, "none of the overloads of `%s` are callable using argument types %s, candidates are:", + ::error(loc, "none of the overloads of `%s` are callable using argument types `%s`, candidates are:", fd->ident->toChars(), fargsBuf.peekChars()); else - fd->error(loc, "%s%s is not callable using argument types %s", - parametersTypeToChars(tf->parameterList), - tf->modToChars(), - fargsBuf.peekChars()); + { + fd->error(loc, "%s `%s%s%s` is not callable using argument types `%s`", + fd->kind(), fd->toPrettyChars(), parametersTypeToChars(tf->parameterList), + tf->modToChars(), fargsBuf.peekChars()); + if (failMessage) + errorSupplemental(loc, failMessage); + } } // Display candidate functions diff --git a/gcc/d/dmd/hdrgen.c b/gcc/d/dmd/hdrgen.c index a11c9c353d9..9eba88f1118 100644 --- a/gcc/d/dmd/hdrgen.c +++ b/gcc/d/dmd/hdrgen.c @@ -122,7 +122,7 @@ public: void visit(CompileStatement *s) { buf->writestring("mixin("); - s->exp->accept(this); + argsToBuffer(s->exps); buf->writestring(");"); if (!hgs->forStmtInit) buf->writenl(); @@ -1104,6 +1104,18 @@ public: buf->writestring("typeof(null)"); } + void visit(TypeMixin *t) + { + buf->writestring("mixin("); + argsToBuffer(t->exps); + buf->writeByte(')'); + } + + void visit(TypeNoreturn *) + { + buf->writestring("noreturn"); + } + //////////////////////////////////////////////////////////////////////////// void visit(Dsymbol *s) @@ -1418,7 +1430,7 @@ public: void visit(CompileDeclaration *d) { buf->writestring("mixin("); - d->exp->accept(this); + argsToBuffer(d->exps); buf->writestring(");"); buf->writenl(); } @@ -2408,8 +2420,15 @@ public: buf->writeByte(')'); if (target.ptrsize == 8) goto L4; - else + else if (target.ptrsize == 4 || + target.ptrsize == 2) goto L3; + else + assert(0); + + case Tvoid: + buf->writestring("cast(void)0"); + break; default: /* This can happen if errors, such as @@ -2822,7 +2841,7 @@ public: void visit(CompileExp *e) { buf->writestring("mixin("); - expToBuffer(e->e1, PREC_assign); + argsToBuffer(e->exps); buf->writeByte(')'); } @@ -3528,6 +3547,13 @@ void arrayObjectsToBuffer(OutBuffer *buf, Objects *objects) } } +/************************************************************* + * Pretty print function parameters. + * Params: + * parameters = parameters to print, such as TypeFunction.parameters. + * varargs = kind of varargs, see TypeFunction.varargs. + * Returns: Null-terminated string representing parameters. + */ const char *parametersTypeToChars(ParameterList pl) { OutBuffer buf; @@ -3536,3 +3562,26 @@ const char *parametersTypeToChars(ParameterList pl) v.parametersToBuffer(pl.parameters, pl.varargs); return buf.extractChars(); } + +/************************************************************* + * Pretty print function parameter. + * Params: + * parameter = parameter to print. + * tf = TypeFunction which holds parameter. + * fullQual = whether to fully qualify types. + * Returns: Null-terminated string representing parameters. + */ +const char *parameterToChars(Parameter *parameter, TypeFunction *tf, bool fullQual) +{ + OutBuffer buf; + HdrGenState hgs; + hgs.fullQual = fullQual; + PrettyPrintVisitor v(&buf, &hgs); + + parameter->accept(&v); + if (tf->parameterList.varargs == 2 && parameter == tf->parameterList[tf->parameterList.parameters->length - 1]) + { + buf.writestring("..."); + } + return buf.extractChars(); +} diff --git a/gcc/d/dmd/hdrgen.h b/gcc/d/dmd/hdrgen.h index d464d4aa618..6822aaf44a9 100644 --- a/gcc/d/dmd/hdrgen.h +++ b/gcc/d/dmd/hdrgen.h @@ -47,6 +47,7 @@ void arrayObjectsToBuffer(OutBuffer *buf, Objects *objects); void moduleToBuffer(OutBuffer *buf, Module *m); const char *parametersTypeToChars(ParameterList pl); +const char *parameterToChars(Parameter *parameter, TypeFunction *tf, bool fullQual); bool stcToBuffer(OutBuffer *buf, StorageClass stc); const char *stcToChars(StorageClass& stc); diff --git a/gcc/d/dmd/idgen.c b/gcc/d/dmd/idgen.c index 59faa993b9b..0740653709a 100644 --- a/gcc/d/dmd/idgen.c +++ b/gcc/d/dmd/idgen.c @@ -86,10 +86,16 @@ Msgtable msgtable[] = { "__c_longlong", NULL }, { "__c_ulonglong", NULL }, { "__c_long_double", NULL }, + { "__c_wchar_t", NULL }, + { "__c_complex_float", NULL }, + { "__c_complex_double", NULL }, + { "__c_complex_real", NULL }, { "cpp_type_info_ptr", "__cpp_type_info_ptr" }, { "_assert", "assert" }, { "_unittest", "unittest" }, { "_body", "body" }, + { "printf", NULL }, + { "scanf", NULL }, { "TypeInfo", NULL }, { "TypeInfo_Class", NULL }, @@ -395,7 +401,6 @@ Msgtable msgtable[] = { "derivedMembers", NULL }, { "isSame", NULL }, { "compiles", NULL }, - { "parameters", NULL }, { "getAliasThis", NULL }, { "getAttributes", NULL }, { "getFunctionAttributes", NULL }, @@ -411,6 +416,7 @@ Msgtable msgtable[] = { "getLocation", NULL }, { "hasPostblit", NULL }, { "isCopyable", NULL }, + { "toType", NULL }, // For C++ mangling { "allocator", NULL }, diff --git a/gcc/d/dmd/import.h b/gcc/d/dmd/import.h index e49ad15c2d3..07fb32aa070 100644 --- a/gcc/d/dmd/import.h +++ b/gcc/d/dmd/import.h @@ -47,6 +47,7 @@ public: Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees void load(Scope *sc); void importAll(Scope *sc); + void addPackageAccess(ScopeDsymbol *scopesym); Dsymbol *toAlias(); void addMember(Scope *sc, ScopeDsymbol *sds); void setScope(Scope* sc); diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index 03078b5e833..1664492bc2d 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -75,6 +75,7 @@ public: unsigned numlines; // number of lines in source file int isDocFile; // if it is a documentation input file, not D source bool isPackageFile; // if it is a package.d + Package *pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; diff --git a/gcc/d/dmd/mtype.c b/gcc/d/dmd/mtype.c index c1071b2f278..6b01999bc7c 100644 --- a/gcc/d/dmd/mtype.c +++ b/gcc/d/dmd/mtype.c @@ -39,6 +39,7 @@ Expression *extractSideEffect(Scope *sc, const char *name, Expression **e0, Expr Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); Expression *typeToExpression(Type *t); Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i = 0); +RootObject *compileTypeMixin(TypeMixin *tm, Loc loc, Scope *sc); /***************************** Type *****************************/ @@ -93,6 +94,7 @@ Type *Type::tdchar; Type *Type::tshiftcnt; Type *Type::terror; Type *Type::tnull; +Type *Type::tnoreturn; Type *Type::tsize_t; Type *Type::tptrdiff_t; @@ -195,6 +197,8 @@ void Type::_init() sizeTy[Tnull] = sizeof(TypeNull); sizeTy[Tvector] = sizeof(TypeVector); sizeTy[Ttraits] = sizeof(TypeTraits); + sizeTy[Tmixin] = sizeof(TypeMixin); + sizeTy[Tnoreturn] = sizeof(TypeNoreturn); initTypeMangle(); @@ -216,6 +220,10 @@ void Type::_init() } basic[Terror] = new TypeError(); + tnoreturn = new TypeNoreturn(); + tnoreturn->deco = tnoreturn->merge()->deco; + basic[Tnoreturn] = tnoreturn; + tvoid = basic[Tvoid]; tint8 = basic[Tint8]; tuns8 = basic[Tuns8]; @@ -246,7 +254,7 @@ void Type::_init() tshiftcnt = tint32; terror = basic[Terror]; - tnull = basic[Tnull]; + tnoreturn = basic[Tnoreturn]; tnull = new TypeNull(); tnull->deco = tnull->merge()->deco; @@ -2079,7 +2087,7 @@ Expression *Type::getProperty(Loc loc, Identifier *ident, int flag) if (this != Type::terror) { if (s) - error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident->toChars(), toChars(), s->toChars()); + error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident->toChars(), toChars(), s->toPrettyChars()); else error(loc, "no property `%s` for type `%s`", ident->toChars(), toChars()); } @@ -2413,6 +2421,16 @@ TypeTraits *Type::isTypeTraits() return ty == Ttraits ? (TypeTraits *)this : NULL; } +TypeMixin *Type::isTypeMixin() +{ + return ty == Tmixin ? (TypeMixin *)this : NULL; +} + +TypeNoreturn *Type::isTypeNoreturn() +{ + return ty == Tnoreturn ? (TypeNoreturn *)this : NULL; +} + TypeFunction *Type::toTypeFunction() { if (ty != Tfunction) @@ -5175,16 +5193,47 @@ void TypeFunction::purityLevel() tf->purity = purity; } +// arguments get specially formatted +static const char *getParamError(TypeFunction *tf, Expression *arg, Parameter *par) +{ + if (global.gag && !global.params.showGaggedErrors) + return NULL; + // show qualification when toChars() is the same but types are different + const char *at = arg->type->toChars(); + bool qual = !arg->type->equals(par->type) && strcmp(at, par->type->toChars()) == 0; + if (qual) + at = arg->type->toPrettyChars(true); + OutBuffer buf; + // only mention rvalue if it's relevant + const bool rv = !arg->isLvalue() && (par->storageClass & (STCref | STCout)) != 0; + buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", + rv ? "rvalue " : "", arg->toChars(), at, + parameterToChars(par, tf, qual)); + return buf.extractChars(); +} + +static const char *getMatchError(const char *format, ...) +{ + if (global.gag && !global.params.showGaggedErrors) + return NULL; + OutBuffer buf; + va_list ap; + va_start(ap, format); + buf.vprintf(format, ap); + return buf.extractChars(); +} + /******************************** * 'args' are being matched to function 'this' * Determine match level. * Input: * flag 1 performing a partial ordering match + * pMessage address to store error message, or null * Returns: * MATCHxxxx */ -MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) +MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag, const char **pMessage) { //printf("TypeFunction::callMatch() %s\n", toChars()); MATCH match = MATCHexact; // assume exact match @@ -5221,12 +5270,15 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) size_t nparams = parameterList.length(); size_t nargs = args ? args->length : 0; - if (nparams == nargs) - ; - else if (nargs > nparams) + if (nargs > nparams) { if (parameterList.varargs == VARARGnone) - goto Nomatch; // too many args; no match + { + // suppress early exit if an error message is wanted, + // so we can check any matching args are valid + if (!pMessage) + goto Nomatch; // too many args; no match + } match = MATCHconvert; // match ... with a "conversion" match level } @@ -5309,7 +5361,10 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) if (m && !arg->isLvalue()) { if (p->storageClass & STCout) + { + if (pMessage) *pMessage = getParamError(this, arg, p); goto Nomatch; + } if (arg->op == TOKstring && tp->ty == Tsarray) { @@ -5331,7 +5386,10 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) } } else + { + if (pMessage) *pMessage = getParamError(this, arg, p); goto Nomatch; + } } /* Find most derived alias this type being matched. @@ -5351,7 +5409,10 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) * ref T[dim] <- an lvalue of const(T[dim]) argument */ if (!ta->constConv(tp)) + { + if (pMessage) *pMessage = getParamError(this, arg, p); goto Nomatch; + } } } @@ -5377,7 +5438,11 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) tsa = (TypeSArray *)tb; sz = tsa->dim->toInteger(); if (sz != nargs - u) + { + if (pMessage) + *pMessage = getMatchError("expected %llu variadic argument(s), not %zu", sz, nargs - u); goto Nomatch; + } /* fall through */ case Tarray: { @@ -5408,7 +5473,10 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) m = arg->implicitConvTo(ta->next); if (m == MATCHnomatch) + { + if (pMessage) *pMessage = getParamError(this, arg, p); goto Nomatch; + } if (m < match) match = m; } @@ -5420,9 +5488,14 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) goto Ldone; default: - goto Nomatch; + break; } } + if (pMessage && u < nargs) + *pMessage = getParamError(this, (*args)[u], p); + else if (pMessage) + *pMessage = getMatchError("missing argument for parameter #%d: `%s`", + u + 1, parameterToChars(p, this, false)); goto Nomatch; } if (m < match) @@ -5430,6 +5503,12 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) } Ldone: + if (pMessage && !parameterList.varargs && nargs > nparams) + { + // all parameters had a match, but there are surplus args + *pMessage = getMatchError("expected %d argument(s), not %d", nparams, nargs); + goto Nomatch; + } //printf("match = %d\n", match); return match; @@ -5797,6 +5876,20 @@ Type *TypeTraits::syntaxCopy() return tt; } +Dsymbol *TypeTraits::toDsymbol(Scope *sc) +{ + Type *t = NULL; + Expression *e = NULL; + Dsymbol *s = NULL; + resolve(loc, sc, &e, &t, &s); + if (t && t->ty != Terror) + s = t->toDsymbol(sc); + else if (e) + s = getDsymbol(e); + + return s; +} + void TypeTraits::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool) { *pt = NULL; @@ -5816,6 +5909,90 @@ d_uns64 TypeTraits::size(Loc) return SIZE_INVALID; } +/***************************** TypeMixin *****************************/ + +/****** + * Implements mixin types. + * + * Semantic analysis will convert it to a real type. + */ +TypeMixin::TypeMixin(const Loc &loc, Expressions *exps) + : Type(Tmixin) +{ + this->loc = loc; + this->exps = exps; + this->obj = NULL; // cached result of semantic analysis. +} + +const char *TypeMixin::kind() +{ + return "mixin"; +} + +Type *TypeMixin::syntaxCopy() +{ + return new TypeMixin(loc, Expression::arraySyntaxCopy(exps)); +} + +Dsymbol *TypeMixin::toDsymbol(Scope *sc) +{ + Type *t = NULL; + Expression *e = NULL; + Dsymbol *s = NULL; + resolve(loc, sc, &e, &t, &s); + if (t) + s = t->toDsymbol(sc); + else if (e) + s = getDsymbol(e); + + return s; +} + +void TypeMixin::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid) +{ + // if already resolved just set pe/pt/ps and return. + if (obj) + { + *pe = isExpression(obj); + *pt = isType(obj); + *ps = isDsymbol(obj); + return; + } + + RootObject *o = compileTypeMixin(this, loc, sc); + if (Type *t = isType(o)) + { + t->resolve(loc, sc, pe, pt, ps, intypeid); + if (*pt) + (*pt) = (*pt)->addMod(mod); + } + else if (Expression *e = isExpression(o)) + { + e = expressionSemantic(e, sc); + if (TypeExp *et = e->isTypeExp()) + { + *pe = NULL; + *pt = et->type->addMod(mod); + *ps = NULL; + } + else + { + *pe = e; + *pt = NULL; + *ps = NULL; + } + } + else + { + *pe = NULL; + *pt = Type::terror; + *ps = NULL; + } + + // save the result + obj = *pe ? (RootObject *)*pe : (*pt ? (RootObject *)*pt : (RootObject *)*ps); +} + /***************************** TypeQualified *****************************/ TypeQualified::TypeQualified(TY ty, Loc loc) @@ -6003,11 +6180,25 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, Type *t = s->getType(); // type symbol, type alias, or type tuple? unsigned errorsave = global.errors; - Dsymbol *sm = s->searchX(loc, sc, id); - if (sm && !(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, sm)) + int flags = t == NULL ? SearchLocalsOnly : IgnorePrivateImports; + Dsymbol *sm = s->searchX(loc, sc, id, flags); + if (sm) { - ::error(loc, "`%s` is not visible from module `%s`", sm->toPrettyChars(), sc->_module->toChars()); - sm = NULL; + if (!(sc->flags & SCOPEignoresymbolvisibility) && !symbolIsVisible(sc, sm)) + { + ::error(loc, "`%s` is not visible from module `%s`", sm->toPrettyChars(), sc->_module->toChars()); + sm = NULL; + } + // Same check as in Expression::semanticY(DotIdExp) + else if (sm->isPackage() && checkAccess(sc, (Package *)sm)) + { + // @@@DEPRECATED_2.096@@@ + // Should be an error in 2.106. Just remove the deprecation call + // and uncomment the null assignment + ::deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", + sm->kind(), sm->toPrettyChars(), sm->toPrettyChars()); + //sm = null; + } } if (global.errors != errorsave) { @@ -6052,7 +6243,7 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, sm = t->toDsymbol(sc); if (sm && id->dyncast() == DYNCAST_IDENTIFIER) { - sm = sm->search(loc, (Identifier *)id); + sm = sm->search(loc, (Identifier *)id, IgnorePrivateImports); if (sm) goto L2; } @@ -8177,6 +8368,49 @@ Expression *TypeNull::defaultInit(Loc) return new NullExp(Loc(), Type::tnull); } +/***************************** TypeNoreturn *****************************/ + +TypeNoreturn::TypeNoreturn() + : Type(Tnoreturn) +{ + //printf("TypeNoreturn %p\n", this); +} + +const char *TypeNoreturn::kind() +{ + return "noreturn"; +} + +Type *TypeNoreturn::syntaxCopy() +{ + // No semantic analysis done, no need to copy + return this; +} + +MATCH TypeNoreturn::implicitConvTo(Type *to) +{ + //printf("TypeNoreturn::implicitConvTo(this=%p, to=%p)\n", this, to); + //printf("from: %s\n", toChars()); + //printf("to : %s\n", to.toChars()); + MATCH m = Type::implicitConvTo(to); + return (m == MATCHexact) ? MATCHexact : MATCHconvert; +} + +bool TypeNoreturn::isBoolean() +{ + return true; // bottom type can be implicitly converted to any other type +} + +d_uns64 TypeNoreturn::size(Loc) +{ + return 0; +} + +unsigned TypeNoreturn::alignsize() +{ + return 0; +} + /*********************************************************** * Encapsulate Parameters* so .length and [i] can be used on it. * https://dlang.org/spec/function.html#ParameterList @@ -8472,3 +8706,25 @@ bool Parameter::isCovariantScope(bool returnByRef, StorageClass from, StorageCla return covariant[SR::buildSR(returnByRef, from)][SR::buildSR(returnByRef, to)]; } + +/** + * For printing two types with qualification when necessary. + * Params: + * t1 = The first type to receive the type name for + * t2 = The second type to receive the type name for + * Returns: + * The fully-qualified names of both types if the two type names are not the same, + * or the unqualified names of both types if the two type names are the same. + */ +void toAutoQualChars(const char **result, Type *t1, Type *t2) +{ + const char *s1 = t1->toChars(); + const char *s2 = t2->toChars(); + if (strcmp(s1, s2) == 0) + { + s1 = t1->toPrettyChars(true); + s2 = t2->toPrettyChars(true); + } + result[0] = s1; + result[1] = s2; +} diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index 083d707f8f3..3687053488d 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -96,6 +96,8 @@ enum ENUMTY Tint128, Tuns128, Ttraits, + Tmixin, + Tnoreturn, TMAX }; typedef unsigned char TY; // ENUMTY @@ -201,6 +203,7 @@ public: static Type *tdstring; // immutable(dchar)[] static Type *terror; // for error recovery static Type *tnull; // for null type + static Type *tnoreturn; // for bottom type typeof(*null) static Type *tsize_t; // matches size_t alias static Type *tptrdiff_t; // matches ptrdiff_t alias @@ -367,7 +370,9 @@ public: TypeTuple *isTypeTuple(); TypeSlice *isTypeSlice(); TypeNull *isTypeNull(); + TypeMixin *isTypeMixin(); TypeTraits *isTypeTraits(); + TypeNoreturn *isTypeNoreturn(); void accept(Visitor *v) { v->visit(this); } }; @@ -686,7 +691,7 @@ public: int attributesApply(void *param, int (*fp)(void *, const char *), TRUSTformat trustFormat = TRUSTformatDefault); Type *substWildTo(unsigned mod); - MATCH callMatch(Type *tthis, Expressions *toargs, int flag = 0); + MATCH callMatch(Type *tthis, Expressions *toargs, int flag = 0, const char **pMessage = NULL); bool checkRetType(Loc loc); Expression *defaultInit(Loc loc) /*const*/; @@ -726,11 +731,27 @@ public: TypeTraits(const Loc &loc, TraitsExp *exp); Type *syntaxCopy(); + Dsymbol *toDsymbol(Scope *sc); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); d_uns64 size(Loc loc); void accept(Visitor *v) { v->visit(this); } }; +class TypeMixin : public Type +{ +public: + Loc loc; + Expressions *exps; + RootObject *obj; + + TypeMixin(const Loc &loc, Expressions *exps); + const char *kind(); + Type *syntaxCopy(); + Dsymbol *toDsymbol(Scope *sc); + void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); + void accept(Visitor *v) { v->visit(this); } +}; + class TypeQualified : public Type { public: @@ -966,6 +987,21 @@ public: void accept(Visitor *v) { v->visit(this); } }; +class TypeNoreturn : public Type +{ +public: + TypeNoreturn(); + const char *kind(); + + Type *syntaxCopy(); + MATCH implicitConvTo(Type *to); + bool isBoolean() /*const*/; + + d_uns64 size(Loc loc) /*const*/; + unsigned alignsize(); + void accept(Visitor *v) { v->visit(this); } +}; + /**************************************************************/ bool arrayTypeCompatible(Loc loc, Type *t1, Type *t2); diff --git a/gcc/d/dmd/parse.c b/gcc/d/dmd/parse.c index c050b050404..e1f13214d58 100644 --- a/gcc/d/dmd/parse.c +++ b/gcc/d/dmd/parse.c @@ -328,11 +328,9 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes { // mixin(string) nextToken(); - check(TOKlparen, "mixin"); - Expression *e = parseAssignExp(); - check(TOKrparen); + Expressions *exps = parseArguments(); check(TOKsemicolon); - s = new CompileDeclaration(loc, e); + s = new CompileDeclaration(loc, exps); break; } case TOKtemplate: @@ -1336,7 +1334,7 @@ LINK Parser::parseLinkage(Identifiers **pidents, CPPMANGLE *pcppmangle, bool *pc } else if (!Identifier::isValidIdentifier(name)) { - error("expected valid identifer for C++ namespace but got `%s`", name); + error("expected valid identifier for C++ namespace but got `%s`", name); idents = NULL; break; } @@ -2922,6 +2920,18 @@ Objects *Parser::parseTemplateArguments() return tiargs; } +/*************************************** + * Parse a Type or an Expression + * Returns: + * RootObject representing the AST + */ +RootObject *Parser::parseTypeOrAssignExp(TOK endtoken) +{ + return isDeclaration(&token, 0, endtoken, NULL) + ? (RootObject *)parseType() // argument is a type + : (RootObject *)parseAssignExp(); // argument is an expression +} + /****************************************** * Parse template argument list. * Input: @@ -2942,20 +2952,10 @@ Objects *Parser::parseTemplateArgumentList() // Get TemplateArgumentList while (token.value != endtok) { - // See if it is an Expression or a Type - if (isDeclaration(&token, 0, TOKreserved, NULL)) - { // Template argument is a type - Type *ta = parseType(); - tiargs->push(ta); - } - else - { // Template argument is an expression - Expression *ea = parseAssignExp(); - tiargs->push(ea); - } - if (token.value != TOKcomma) - break; - nextToken(); + tiargs->push(parseTypeOrAssignExp()); + if (token.value != TOKcomma) + break; + nextToken(); } check(endtok, "template argument list"); return tiargs; @@ -3288,6 +3288,15 @@ Type *Parser::parseBasicType(bool dontLookDotIdents) } break; + case TOKmixin: + // https://dlang.org/spec/expression.html#mixin_types + loc = token.loc; + nextToken(); + if (token.value != TOKlparen) + error("found `%s` when expecting `%s` following %s", token.toChars(), Token::toChars(TOKlparen), "`mixin`"); + t = new TypeMixin(loc, parseArguments()); + break; + case TOKdot: // Leading . as in .foo t = parseBasicTypeStartingAt(new TypeIdentifier(token.loc, Id::empty), dontLookDotIdents); @@ -3602,7 +3611,7 @@ Type *Parser::parseDeclarator(Type *t, int *palt, Identifier **pident, if (pident) *pident = token.ident; else - error("unexpected identifer `%s` in declarator", token.ident->toChars()); + error("unexpected identifier `%s` in declarator", token.ident->toChars()); ts = t; nextToken(); break; @@ -3863,6 +3872,21 @@ void Parser::parseStorageClasses(StorageClass &storage_class, LINK &link, } } +static void parseAttributes(Parser *p, bool &hasParsedAttributes, + StorageClass &storage_class, LINK &link, bool &setAlignment, + Expression *&ealign, Expressions *&udas) +{ + if (hasParsedAttributes) // only parse once + return; + hasParsedAttributes = true; + udas = NULL; + storage_class = STCundefined; + link = p->linkage; + setAlignment = false; + ealign = NULL; + p->parseStorageClasses(storage_class, link, setAlignment, ealign, udas); +} + /********************************** * Parse Declarations. * These can be: @@ -3936,26 +3960,48 @@ Dsymbols *Parser::parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, con bool hasParsedAttributes = false; if (token.value == TOKat) { - if (!hasParsedAttributes) - { - hasParsedAttributes = true; - storage_class = STCundefined; - link = linkage; - setAlignment = false; - ealign = NULL; - udas = NULL; - parseStorageClasses(storage_class, link, setAlignment, ealign, udas); - } + parseAttributes(this, hasParsedAttributes, + storage_class, link, setAlignment, ealign, udas); } Declaration *v; - if (token.value == TOKfunction || + Dsymbol *s; + + // try to parse function type: + // TypeCtors? BasicType ( Parameters ) MemberFunctionAttributes + bool attributesAppended = false; + const StorageClass funcStc = parseTypeCtor(); + Token *tlu = &token; + if (token.value != TOKfunction && + token.value != TOKdelegate && + isBasicType(&tlu) && tlu && + tlu->value == TOKlparen) + { + VarArg vargs; + Type *tret = parseBasicType(); + Parameters *prms = parseParameters(&vargs); + ParameterList pl = ParameterList(prms, vargs); + + parseAttributes(this, hasParsedAttributes, + storage_class, link, setAlignment, ealign, udas); + if (udas) + error("user-defined attributes not allowed for `alias` declarations"); + + attributesAppended = true; + storage_class = appendStorageClass(storage_class, funcStc); + Type *tf = new TypeFunction(pl, tret, link, storage_class); + v = new AliasDeclaration(loc, ident, tf); + } + else if (token.value == TOKfunction || token.value == TOKdelegate || (token.value == TOKlparen && skipAttributes(peekPastParen(&token), &tk) && (tk->value == TOKgoesto || tk->value == TOKlcurly)) || token.value == TOKlcurly || - (token.value == TOKidentifier && peekNext() == TOKgoesto)) + (token.value == TOKidentifier && peekNext() == TOKgoesto) || + (token.value == TOKref && peekNext() == TOKlparen && + skipAttributes(peekPastParen(peek(&token)), &tk) && + (tk->value == TOKgoesto || tk->value == TOKlcurly))) { // function (parameters) { statements... } // delegate (parameters) { statements... } @@ -3963,8 +4009,10 @@ Dsymbols *Parser::parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, con // (parameters) => expression // { statements... } // identifier => expression + // ref (parameters) { statements... } + // ref (parameters) => expression - Dsymbol *s = parseFunctionLiteral(); + s = parseFunctionLiteral(); if (udas != NULL) { @@ -3985,26 +4033,19 @@ Dsymbols *Parser::parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, con else { // StorageClasses type - if (!hasParsedAttributes) - { - hasParsedAttributes = true; - storage_class = STCundefined; - link = linkage; - setAlignment = false; - ealign = NULL; - udas = NULL; - parseStorageClasses(storage_class, link, setAlignment, ealign, udas); - } - + parseAttributes(this, hasParsedAttributes, + storage_class, link, setAlignment, ealign, udas); if (udas) error("user-defined attributes not allowed for %s declarations", Token::toChars(tok)); t = parseType(); v = new AliasDeclaration(loc, ident, t); } + if (!attributesAppended) + storage_class = appendStorageClass(storage_class, funcStc); v->storage_class = storage_class; - Dsymbol *s = v; + s = v; if (tpl) { Dsymbols *a2 = new Dsymbols(); @@ -4358,6 +4399,13 @@ Dsymbol *Parser::parseFunctionLiteral() case TOKdelegate: save = token.value; nextToken(); + if (token.value == TOKref) + { + // function ref (parameters) { statements... } + // delegate ref (parameters) { statements... } + stc = STCref; + nextToken(); + } if (token.value != TOKlparen && token.value != TOKlcurly) { // function type (parameters) { statements... } @@ -4377,14 +4425,22 @@ Dsymbol *Parser::parseFunctionLiteral() // delegate { statements... } break; } - /* fall through */ + goto LTOKlparen; + + case TOKref: + // ref (parameters) => expression + // ref (parameters) { statements... } + stc = STCref; + nextToken(); + goto LTOKlparen; case TOKlparen: + LTOKlparen: { // (parameters) => expression // (parameters) { statements... } parameters = parseParameters(&varargs, &tpl); - stc = parsePostfix(STCundefined, NULL); + stc = parsePostfix(stc, NULL); if (StorageClass modStc = stc & STC_TYPECTOR) { if (save == TOKfunction) @@ -5093,7 +5149,7 @@ Statement *Parser::parseForeach(Loc loc, bool *isRange, bool isDecl) check(TOKrparen); Loc endloc; Statement *body = (!isDecl) ? parseStatement(0, NULL, &endloc) : NULL; - if (isRange) + if (isRange) *isRange = false; return new ForeachStatement(loc, op, parameters, aggr, body, endloc); } @@ -5378,15 +5434,19 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr, Loc *pEndloc } case TOKmixin: - { Token *t = peek(&token); + { + if (isDeclaration(&token, 3, TOKreserved, NULL)) + goto Ldeclaration; + Token *t = peek(&token); if (t->value == TOKlparen) - { // mixin(string) + { + // mixin(string) Expression *e = parseAssignExp(); check(TOKsemicolon); if (e->op == TOKmixin) { CompileExp *cpe = (CompileExp *)e; - s = new CompileStatement(loc, cpe->e1); + s = new CompileStatement(loc, cpe->exps); } else { @@ -6332,6 +6392,7 @@ bool Parser::isBasicType(Token **pt) case TOKtypeof: case TOKvector: + case TOKmixin: /* typeof(exp).identifier... */ t = peek(t); @@ -6592,9 +6653,11 @@ bool Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok, boo case TOKin: case TOKout: case TOKdo: + LTOKdo: // The !parens is to disallow unnecessary parentheses if (!parens && (endtok == TOKreserved || endtok == t->value)) - { *pt = t; + { + *pt = t; return true; } return false; @@ -6602,6 +6665,12 @@ bool Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok, boo case TOKif: return haveTpl ? true : false; + // Used for mixin type parsing + case TOKeof: + if (endtok == TOKeof) + goto LTOKdo; + return false; + default: Ldefault: return false; @@ -7229,15 +7298,7 @@ Expression *Parser::parsePrimaryExp() { nextToken(); check(TOKlparen, "typeid"); - RootObject *o; - if (isDeclaration(&token, 0, TOKreserved, NULL)) - { // argument is a type - o = parseType(); - } - else - { // argument is an expression - o = parseAssignExp(); - } + RootObject *o = parseTypeOrAssignExp(); check(TOKrparen); e = new TypeidExp(loc, o); break; @@ -7279,6 +7340,14 @@ Expression *Parser::parsePrimaryExp() if (token.value == TOKlparen) { nextToken(); + if (token.value == TOKidentifier && peekNext() == TOKlparen) + { + error(loc, "unexpected `(` after `%s`, inside `is` expression. Try enclosing the contents of `is` with a `typeof` expression", token.toChars()); + nextToken(); + Token *tempTok = peekPastParen(&token); + memcpy(&token, tempTok, sizeof(Token)); + goto Lerr; + } targ = parseType(&ident); if (token.value == TOKcolon || token.value == TOKequal) { @@ -7357,11 +7426,11 @@ Expression *Parser::parsePrimaryExp() case TOKmixin: { + // https://dlang.org/spec/expression.html#mixin_expressions nextToken(); - check(TOKlparen, "mixin"); - e = parseAssignExp(); - check(TOKrparen); - e = new CompileExp(loc, e); + if (token.value != TOKlparen) + error("found `%s` when expecting `%s` following %s", token.toChars(), Token::toChars(TOKlparen), "`mixin`"); + e = new CompileExp(loc, parseArguments()); break; } @@ -7379,6 +7448,24 @@ Expression *Parser::parsePrimaryExp() e = parseNewExp(NULL); break; + case TOKref: + { + if (peekNext() == TOKlparen) + { + Token *tk = peekPastParen(peek(&token)); + if (skipAttributes(tk, &tk) && + (tk->value == TOKgoesto || tk->value == TOKlcurly)) + { + // ref (arguments) => expression + // ref (arguments) { statements... } + goto case_delegate; + } + } + nextToken(); + error("found `%s` when expecting function literal following `ref`", token.toChars()); + goto Lerr; + } + case TOKlparen: { Token *tk = peekPastParen(&token); diff --git a/gcc/d/dmd/parse.h b/gcc/d/dmd/parse.h index 82ce3254607..a2ad47882ef 100644 --- a/gcc/d/dmd/parse.h +++ b/gcc/d/dmd/parse.h @@ -83,6 +83,7 @@ public: TemplateParameters *parseTemplateParameterList(int flag = 0); Dsymbol *parseMixin(); Objects *parseTemplateArguments(); + RootObject *parseTypeOrAssignExp(TOK endtoken = TOKreserved); Objects *parseTemplateArgumentList(); Objects *parseTemplateSingleArgument(); StaticAssert *parseStaticAssert(); diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index ea3bf3874e1..ea3061b8fa6 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -66,6 +66,10 @@ enum PINLINE; #define SCOPEfullinst 0x10000 // fully instantiate templates #define SCOPEalias 0x20000 // inside alias declaration +// The following are mutually exclusive +#define SCOPEprintf 0x40000 // printf-style function +#define SCOPEscanf 0x80000 // scanf-style function + struct Scope { Scope *enclosing; // enclosing Scope diff --git a/gcc/d/dmd/semantic2.c b/gcc/d/dmd/semantic2.c index 7bcf6ce4f33..194a3fb9661 100644 --- a/gcc/d/dmd/semantic2.c +++ b/gcc/d/dmd/semantic2.c @@ -163,6 +163,15 @@ public: if (vd->_init && !vd->toParent()->isFuncDeclaration()) { vd->inuse++; + + /* https://issues.dlang.org/show_bug.cgi?id=20280 + * + * Template instances may import modules that have not + * finished semantic1. + */ + if (!vd->type) + dsymbolSemantic(vd, sc); + // Bugzilla 14166: Don't run CTFE for the temporary variables inside typeof vd->_init = initializerSemantic(vd->_init, sc, vd->type, sc->intypeof == 1 ? INITnointerpret : INITinterpret); vd->inuse--; @@ -265,6 +274,17 @@ public: { if (fd->semanticRun >= PASSsemantic2done) return; + + if (fd->semanticRun < PASSsemanticdone && !fd->errors) + { + /* https://issues.dlang.org/show_bug.cgi?id=21614 + * + * Template instances may import modules that have not + * finished semantic1. + */ + dsymbolSemantic(fd, sc); + } + assert(fd->semanticRun <= PASSsemantic2); fd->semanticRun = PASSsemantic2; diff --git a/gcc/d/dmd/semantic3.c b/gcc/d/dmd/semantic3.c index 304eaeeb119..6bd9a6d2e27 100644 --- a/gcc/d/dmd/semantic3.c +++ b/gcc/d/dmd/semantic3.c @@ -850,32 +850,18 @@ public: } assert(!funcdecl->returnLabel); } + else if (f->next->ty == Tnoreturn) + { + } else { const bool inlineAsm = (funcdecl->hasReturnExp & 8) != 0; if ((blockexit & BEfallthru) && f->next->ty != Tvoid && !inlineAsm) { - Expression *e; if (!funcdecl->hasReturnExp) - funcdecl->error("has no return statement, but is expected to return a value of type %s", f->next->toChars()); - else - funcdecl->error("no return exp; or assert(0); at end of function"); - if (global.params.useAssert == CHECKENABLEon && - !global.params.useInline) - { - /* Add an assert(0, msg); where the missing return - * should be. - */ - e = new AssertExp(funcdecl->endloc, - new IntegerExp(0), - new StringExp(funcdecl->loc, const_cast("missing return expression"))); - } + funcdecl->error("has no `return` statement, but is expected to return a value of type `%s`", f->next->toChars()); else - e = new HaltExp(funcdecl->endloc); - e = new CommaExp(Loc(), e, f->next->defaultInit()); - e = expressionSemantic(e, sc2); - Statement *s = new ExpStatement(Loc(), e); - funcdecl->fbody = new CompoundStatement(Loc(), funcdecl->fbody, s); + funcdecl->error("no `return exp;` or `assert(0);` at end of function"); } } @@ -1162,15 +1148,7 @@ public: if (cd) { - if (!global.params.is64bit && - global.params.isWindows && - !funcdecl->isStatic() && !sbody->usesEH() && !global.params.trace) - { - /* The back end uses the "jmonitor" hack for syncing; - * no need to do the sync at this level. - */ - } - else + if (target.libraryObjectMonitors(funcdecl, sbody)) { Expression *vsync; if (funcdecl->isStatic()) diff --git a/gcc/d/dmd/statement.c b/gcc/d/dmd/statement.c index 2f7b7e13ffc..1f8e5122b1a 100644 --- a/gcc/d/dmd/statement.c +++ b/gcc/d/dmd/statement.c @@ -31,6 +31,7 @@ StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f); bool checkEscapeRef(Scope *sc, Expression *e, bool gag); VarDeclaration *copyToTemp(StorageClass stc, const char *name, Expression *e); Statement *makeTupleForeachStatic(Scope *sc, ForeachStatement *fs, bool needExpansion); +bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps); Identifier *fixupLabelName(Scope *sc, Identifier *ident) { @@ -504,12 +505,19 @@ Statement *DtorExpStatement::syntaxCopy() CompileStatement::CompileStatement(Loc loc, Expression *exp) : Statement(loc) { - this->exp = exp; + this->exps = new Expressions(); + this->exps->push(exp); +} + +CompileStatement::CompileStatement(Loc loc, Expressions *exps) + : Statement(loc) +{ + this->exps = exps; } Statement *CompileStatement::syntaxCopy() { - return new CompileStatement(loc, exp->syntaxCopy()); + return new CompileStatement(loc, Expression::arraySyntaxCopy(exps)); } static Statements *errorStatements() @@ -519,32 +527,36 @@ static Statements *errorStatements() return a; } -Statements *CompileStatement::flatten(Scope *sc) +static Statements *compileIt(CompileStatement *cs, Scope *sc) { - //printf("CompileStatement::flatten() %s\n", exp->toChars()); - StringExp *se = semanticString(sc, exp, "argument to mixin"); - if (!se) + //printf("CompileStatement::compileIt() %s\n", exp->toChars()); + OutBuffer buf; + if (expressionsToString(buf, sc, cs->exps)) return errorStatements(); - se = se->toUTF8(sc); unsigned errors = global.errors; - Parser p(loc, sc->_module, (utf8_t *)se->string, se->len, 0); + const size_t len = buf.length(); + const char *str = buf.extractChars(); + Parser p(cs->loc, sc->_module, (const utf8_t *)str, len, false); p.nextToken(); Statements *a = new Statements(); while (p.token.value != TOKeof) { Statement *s = p.parseStatement(PSsemi | PScurlyscope); - if (!s || p.errors) - { - assert(!p.errors || global.errors != errors); // make sure we caught all the cases + if (!s || global.errors != errors) return errorStatements(); - } a->push(s); } return a; } +Statements *CompileStatement::flatten(Scope *sc) +{ + //printf("CompileStatement::flatten() %s\n", exp->toChars()); + return compileIt(this, sc); +} + /******************************** CompoundStatement ***************************/ CompoundStatement::CompoundStatement(Loc loc, Statements *s) diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index 2d8b46c9487..c64e51a5be7 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -173,9 +173,10 @@ public: class CompileStatement : public Statement { public: - Expression *exp; + Expressions *exps; CompileStatement(Loc loc, Expression *exp); + CompileStatement(Loc loc, Expressions *exps); Statement *syntaxCopy(); Statements *flatten(Scope *sc); void accept(Visitor *v) { v->visit(this); } diff --git a/gcc/d/dmd/statementsem.c b/gcc/d/dmd/statementsem.c index 5579c1ca893..491d9c9bce9 100644 --- a/gcc/d/dmd/statementsem.c +++ b/gcc/d/dmd/statementsem.c @@ -581,7 +581,7 @@ public: { fs->error("constant value %s cannot be ref", ie->toChars()); } - else + else { fs->error("constant value %s cannot be ref", ident->toChars()); } diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index a91880fd0fc..f8f977c9aea 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -21,6 +21,7 @@ class Dsymbol; class Expression; class FuncDeclaration; class Parameter; +class Statement; class Type; class TypeFunction; class TypeTuple; @@ -30,6 +31,7 @@ struct TargetC { unsigned longsize; // size of a C 'long' or 'unsigned long' type unsigned long_doublesize; // size of a C 'long double' + Type *twchar_t; // C 'wchar_t' type }; struct TargetCPP @@ -44,6 +46,7 @@ struct TargetCPP const char *typeMangle(Type *t); Type *parameterType(Parameter *p); bool fundamentalType(const Type *t, bool& isFundamental); + unsigned derivedClassOffset(ClassDeclaration *baseClass); }; struct TargetObjC @@ -108,6 +111,7 @@ public: TypeTuple *toArgTypes(Type *t); bool isReturnOnStack(TypeFunction *tf, bool needsThis); Expression *getTargetInfo(const char* name, const Loc& loc); + bool libraryObjectMonitors(FuncDeclaration *fd, Statement *fbody); }; extern Target target; diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index dac1e85b433..fb842ac5b76 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -78,6 +78,7 @@ public: bool ismixin; // template declaration is only to be used as a mixin bool isstatic; // this is static template declaration Prot protection; + int inuse; // for recursive expansion detection TemplatePrevious *previous; // threaded list of previous instantiation attempts on stack diff --git a/gcc/d/dmd/templateparamsem.c b/gcc/d/dmd/templateparamsem.c index 11cd52e351b..d3e9b2390e9 100644 --- a/gcc/d/dmd/templateparamsem.c +++ b/gcc/d/dmd/templateparamsem.c @@ -25,7 +25,7 @@ public: { this->sc = sc; this->parameters = parameters; - this->result = false; + this->result = false; } void visit(TemplateTypeParameter *ttp) diff --git a/gcc/d/dmd/traits.c b/gcc/d/dmd/traits.c index b7c612c2af2..99b5457f031 100644 --- a/gcc/d/dmd/traits.c +++ b/gcc/d/dmd/traits.c @@ -40,6 +40,8 @@ void freeFieldinit(Scope *sc); Expression *resolve(Loc loc, Scope *sc, Dsymbol *s, bool hasOverloads); Package *resolveIsPackage(Dsymbol *sym); Expression *typeToExpression(Type *t); +Type *decoToType(const char *deco); +bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps); /************************************************ @@ -442,7 +444,6 @@ TraitsInitializer::TraitsInitializer() "derivedMembers", "isSame", "compiles", - "parameters", "getAliasThis", "getAttributes", "getFunctionAttributes", @@ -1032,6 +1033,34 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) ex = expressionSemantic(ex, sc); return ex; } + else if (e->ident == Id::toType) + { + if (dim != 1) + return dimError(e, 1, dim); + + Expression *ex = isExpression((*e->args)[0]); + if (!ex) + { + e->error("expression expected as second argument of __traits `%s`", e->ident->toChars()); + return new ErrorExp(); + } + ex = ex->ctfeInterpret(); + + StringExp *se = semanticString(sc, ex, "__traits(toType, string)"); + if (!se) + { + return new ErrorExp(); + } + Type *t = decoToType(se->toUTF8(sc)->toPtr()); + if (!t) + { + e->error("cannot determine `%s`", e->toChars()); + return new ErrorExp(); + } + ex = new TypeExp(e->loc, t); + ex = expressionSemantic(ex, sc); + return ex; + } else if (e->ident == Id::hasMember || e->ident == Id::getMember || e->ident == Id::getOverloads || @@ -1674,33 +1703,67 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) RootObject *o = (*e->args)[i]; Type *t = isType(o); - Expression *ex = t ? typeToExpression(t) : isExpression(o); - if (!ex && t) + while (t) { - Dsymbol *s; - t->resolve(e->loc, sc2, &ex, &t, &s); - if (t) + if (TypeMixin *tm = t->isTypeMixin()) { - typeSemantic(t, e->loc, sc2); - if (t->ty == Terror) + /* The mixin string could be a type or an expression. + * Have to try compiling it to see. + */ + OutBuffer buf; + if (expressionsToString(buf, sc, tm->exps)) + { + err = true; + break; + } + const size_t len = buf.length(); + const char *str = buf.extractChars(); + Parser p(e->loc, sc->_module, (const utf8_t *)str, len, false); + p.nextToken(); + //printf("p.loc.linnum = %d\n", p.loc.linnum); + + o = p.parseTypeOrAssignExp(TOKeof); + if (p.errors || p.token.value != TOKeof) + { err = true; + break; + } + t = isType(o); } - else if (s && s->errors) - err = true; + else + break; } - if (ex) + + if (!err) { - ex = expressionSemantic(ex, sc2); - ex = resolvePropertiesOnly(sc2, ex); - ex = ex->optimize(WANTvalue); - if (sc2->func && sc2->func->type->ty == Tfunction) + Expression *ex = t ? typeToExpression(t) : isExpression(o); + if (!ex && t) { - TypeFunction *tf = (TypeFunction *)sc2->func->type; - canThrow(ex, sc2->func, tf->isnothrow); + Dsymbol *s; + t->resolve(e->loc, sc2, &ex, &t, &s); + if (t) + { + typeSemantic(t, e->loc, sc2); + if (t->ty == Terror) + err = true; + } + else if (s && s->errors) + err = true; + } + if (ex) + { + ex = expressionSemantic(ex, sc2); + ex = resolvePropertiesOnly(sc2, ex); + ex = ex->optimize(WANTvalue); + if (sc2->func && sc2->func->type->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)sc2->func->type; + canThrow(ex, sc2->func, tf->isnothrow); + } + ex = checkGC(sc2, ex); + if (ex->op == TOKerror) + err = true; } - ex = checkGC(sc2, ex); - if (ex->op == TOKerror) - err = true; } // Carefully detach the scope from the parent and throw it away as diff --git a/gcc/d/dmd/typesem.c b/gcc/d/dmd/typesem.c index 496cfe3a855..670144d1282 100644 --- a/gcc/d/dmd/typesem.c +++ b/gcc/d/dmd/typesem.c @@ -18,6 +18,7 @@ #include "hdrgen.h" #include "id.h" #include "init.h" +#include "parse.h" #include "scope.h" #include "target.h" #include "template.h" @@ -25,6 +26,7 @@ Expression *typeToExpression(Type *t); Expression *typeToExpressionHelper(TypeQualified *t, Expression *e, size_t i = 0); +bool expressionsToString(OutBuffer &buf, Scope *sc, Expressions *exps); char *MODtoChars(MOD mod); class TypeToExpressionVisitor : public Visitor @@ -76,6 +78,11 @@ public: { result = typeToExpressionHelper(t, new ScopeExp(t->loc, t->tempinst)); } + + void visit(TypeMixin *t) + { + result = new TypeExp(t->loc, t); + } }; /* We've mistakenly parsed this as a type. @@ -84,6 +91,8 @@ public: */ Expression *typeToExpression(Type *t) { + if (t->mod) + return NULL; TypeToExpressionVisitor v = TypeToExpressionVisitor(t); t->accept(&v); return v.result; @@ -177,6 +186,48 @@ static Expression *semanticLength(Scope *sc, TupleDeclaration *s, Expression *ex return exp; } +/****************************************** + * Compile the MixinType, returning the type or expression AST. + * + * Doesn't run semantic() on the returned object. + * Params: + * tm = mixin to compile as a type or expression + * loc = location for error messages + * sc = context + * Return: + * null if error, else RootObject AST as parsed + */ +RootObject *compileTypeMixin(TypeMixin *tm, Loc loc, Scope *sc) +{ + OutBuffer buf; + if (expressionsToString(buf, sc, tm->exps)) + return NULL; + + const unsigned errors = global.errors; + const size_t len = buf.length(); + const char *str = buf.extractChars(); + Parser p(loc, sc->_module, (const utf8_t *)str, len, false); + p.nextToken(); + //printf("p.loc.linnum = %d\n", p.loc.linnum); + + RootObject *o = p.parseTypeOrAssignExp(TOKeof); + if (errors != global.errors) + { + assert(global.errors != errors); // should have caught all these cases + return NULL; + } + if (p.token.value != TOKeof) + { + ::error(loc, "incomplete mixin type `%s`", str); + return NULL; + } + + Type *t = isType(o); + Expression *e = t ? typeToExpression(t) : isExpression(o); + + return (!e && t) ? (RootObject *)t : (RootObject *)e; +} + /****************************************** * Perform semantic analysis on a type. * Params: @@ -440,7 +491,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc) // Deal with the case where we thought the index was a type, but // in reality it was an expression. if (mtype->index->ty == Tident || mtype->index->ty == Tinstance || mtype->index->ty == Tsarray || - mtype->index->ty == Ttypeof || mtype->index->ty == Treturn) + mtype->index->ty == Ttypeof || mtype->index->ty == Treturn || mtype->index->ty == Tmixin) { Expression *e; Type *t; @@ -1072,6 +1123,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc) mtype->exp->ident != Id::getMember && mtype->exp->ident != Id::parent && mtype->exp->ident != Id::child && + mtype->exp->ident != Id::toType && mtype->exp->ident != Id::getOverloads && mtype->exp->ident != Id::getVirtualFunctions && mtype->exp->ident != Id::getVirtualMethods && @@ -1275,7 +1327,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc) void visit(TypeStruct *mtype) { - //printf("TypeStruct::semantic('%s')\n", mtype->sym->toChars()); + //printf("TypeStruct::semantic('%s')\n", mtype->toChars()); if (mtype->deco) { if (sc && sc->cppmangle != CPPMANGLEdefault) @@ -1304,7 +1356,7 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc) void visit(TypeClass *mtype) { - //printf("TypeClass::semantic(%s)\n", mtype->sym->toChars()); + //printf("TypeClass::semantic(%s)\n", mtype->toChars()); if (mtype->deco) { if (sc && sc->cppmangle != CPPMANGLEdefault) @@ -1386,6 +1438,25 @@ Type *typeSemantic(Type *type, const Loc &loc, Scope *sc) Type *t = new TypeTuple(args); result = typeSemantic(t, loc, sc); } + + void visit(TypeMixin *mtype) + { + //printf("TypeMixin::semantic() %s\n", mtype->toChars()); + + Expression *e = NULL; + Type *t = NULL; + Dsymbol *s = NULL; + mtype->resolve(loc, sc, &e, &t, &s); + + if (t && t->ty != Terror) + { + result = t; + return; + } + + ::error(mtype->loc, "`mixin(%s)` does not give a valid type", mtype->obj->toChars()); + return error(); + } }; TypeSemanticVisitor v(loc, sc); type->accept(&v); diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h index a274e6f083e..09ba2024e30 100644 --- a/gcc/d/dmd/visitor.h +++ b/gcc/d/dmd/visitor.h @@ -81,7 +81,9 @@ class TypeClass; class TypeTuple; class TypeSlice; class TypeNull; +class TypeNoreturn; class TypeTraits; +class TypeMixin; class Dsymbol; @@ -374,7 +376,9 @@ public: virtual void visit(TypeTuple *t) { visit((Type *)t); } virtual void visit(TypeSlice *t) { visit((TypeNext *)t); } virtual void visit(TypeNull *t) { visit((Type *)t); } + virtual void visit(TypeNoreturn *t) { visit((Type *)t); } virtual void visit(TypeTraits *t) { visit((Type *)t); } + virtual void visit(TypeMixin *t) { visit((Type *)t); } virtual void visit(Dsymbol *) { assert(0); } diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index ee3e3d5bdd5..41d07a7b70e 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -1068,6 +1068,13 @@ public: add_stmt (return_expr (decl)); } + else if (tf->next->ty == Tnoreturn) + { + /* Returning an expression that has no value, but has a side effect + that should never return. */ + add_stmt (build_expr_dtor (s->exp)); + add_stmt (return_expr (NULL_TREE)); + } else { /* Convert for initializing the DECL_RESULT. */ diff --git a/gcc/d/types.cc b/gcc/d/types.cc index 3cddfc5dd46..924d8298211 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -603,6 +603,12 @@ public: t->ctype = ptr_type_node; } + /* Bottom type used for functions that never return. */ + + void visit (TypeNoreturn *t) + { + t->ctype = void_type_node; + } /* Basic Data Types. */ @@ -852,7 +858,46 @@ public: tree basetype = (t->sym->memtype) ? build_ctype (t->sym->memtype) : void_type_node; - if (!INTEGRAL_TYPE_P (basetype) || TREE_CODE (basetype) == BOOLEAN_TYPE) + if (t->sym->isSpecial ()) + { + /* Special enums are opaque types that bind to C types. */ + const char *ident = t->toChars (); + Type *underlying = NULL; + + /* Skip over the prefixing `__c_'. */ + gcc_assert (strncmp (ident, "__c_", strlen ("__c_")) == 0); + ident = ident + strlen ("__c_"); + + /* To keep things compatible within the code generation we stick to + mapping to equivalent D types. However it should be OK to use the + GCC provided C types here as the front-end enforces that everything + must be explicitly cast from a D type to any of the opaque types. */ + if (strcmp (ident, "long") == 0) + underlying = build_frontend_type (long_integer_type_node); + else if (strcmp (ident, "ulong") == 0) + underlying = build_frontend_type (long_unsigned_type_node); + else if (strcmp (ident, "wchar_t") == 0) + underlying = target.c.twchar_t; + else if (strcmp (ident, "longlong") == 0) + underlying = build_frontend_type (long_long_integer_type_node); + else if (strcmp (ident, "ulonglong") == 0) + underlying = build_frontend_type (long_long_unsigned_type_node); + else if (strcmp (ident, "long_double") == 0) + underlying = build_frontend_type (long_double_type_node); + else if (strcmp (ident, "complex_real") == 0) + underlying = build_frontend_type (complex_long_double_type_node); + else if (strcmp (ident, "complex_float") == 0) + underlying = build_frontend_type (complex_float_type_node); + else if (strcmp (ident, "complex_double") == 0) + underlying = build_frontend_type (complex_double_type_node); + + /* Conversion failed or there's an unhandled special type. */ + gcc_assert (underlying != NULL); + + t->ctype = build_variant_type_copy (build_ctype (underlying)); + build_type_decl (t->ctype, t->sym); + } + else if (!INTEGRAL_TYPE_P (basetype) || TREE_CODE (basetype) == BOOLEAN_TYPE) { /* Enums in D2 can have a base type that is not necessarily integral. For these, we simplify this a little by using the base type directly