aboutsummaryrefslogtreecommitdiff
path: root/gcc/d/expr.cc
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /gcc/d/expr.cc
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'gcc/d/expr.cc')
-rw-r--r--gcc/d/expr.cc3138
1 files changed, 3138 insertions, 0 deletions
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
new file mode 100644
index 0000000..9a1aad4
--- /dev/null
+++ b/gcc/d/expr.cc
@@ -0,0 +1,3138 @@
+/* expr.cc -- Lower D frontend expressions to GCC trees.
+ Copyright (C) 2015-2018 Free Software Foundation, Inc.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "dmd/aggregate.h"
+#include "dmd/ctfe.h"
+#include "dmd/declaration.h"
+#include "dmd/expression.h"
+#include "dmd/identifier.h"
+#include "dmd/init.h"
+#include "dmd/module.h"
+#include "dmd/mtype.h"
+#include "dmd/template.h"
+
+#include "tree.h"
+#include "fold-const.h"
+#include "diagnostic.h"
+#include "langhooks.h"
+#include "tm.h"
+#include "function.h"
+#include "toplev.h"
+#include "varasm.h"
+#include "predict.h"
+#include "stor-layout.h"
+
+#include "d-tree.h"
+
+
+/* Implements the visitor interface to build the GCC trees of all Expression
+ AST classes emitted from the D Front-end.
+ All visit methods accept one parameter E, which holds the frontend AST
+ of the expression to compile. They also don't return any value, instead
+ generated code is cached in RESULT_ and returned from the caller. */
+
+class ExprVisitor : public Visitor
+{
+ using Visitor::visit;
+
+ tree result_;
+ bool constp_;
+
+ /* Determine if type is a struct that has a postblit. */
+
+ bool needs_postblit (Type *t)
+ {
+ t = t->baseElemOf ();
+
+ if (t->ty == Tstruct)
+ {
+ StructDeclaration *sd = ((TypeStruct *) t)->sym;
+ if (sd->postblit)
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Determine if type is a struct that has a destructor. */
+
+ bool needs_dtor (Type *t)
+ {
+ t = t->baseElemOf ();
+
+ if (t->ty == Tstruct)
+ {
+ StructDeclaration *sd = ((TypeStruct *) t)->sym;
+ if (sd->dtor)
+ return true;
+ }
+
+ return false;
+ }
+
+ /* Determine if expression is suitable lvalue. */
+
+ bool lvalue_p (Expression *e)
+ {
+ return ((e->op != TOKslice && e->isLvalue ())
+ || (e->op == TOKslice && ((UnaExp *) e)->e1->isLvalue ())
+ || (e->op == TOKcast && ((UnaExp *) e)->e1->isLvalue ()));
+ }
+
+ /* Build an expression of code CODE, data type TYPE, and operands ARG0 and
+ ARG1. Perform relevant conversions needed for correct code operations. */
+
+ tree binary_op (tree_code code, tree type, tree arg0, tree arg1)
+ {
+ tree t0 = TREE_TYPE (arg0);
+ tree t1 = TREE_TYPE (arg1);
+ tree ret = NULL_TREE;
+
+ bool unsignedp = TYPE_UNSIGNED (t0) || TYPE_UNSIGNED (t1);
+
+ /* Deal with float mod expressions immediately. */
+ if (code == FLOAT_MOD_EXPR)
+ return build_float_modulus (type, arg0, arg1);
+
+ if (POINTER_TYPE_P (t0) && INTEGRAL_TYPE_P (t1))
+ return build_nop (type, build_offset_op (code, arg0, arg1));
+
+ if (INTEGRAL_TYPE_P (t0) && POINTER_TYPE_P (t1))
+ return build_nop (type, build_offset_op (code, arg1, arg0));
+
+ if (POINTER_TYPE_P (t0) && POINTER_TYPE_P (t1))
+ {
+ gcc_assert (code == MINUS_EXPR);
+ tree ptrtype = lang_hooks.types.type_for_mode (ptr_mode, 0);
+
+ /* POINTER_DIFF_EXPR requires a signed integer type of the same size as
+ pointers. If some platform cannot provide that, or has a larger
+ ptrdiff_type to support differences larger than half the address
+ space, cast the pointers to some larger integer type and do the
+ computations in that type. */
+ if (TYPE_PRECISION (ptrtype) > TYPE_PRECISION (t0))
+ ret = fold_build2 (MINUS_EXPR, ptrtype,
+ d_convert (ptrtype, arg0),
+ d_convert (ptrtype, arg1));
+ else
+ ret = fold_build2 (POINTER_DIFF_EXPR, ptrtype, arg0, arg1);
+ }
+ else if (INTEGRAL_TYPE_P (type) && (TYPE_UNSIGNED (type) != unsignedp))
+ {
+ tree inttype = (unsignedp)
+ ? d_unsigned_type (type) : d_signed_type (type);
+ ret = fold_build2 (code, inttype, arg0, arg1);
+ }
+ else
+ {
+ /* If the operation needs excess precision. */
+ tree eptype = excess_precision_type (type);
+ if (eptype != NULL_TREE)
+ {
+ arg0 = d_convert (eptype, arg0);
+ arg1 = d_convert (eptype, arg1);
+ }
+ else
+ {
+ /* Front-end does not do this conversion and GCC does not
+ always do it right. */
+ if (COMPLEX_FLOAT_TYPE_P (t0) && !COMPLEX_FLOAT_TYPE_P (t1))
+ arg1 = d_convert (t0, arg1);
+ else if (COMPLEX_FLOAT_TYPE_P (t1) && !COMPLEX_FLOAT_TYPE_P (t0))
+ arg0 = d_convert (t1, arg0);
+
+ eptype = type;
+ }
+
+ ret = fold_build2 (code, eptype, arg0, arg1);
+ }
+
+ return d_convert (type, ret);
+ }
+
+ /* Build a binary expression of code CODE, assigning the result into E1. */
+
+ tree binop_assignment (tree_code code, Expression *e1, Expression *e2)
+ {
+ /* Skip casts for lhs assignment. */
+ Expression *e1b = e1;
+ while (e1b->op == TOKcast)
+ {
+ CastExp *ce = (CastExp *) e1b;
+ gcc_assert (same_type_p (ce->type, ce->to));
+ e1b = ce->e1;
+ }
+
+ /* Stabilize LHS for assignment. */
+ tree lhs = build_expr (e1b);
+ tree lexpr = stabilize_expr (&lhs);
+
+ /* The LHS expression could be an assignment, to which its operation gets
+ lost during gimplification. */
+ if (TREE_CODE (lhs) == MODIFY_EXPR)
+ {
+ /* If LHS has side effects, call stabilize_reference on it, so it can
+ be evaluated multiple times. */
+ if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)))
+ lhs = build_assign (MODIFY_EXPR,
+ stabilize_reference (TREE_OPERAND (lhs, 0)),
+ TREE_OPERAND (lhs, 1));
+
+ lexpr = compound_expr (lexpr, lhs);
+ lhs = TREE_OPERAND (lhs, 0);
+ }
+
+ lhs = stabilize_reference (lhs);
+
+ /* Save RHS, to ensure that the expression is evaluated before LHS. */
+ tree rhs = build_expr (e2);
+ tree rexpr = d_save_expr (rhs);
+
+ rhs = this->binary_op (code, build_ctype (e1->type),
+ convert_expr (lhs, e1b->type, e1->type), rexpr);
+ if (TREE_SIDE_EFFECTS (rhs))
+ rhs = compound_expr (rexpr, rhs);
+
+ tree expr = modify_expr (lhs, convert_expr (rhs, e1->type, e1b->type));
+ return compound_expr (lexpr, expr);
+ }
+
+public:
+ ExprVisitor (bool constp)
+ {
+ this->result_ = NULL_TREE;
+ this->constp_ = constp;
+ }
+
+ tree result (void)
+ {
+ return this->result_;
+ }
+
+ /* Visitor interfaces, each Expression class should have
+ overridden the default. */
+
+ void visit (Expression *)
+ {
+ gcc_unreachable ();
+ }
+
+ /* Build a conditional expression. If either the second or third
+ expression is void, then the resulting type is void. Otherwise
+ they are implicitly converted to a common type. */
+
+ void visit (CondExp *e)
+ {
+ tree cond = convert_for_condition (build_expr (e->econd),
+ e->econd->type);
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+
+ if (e->type->ty != Tvoid)
+ {
+ t1 = convert_expr (t1, e->e1->type, e->type);
+ t2 = convert_expr (t2, e->e2->type, e->type);
+ }
+
+ this->result_ = build_condition (build_ctype (e->type), cond, t1, t2);
+ }
+
+ /* Build an identity comparison expression. Operands go through the
+ usual conversions to bring them to a common type before comparison.
+ The result type is bool. */
+
+ void visit (IdentityExp *e)
+ {
+ tree_code code = (e->op == TOKidentity) ? EQ_EXPR : NE_EXPR;
+ Type *tb1 = e->e1->type->toBasetype ();
+ Type *tb2 = e->e2->type->toBasetype ();
+
+ if ((tb1->ty == Tsarray || tb1->ty == Tarray)
+ && (tb2->ty == Tsarray || tb2->ty == Tarray))
+ {
+ /* For static and dynamic arrays, identity is defined as referring to
+ the same array elements and the same number of elements. */
+ tree t1 = d_array_convert (e->e1);
+ tree t2 = d_array_convert (e->e2);
+ this->result_ = d_convert (build_ctype (e->type),
+ build_boolop (code, t1, t2));
+ }
+ else if (tb1->isfloating () && tb1->ty != Tvector)
+ {
+ /* For floating-point values, identity is defined as the bits in the
+ operands being identical. */
+ tree t1 = d_save_expr (build_expr (e->e1));
+ tree t2 = d_save_expr (build_expr (e->e2));
+
+ tree tmemcmp = builtin_decl_explicit (BUILT_IN_MEMCMP);
+ tree size = size_int (TYPE_PRECISION (TREE_TYPE (t1)) / BITS_PER_UNIT);
+
+ tree result = build_call_expr (tmemcmp, 3, build_address (t1),
+ build_address (t2), size);
+ this->result_ = build_boolop (code, result, integer_zero_node);
+ }
+ else if (tb1->ty == Tstruct)
+ {
+ /* For struct objects, identity is defined as bits in operands being
+ identical also. Alignment holes in structs are ignored. */
+ StructDeclaration *sd = ((TypeStruct *) tb1)->sym;
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+
+ gcc_assert (same_type_p (tb1, tb2));
+
+ this->result_ = build_struct_comparison (code, sd, t1, t2);
+ }
+ else
+ {
+ /* For operands of other types, identity is defined as being the
+ same as equality expressions. */
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+ this->result_ = d_convert (build_ctype (e->type),
+ build_boolop (code, t1, t2));
+ }
+ }
+
+ /* Build an equality expression, which compare the two operands for either
+ equality or inequality. Operands go through the usual conversions to bring
+ them to a common type before comparison. The result type is bool. */
+
+ void visit (EqualExp *e)
+ {
+ Type *tb1 = e->e1->type->toBasetype ();
+ Type *tb2 = e->e2->type->toBasetype ();
+ tree_code code = (e->op == TOKequal) ? EQ_EXPR : NE_EXPR;
+
+ if ((tb1->ty == Tsarray || tb1->ty == Tarray)
+ && (tb2->ty == Tsarray || tb2->ty == Tarray))
+ {
+ /* For static and dynamic arrays, equality is defined as the lengths of
+ the arrays matching, and all the elements are equal. */
+ Type *t1elem = tb1->nextOf ()->toBasetype ();
+ Type *t2elem = tb1->nextOf ()->toBasetype ();
+
+ /* Check if comparisons of arrays can be optimized using memcmp.
+ This will inline EQ expressions as:
+ e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0;
+ Or when generating a NE expression:
+ e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0; */
+ if ((t1elem->isintegral () || t1elem->ty == Tvoid
+ || (t1elem->ty == Tstruct && !((TypeStruct *)t1elem)->sym->xeq))
+ && t1elem->ty == t2elem->ty)
+ {
+ tree t1 = d_array_convert (e->e1);
+ tree t2 = d_array_convert (e->e2);
+ tree result;
+
+ /* Make temporaries to prevent multiple evaluations. */
+ tree t1saved = d_save_expr (t1);
+ tree t2saved = d_save_expr (t2);
+
+ /* Length of arrays, for comparisons done before calling memcmp. */
+ tree t1len = d_array_length (t1saved);
+ tree t2len = d_array_length (t2saved);
+
+ /* Reference to array data. */
+ tree t1ptr = d_array_ptr (t1saved);
+ tree t2ptr = d_array_ptr (t2saved);
+
+ /* Compare arrays using memcmp if possible, otherwise for structs,
+ each field is compared inline. */
+ if (t1elem->ty != Tstruct
+ || identity_compare_p (((TypeStruct *) t1elem)->sym))
+ {
+ tree size = size_mult_expr (t1len, size_int (t1elem->size ()));
+ tree tmemcmp = builtin_decl_explicit (BUILT_IN_MEMCMP);
+
+ result = build_call_expr (tmemcmp, 3, t1ptr, t2ptr, size);
+ result = build_boolop (code, result, integer_zero_node);
+ }
+ else
+ {
+ StructDeclaration *sd = ((TypeStruct *) t1elem)->sym;
+
+ result = build_array_struct_comparison (code, sd, t1len,
+ t1ptr, t2ptr);
+ }
+
+ /* Check array length first before passing to memcmp.
+ For equality expressions, this becomes:
+ (e1.length == 0 || memcmp);
+ Otherwise for inequality:
+ (e1.length != 0 && memcmp); */
+ tree tsizecmp = build_boolop (code, t1len, size_zero_node);
+ if (e->op == TOKequal)
+ result = build_boolop (TRUTH_ORIF_EXPR, tsizecmp, result);
+ else
+ result = build_boolop (TRUTH_ANDIF_EXPR, tsizecmp, result);
+
+ /* Finally, check if lengths of both arrays match if dynamic.
+ The frontend should have already guaranteed that static arrays
+ have same size. */
+ if (tb1->ty == Tsarray && tb2->ty == Tsarray)
+ gcc_assert (tb1->size () == tb2->size ());
+ else
+ {
+ tree tlencmp = build_boolop (code, t1len, t2len);
+ if (e->op == TOKequal)
+ result = build_boolop (TRUTH_ANDIF_EXPR, tlencmp, result);
+ else
+ result = build_boolop (TRUTH_ORIF_EXPR, tlencmp, result);
+ }
+
+ /* Ensure left-to-right order of evaluation. */
+ if (TREE_SIDE_EFFECTS (t2))
+ result = compound_expr (t2saved, result);
+
+ if (TREE_SIDE_EFFECTS (t1))
+ result = compound_expr (t1saved, result);
+
+ this->result_ = result;
+ }
+ else
+ {
+ /* Use _adEq2() to compare each element. */
+ Type *t1array = t1elem->arrayOf ();
+ tree result = build_libcall (LIBCALL_ADEQ2, e->type, 3,
+ d_array_convert (e->e1),
+ d_array_convert (e->e2),
+ build_typeinfo (t1array));
+
+ if (e->op == TOKnotequal)
+ result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result);
+
+ this->result_ = result;
+ }
+ }
+ else if (tb1->ty == Tstruct)
+ {
+ /* Equality for struct objects means the logical product of all
+ equality results of the corresponding object fields. */
+ StructDeclaration *sd = ((TypeStruct *) tb1)->sym;
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+
+ gcc_assert (same_type_p (tb1, tb2));
+
+ this->result_ = build_struct_comparison (code, sd, t1, t2);
+ }
+ else if (tb1->ty == Taarray && tb2->ty == Taarray)
+ {
+ /* Use _aaEqual() for associative arrays. */
+ TypeAArray *taa1 = (TypeAArray *) tb1;
+ tree result = build_libcall (LIBCALL_AAEQUAL, e->type, 3,
+ build_typeinfo (taa1),
+ build_expr (e->e1),
+ build_expr (e->e2));
+
+ if (e->op == TOKnotequal)
+ result = build1 (TRUTH_NOT_EXPR, build_ctype (e->type), result);
+
+ this->result_ = result;
+ }
+ else
+ {
+ /* For operands of other types, equality is defined as the bit pattern
+ of the type matches exactly. */
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+
+ this->result_ = d_convert (build_ctype (e->type),
+ build_boolop (code, t1, t2));
+ }
+ }
+
+ /* Build an `in' expression. This is a condition to see if an element
+ exists in an associative array. The result is a pointer to the
+ element, or null if false. */
+
+ void visit (InExp *e)
+ {
+ Type *tb2 = e->e2->type->toBasetype ();
+ gcc_assert (tb2->ty == Taarray);
+
+ Type *tkey = ((TypeAArray *) tb2)->index->toBasetype ();
+ tree key = convert_expr (build_expr (e->e1), e->e1->type, tkey);
+
+ /* Build a call to _aaInX(). */
+ this->result_ = build_libcall (LIBCALL_AAINX, e->type, 3,
+ build_expr (e->e2),
+ build_typeinfo (tkey),
+ build_address (key));
+ }
+
+ /* Build a relational expression. The result type is bool. */
+
+ void visit (CmpExp *e)
+ {
+ Type *tb1 = e->e1->type->toBasetype ();
+ Type *tb2 = e->e2->type->toBasetype ();
+
+ tree result;
+ tree_code code;
+
+ switch (e->op)
+ {
+ case TOKle:
+ code = LE_EXPR;
+ break;
+
+ case TOKlt:
+ code = LT_EXPR;
+ break;
+
+ case TOKge:
+ code = GE_EXPR;
+ break;
+
+ case TOKgt:
+ code = GT_EXPR;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ if ((tb1->ty == Tsarray || tb1->ty == Tarray)
+ && (tb2->ty == Tsarray || tb2->ty == Tarray))
+ {
+ /* For static and dynamic arrays, the result of the relational op is
+ the result of the operator applied to the first non-equal element
+ of the array. If two arrays compare equal, but are of different
+ lengths, the shorter array compares as less than the longer. */
+ Type *telem = tb1->nextOf ()->toBasetype ();
+
+ tree call = build_libcall (LIBCALL_ADCMP2, Type::tint32, 3,
+ d_array_convert (e->e1),
+ d_array_convert (e->e2),
+ build_typeinfo (telem->arrayOf ()));
+ result = build_boolop (code, call, integer_zero_node);
+
+ this->result_ = d_convert (build_ctype (e->type), result);
+ return;
+ }
+
+ /* Simple comparison. */
+ result = build_boolop (code, build_expr (e->e1), build_expr (e->e2));
+ this->result_ = d_convert (build_ctype (e->type), result);
+ }
+
+ /* Build an `and if' expression. If the right operand expression is void,
+ then the resulting type is void. Otherwise the result is bool. */
+
+ void visit (AndAndExp *e)
+ {
+ if (e->e2->type->toBasetype ()->ty != Tvoid)
+ {
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+
+ t1 = convert_for_condition (t1, e->e1->type);
+ t2 = convert_for_condition (t2, e->e2->type);
+
+ this->result_ = d_convert (build_ctype (e->type),
+ build_boolop (TRUTH_ANDIF_EXPR, t1, t2));
+ }
+ else
+ {
+ tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type);
+ tree t2 = build_expr_dtor (e->e2);
+
+ this->result_ = build_condition (build_ctype (e->type),
+ t1, t2, void_node);
+ }
+ }
+
+ /* Build an `or if' expression. If the right operand expression is void,
+ then the resulting type is void. Otherwise the result is bool. */
+
+ void visit (OrOrExp *e)
+ {
+ if (e->e2->type->toBasetype ()->ty != Tvoid)
+ {
+ tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type);
+ tree t2 = convert_for_condition (build_expr (e->e2), e->e2->type);
+
+ this->result_ = d_convert (build_ctype (e->type),
+ build_boolop (TRUTH_ORIF_EXPR, t1, t2));
+ }
+ else
+ {
+ tree t1 = convert_for_condition (build_expr (e->e1), e->e1->type);
+ tree t2 = build_expr_dtor (e->e2);
+ tree cond = build1 (TRUTH_NOT_EXPR, d_bool_type, t1);
+
+ this->result_ = build_condition (build_ctype (e->type),
+ cond, t2, void_node);
+ }
+ }
+
+ /* Build a binary operand expression. Operands go through usual arithmetic
+ conversions to bring them to a common type before evaluating. */
+
+ void visit (BinExp *e)
+ {
+ tree_code code;
+
+ switch (e->op)
+ {
+ case TOKadd:
+ case TOKmin:
+ if ((e->e1->type->isreal () && e->e2->type->isimaginary ())
+ || (e->e1->type->isimaginary () && e->e2->type->isreal ()))
+ {
+ /* If the result is complex, then we can shortcut binary_op.
+ Frontend should have already validated types and sizes. */
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+
+ if (e->op == TOKmin)
+ t2 = build1 (NEGATE_EXPR, TREE_TYPE (t2), t2);
+
+ if (e->e1->type->isreal ())
+ this->result_ = complex_expr (build_ctype (e->type), t1, t2);
+ else
+ this->result_ = complex_expr (build_ctype (e->type), t2, t1);
+
+ return;
+ }
+ else
+ code = (e->op == TOKadd)
+ ? PLUS_EXPR : MINUS_EXPR;
+ break;
+
+ case TOKmul:
+ code = MULT_EXPR;
+ break;
+
+ case TOKdiv:
+ code = e->e1->type->isintegral ()
+ ? TRUNC_DIV_EXPR : RDIV_EXPR;
+ break;
+
+ case TOKmod:
+ code = e->e1->type->isfloating ()
+ ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR;
+ break;
+
+ case TOKand:
+ code = BIT_AND_EXPR;
+ break;
+
+ case TOKor:
+ code = BIT_IOR_EXPR;
+ break;
+
+ case TOKxor:
+ code = BIT_XOR_EXPR;
+ break;
+
+ case TOKshl:
+ code = LSHIFT_EXPR;
+ break;
+
+ case TOKshr:
+ code = RSHIFT_EXPR;
+ break;
+
+ case TOKushr:
+ code = UNSIGNED_RSHIFT_EXPR;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ this->result_ = this->binary_op (code, build_ctype (e->type),
+ build_expr (e->e1), build_expr (e->e2));
+ }
+
+
+ /* Build a concat expression, which concatenates two or more arrays of the
+ same type, producing a dynamic array with the result. If one operand
+ is an element type, that element is converted to an array of length 1. */
+
+ void visit (CatExp *e)
+ {
+ Type *tb1 = e->e1->type->toBasetype ();
+ Type *tb2 = e->e2->type->toBasetype ();
+ Type *etype;
+
+ if (tb1->ty == Tarray || tb1->ty == Tsarray)
+ etype = tb1->nextOf ();
+ else
+ etype = tb2->nextOf ();
+
+ vec<tree, va_gc> *elemvars = NULL;
+ tree result;
+
+ if (e->e1->op == TOKcat)
+ {
+ /* Flatten multiple concatenations to an array.
+ So the expression ((a ~ b) ~ c) becomes [a, b, c] */
+ int ndims = 2;
+
+ for (Expression *ex = e->e1; ex->op == TOKcat;)
+ {
+ if (ex->op == TOKcat)
+ {
+ ex = ((CatExp *) ex)->e1;
+ ndims++;
+ }
+ }
+
+ /* Store all concatenation args to a temporary byte[][ndims] array. */
+ Type *targselem = Type::tint8->arrayOf ();
+ tree var = create_temporary_var (make_array_type (targselem, ndims));
+ tree init = build_constructor (TREE_TYPE (var), NULL);
+ vec_safe_push (elemvars, var);
+
+ /* Loop through each concatenation from right to left. */
+ vec<constructor_elt, va_gc> *elms = NULL;
+ CatExp *ce = e;
+ int dim = ndims - 1;
+
+ for (Expression *oe = ce->e2; oe != NULL;
+ (ce->e1->op != TOKcat
+ ? (oe = ce->e1)
+ : (ce = (CatExp *)ce->e1, oe = ce->e2)))
+ {
+ tree arg = d_array_convert (etype, oe, &elemvars);
+ tree index = size_int (dim);
+ CONSTRUCTOR_APPEND_ELT (elms, index, d_save_expr (arg));
+
+ /* Finished pushing all arrays. */
+ if (oe == ce->e1)
+ break;
+
+ dim -= 1;
+ }
+
+ /* Check there is no logic bug in constructing byte[][] of arrays. */
+ gcc_assert (dim == 0);
+ CONSTRUCTOR_ELTS (init) = elms;
+ DECL_INITIAL (var) = init;
+
+ tree arrs = d_array_value (build_ctype (targselem->arrayOf ()),
+ size_int (ndims), build_address (var));
+
+ result = build_libcall (LIBCALL_ARRAYCATNTX, e->type, 2,
+ build_typeinfo (e->type), arrs);
+ }
+ else
+ {
+ /* Handle single concatenation (a ~ b). */
+ result = build_libcall (LIBCALL_ARRAYCATT, e->type, 3,
+ build_typeinfo (e->type),
+ d_array_convert (etype, e->e1, &elemvars),
+ d_array_convert (etype, e->e2, &elemvars));
+ }
+
+ for (size_t i = 0; i < vec_safe_length (elemvars); ++i)
+ result = bind_expr ((*elemvars)[i], result);
+
+ this->result_ = result;
+ }
+
+ /* Build an assignment operator expression. The right operand is implicitly
+ converted to the type of the left operand, and assigned to it. */
+
+ void visit (BinAssignExp *e)
+ {
+ tree_code code;
+ Expression *e1b = e->e1;
+
+ switch (e->op)
+ {
+ case TOKaddass:
+ code = PLUS_EXPR;
+ break;
+
+ case TOKminass:
+ code = MINUS_EXPR;
+ break;
+
+ case TOKmulass:
+ code = MULT_EXPR;
+ break;
+
+ case TOKdivass:
+ code = e->e1->type->isintegral ()
+ ? TRUNC_DIV_EXPR : RDIV_EXPR;
+ break;
+
+ case TOKmodass:
+ code = e->e1->type->isfloating ()
+ ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR;
+ break;
+
+ case TOKandass:
+ code = BIT_AND_EXPR;
+ break;
+
+ case TOKorass:
+ code = BIT_IOR_EXPR;
+ break;
+
+ case TOKxorass:
+ code = BIT_XOR_EXPR;
+ break;
+
+ case TOKpowass:
+ gcc_unreachable ();
+
+ case TOKshlass:
+ code = LSHIFT_EXPR;
+ break;
+
+ case TOKshrass:
+ case TOKushrass:
+ /* Use the original lhs type before it was promoted. The left operand
+ of `>>>=' does not undergo integral promotions before shifting.
+ Strip off casts just incase anyway. */
+ while (e1b->op == TOKcast)
+ {
+ CastExp *ce = (CastExp *) e1b;
+ gcc_assert (same_type_p (ce->type, ce->to));
+ e1b = ce->e1;
+ }
+ code = (e->op == TOKshrass) ? RSHIFT_EXPR : UNSIGNED_RSHIFT_EXPR;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ tree exp = this->binop_assignment (code, e1b, e->e2);
+ this->result_ = convert_expr (exp, e1b->type, e->type);
+ }
+
+ /* Build a concat assignment expression. The right operand is appended
+ to the the left operand. */
+
+ void visit (CatAssignExp *e)
+ {
+ Type *tb1 = e->e1->type->toBasetype ();
+ Type *tb2 = e->e2->type->toBasetype ();
+ Type *etype = tb1->nextOf ()->toBasetype ();
+
+ if (tb1->ty == Tarray && tb2->ty == Tdchar
+ && (etype->ty == Tchar || etype->ty == Twchar))
+ {
+ /* Append a dchar to a char[] or wchar[] */
+ libcall_fn libcall = (etype->ty == Tchar)
+ ? LIBCALL_ARRAYAPPENDCD : LIBCALL_ARRAYAPPENDWD;
+
+ this->result_ = build_libcall (libcall, e->type, 2,
+ build_address (build_expr (e->e1)),
+ build_expr (e->e2));
+ }
+ else
+ {
+ gcc_assert (tb1->ty == Tarray || tb2->ty == Tsarray);
+
+ tree tinfo = build_typeinfo (e->type);
+ tree ptr = build_address (build_expr (e->e1));
+
+ if ((tb2->ty == Tarray || tb2->ty == Tsarray)
+ && same_type_p (etype, tb2->nextOf ()->toBasetype ()))
+ {
+ /* Append an array. */
+ this->result_ = build_libcall (LIBCALL_ARRAYAPPENDT, e->type, 3,
+ tinfo, ptr, d_array_convert (e->e2));
+
+ }
+ else if (same_type_p (etype, tb2))
+ {
+ /* Append an element. */
+ tree result = build_libcall (LIBCALL_ARRAYAPPENDCTX, e->type, 3,
+ tinfo, ptr, size_one_node);
+ result = d_save_expr (result);
+
+ /* Assign e2 to last element. */
+ tree offexp = d_array_length (result);
+ offexp = build2 (MINUS_EXPR, TREE_TYPE (offexp),
+ offexp, size_one_node);
+ offexp = d_save_expr (offexp);
+
+ tree ptrexp = d_array_ptr (result);
+ ptrexp = void_okay_p (ptrexp);
+ ptrexp = build_array_index (ptrexp, offexp);
+
+ /* Evaluate expression before appending. */
+ tree t2 = build_expr (e->e2);
+ tree expr = stabilize_expr (&t2);
+
+ t2 = d_save_expr (t2);
+ result = modify_expr (build_deref (ptrexp), t2);
+ result = compound_expr (t2, result);
+
+ this->result_ = compound_expr (expr, result);
+ }
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ /* Build an assignment expression. The right operand is implicitly
+ converted to the type of the left operand, and assigned to it. */
+
+ void visit (AssignExp *e)
+ {
+ /* First, handle special assignment semantics. */
+
+ /* Look for array.length = n; */
+ if (e->e1->op == TOKarraylength)
+ {
+ /* Assignment to an array's length property; resize the array. */
+ ArrayLengthExp *ale = (ArrayLengthExp *) e->e1;
+ tree newlength = convert_expr (build_expr (e->e2), e->e2->type,
+ Type::tsize_t);
+ tree ptr = build_address (build_expr (ale->e1));
+
+ /* Don't want the basetype for the element type. */
+ Type *etype = ale->e1->type->toBasetype ()->nextOf ();
+ libcall_fn libcall = etype->isZeroInit ()
+ ? LIBCALL_ARRAYSETLENGTHT : LIBCALL_ARRAYSETLENGTHIT;
+
+ tree result = build_libcall (libcall, ale->e1->type, 3,
+ build_typeinfo (ale->e1->type),
+ newlength, ptr);
+
+ this->result_ = d_array_length (result);
+ return;
+ }
+
+ /* Look for array[] = n; */
+ if (e->e1->op == TOKslice)
+ {
+ SliceExp *se = (SliceExp *) e->e1;
+ Type *stype = se->e1->type->toBasetype ();
+ Type *etype = stype->nextOf ()->toBasetype ();
+
+ /* Determine if we need to run postblit or dtor. */
+ bool postblit = this->needs_postblit (etype) && this->lvalue_p (e->e2);
+ bool destructor = this->needs_dtor (etype);
+
+ if (e->memset & blockAssign)
+ {
+ /* Set a range of elements to one value. */
+ tree t1 = d_save_expr (build_expr (e->e1));
+ tree t2 = build_expr (e->e2);
+ tree result;
+
+ if ((postblit || destructor) && e->op != TOKblit)
+ {
+ libcall_fn libcall = (e->op == TOKconstruct)
+ ? LIBCALL_ARRAYSETCTOR : LIBCALL_ARRAYSETASSIGN;
+ /* So we can call postblits on const/immutable objects. */
+ tree ti = build_typeinfo (etype->unSharedOf ()->mutableOf ());
+
+ tree result = build_libcall (libcall, Type::tvoid, 4,
+ d_array_ptr (t1),
+ build_address (t2),
+ d_array_length (t1), ti);
+ this->result_ = compound_expr (result, t1);
+ return;
+ }
+
+ if (integer_zerop (t2))
+ {
+ tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET);
+ tree size = size_mult_expr (d_array_length (t1),
+ size_int (etype->size ()));
+
+ result = build_call_expr (tmemset, 3, d_array_ptr (t1),
+ integer_zero_node, size);
+ }
+ else
+ result = build_array_set (d_array_ptr (t1),
+ d_array_length (t1), t2);
+
+ this->result_ = compound_expr (result, t1);
+ }
+ else
+ {
+ /* Perform a memcpy operation. */
+ gcc_assert (e->e2->type->ty != Tpointer);
+
+ if (!postblit && !destructor && !array_bounds_check ())
+ {
+ tree t1 = d_save_expr (d_array_convert (e->e1));
+ tree t2 = d_array_convert (e->e2);
+ tree tmemcpy = builtin_decl_explicit (BUILT_IN_MEMCPY);
+ tree size = size_mult_expr (d_array_length (t1),
+ size_int (etype->size ()));
+
+ tree result = build_call_expr (tmemcpy, 3, d_array_ptr (t1),
+ d_array_ptr (t2), size);
+ this->result_ = compound_expr (result, t1);
+ }
+ else if ((postblit || destructor) && e->op != TOKblit)
+ {
+ /* Generate: _d_arrayassign(ti, from, to)
+ or: _d_arrayctor(ti, from, to) */
+ libcall_fn libcall = (e->op == TOKconstruct)
+ ? LIBCALL_ARRAYCTOR : LIBCALL_ARRAYASSIGN;
+
+ this->result_ = build_libcall (libcall, e->type, 3,
+ build_typeinfo (etype),
+ d_array_convert (e->e2),
+ d_array_convert (e->e1));
+ }
+ else
+ {
+ /* Generate: _d_arraycopy() */
+ this->result_ = build_libcall (LIBCALL_ARRAYCOPY, e->type, 3,
+ size_int (etype->size ()),
+ d_array_convert (e->e2),
+ d_array_convert (e->e1));
+ }
+ }
+
+ return;
+ }
+
+ /* Look for reference initializations. */
+ if (e->memset & referenceInit)
+ {
+ gcc_assert (e->op == TOKconstruct || e->op == TOKblit);
+ gcc_assert (e->e1->op == TOKvar);
+
+ Declaration *decl = ((VarExp *) e->e1)->var;
+ if (decl->storage_class & (STCout | STCref))
+ {
+ tree t2 = convert_for_assignment (build_expr (e->e2),
+ e->e2->type, e->e1->type);
+ tree t1 = build_expr (e->e1);
+ /* Want reference to lhs, not indirect ref. */
+ t1 = TREE_OPERAND (t1, 0);
+ t2 = build_address (t2);
+
+ this->result_ = indirect_ref (build_ctype (e->type),
+ build_assign (INIT_EXPR, t1, t2));
+ return;
+ }
+ }
+
+ /* Other types of assignments that may require post construction. */
+ Type *tb1 = e->e1->type->toBasetype ();
+ tree_code modifycode = (e->op == TOKconstruct) ? INIT_EXPR : MODIFY_EXPR;
+
+ /* Look for struct assignment. */
+ if (tb1->ty == Tstruct)
+ {
+ tree t1 = build_expr (e->e1);
+ tree t2 = convert_for_assignment (build_expr (e->e2),
+ e->e2->type, e->e1->type);
+
+ /* Look for struct = 0. */
+ if (e->e2->op == TOKint64)
+ {
+ /* Use memset to fill struct. */
+ gcc_assert (e->op == TOKblit);
+ StructDeclaration *sd = ((TypeStruct *) tb1)->sym;
+
+ tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET);
+ tree result = build_call_expr (tmemset, 3, build_address (t1),
+ t2, size_int (sd->structsize));
+
+ /* Maybe set-up hidden pointer to outer scope context. */
+ if (sd->isNested ())
+ {
+ tree field = get_symbol_decl (sd->vthis);
+ tree value = build_vthis (sd);
+
+ tree vthis_exp = modify_expr (component_ref (t1, field), value);
+ result = compound_expr (result, vthis_exp);
+ }
+
+ this->result_ = compound_expr (result, t1);
+ }
+ else
+ this->result_ = build_assign (modifycode, t1, t2);
+
+ return;
+ }
+
+ /* Look for static array assignment. */
+ if (tb1->ty == Tsarray)
+ {
+ /* Look for array = 0. */
+ if (e->e2->op == TOKint64)
+ {
+ /* Use memset to fill the array. */
+ gcc_assert (e->op == TOKblit);
+
+ tree t1 = build_expr (e->e1);
+ tree t2 = convert_for_assignment (build_expr (e->e2),
+ e->e2->type, e->e1->type);
+ tree size = size_int (e->e1->type->size ());
+
+ tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET);
+ this->result_ = build_call_expr (tmemset, 3, build_address (t1),
+ t2, size);
+ return;
+ }
+
+ Type *etype = tb1->nextOf ();
+ gcc_assert (e->e2->type->toBasetype ()->ty == Tsarray);
+
+ /* Determine if we need to run postblit. */
+ bool postblit = this->needs_postblit (etype);
+ bool destructor = this->needs_dtor (etype);
+ bool lvalue_p = this->lvalue_p (e->e2);
+
+ /* Even if the elements in rhs are all rvalues and don't have
+ to call postblits, this assignment should call dtors on old
+ assigned elements. */
+ if ((!postblit && !destructor)
+ || (e->op == TOKconstruct && !lvalue_p && postblit)
+ || (e->op == TOKblit || e->e1->type->size () == 0))
+ {
+ tree t1 = build_expr (e->e1);
+ tree t2 = convert_for_assignment (build_expr (e->e2),
+ e->e2->type, e->e1->type);
+
+ this->result_ = build_assign (modifycode, t1, t2);
+ return;
+ }
+
+ Type *arrtype = (e->type->ty == Tsarray) ? etype->arrayOf () : e->type;
+ tree result;
+
+ if (e->op == TOKconstruct)
+ {
+ /* Generate: _d_arrayctor(ti, from, to) */
+ result = build_libcall (LIBCALL_ARRAYCTOR, arrtype, 3,
+ build_typeinfo (etype),
+ d_array_convert (e->e2),
+ d_array_convert (e->e1));
+ }
+ else
+ {
+ /* Generate: _d_arrayassign_l()
+ or: _d_arrayassign_r() */
+ libcall_fn libcall = (lvalue_p)
+ ? LIBCALL_ARRAYASSIGN_L : LIBCALL_ARRAYASSIGN_R;
+ tree elembuf = build_local_temp (build_ctype (etype));
+
+ result = build_libcall (libcall, arrtype, 4,
+ build_typeinfo (etype),
+ d_array_convert (e->e2),
+ d_array_convert (e->e1),
+ build_address (elembuf));
+ }
+
+ /* Cast the libcall result back to a static array. */
+ if (e->type->ty == Tsarray)
+ result = indirect_ref (build_ctype (e->type),
+ d_array_ptr (result));
+
+ this->result_ = result;
+ return;
+ }
+
+ /* Simple assignment. */
+ tree t1 = build_expr (e->e1);
+ tree t2 = convert_for_assignment (build_expr (e->e2),
+ e->e2->type, e->e1->type);
+
+ this->result_ = build_assign (modifycode, t1, t2);
+ }
+
+ /* Build a postfix expression. */
+
+ void visit (PostExp *e)
+ {
+ tree result;
+
+ if (e->op == TOKplusplus)
+ {
+ result = build2 (POSTINCREMENT_EXPR, build_ctype (e->type),
+ build_expr (e->e1), build_expr (e->e2));
+ }
+ else if (e->op == TOKminusminus)
+ {
+ result = build2 (POSTDECREMENT_EXPR, build_ctype (e->type),
+ build_expr (e->e1), build_expr (e->e2));
+ }
+ else
+ gcc_unreachable ();
+
+ TREE_SIDE_EFFECTS (result) = 1;
+ this->result_ = result;
+ }
+
+ /* Build an index expression. */
+
+ void visit (IndexExp *e)
+ {
+ Type *tb1 = e->e1->type->toBasetype ();
+
+ if (tb1->ty == Taarray)
+ {
+ /* Get the key for the associative array. */
+ Type *tkey = ((TypeAArray *) tb1)->index->toBasetype ();
+ tree key = convert_expr (build_expr (e->e2), e->e2->type, tkey);
+ libcall_fn libcall;
+ tree tinfo, ptr;
+
+ if (e->modifiable)
+ {
+ libcall = LIBCALL_AAGETY;
+ ptr = build_address (build_expr (e->e1));
+ tinfo = build_typeinfo (tb1->unSharedOf ()->mutableOf ());
+ }
+ else
+ {
+ libcall = LIBCALL_AAGETRVALUEX;
+ ptr = build_expr (e->e1);
+ tinfo = build_typeinfo (tkey);
+ }
+
+ /* Index the associative array. */
+ tree result = build_libcall (libcall, e->type->pointerTo (), 4,
+ ptr, tinfo,
+ size_int (tb1->nextOf ()->size ()),
+ build_address (key));
+
+ if (!e->indexIsInBounds && array_bounds_check ())
+ {
+ tree tassert = d_assert_call (e->loc, LIBCALL_ARRAY_BOUNDS);
+ result = d_save_expr (result);
+ result = build_condition (TREE_TYPE (result),
+ d_truthvalue_conversion (result),
+ result, tassert);
+ }
+
+ this->result_ = indirect_ref (build_ctype (e->type), result);
+ }
+ else
+ {
+ /* Get the data pointer and length for static and dynamic arrays. */
+ tree array = d_save_expr (build_expr (e->e1));
+ tree ptr = convert_expr (array, tb1, tb1->nextOf ()->pointerTo ());
+
+ tree length = NULL_TREE;
+ if (tb1->ty != Tpointer)
+ length = get_array_length (array, tb1);
+ else
+ gcc_assert (e->lengthVar == NULL);
+
+ /* The __dollar variable just becomes a placeholder for the
+ actual length. */
+ if (e->lengthVar)
+ e->lengthVar->csym = length;
+
+ /* Generate the index. */
+ tree index = build_expr (e->e2);
+
+ /* If it's a static array and the index is constant, the front end has
+ already checked the bounds. */
+ if (tb1->ty != Tpointer && !e->indexIsInBounds)
+ index = build_bounds_condition (e->e2->loc, index, length, false);
+
+ /* Index the .ptr. */
+ ptr = void_okay_p (ptr);
+ this->result_ = indirect_ref (TREE_TYPE (TREE_TYPE (ptr)),
+ build_array_index (ptr, index));
+ }
+ }
+
+ /* Build a comma expression. The type is the type of the right operand. */
+
+ void visit (CommaExp *e)
+ {
+ tree t1 = build_expr (e->e1);
+ tree t2 = build_expr (e->e2);
+ tree type = e->type ? build_ctype (e->type) : void_type_node;
+
+ this->result_ = build2 (COMPOUND_EXPR, type, t1, t2);
+ }
+
+ /* Build an array length expression. Returns the number of elements
+ in the array. The result is of type size_t. */
+
+ void visit (ArrayLengthExp *e)
+ {
+ if (e->e1->type->toBasetype ()->ty == Tarray)
+ this->result_ = d_array_length (build_expr (e->e1));
+ else
+ {
+ /* Static arrays have already been handled by the front-end. */
+ error ("unexpected type for array length: %qs", e->type->toChars ());
+ this->result_ = error_mark_node;
+ }
+ }
+
+ /* Build a delegate pointer expression. This will return the frame
+ pointer value as a type void*. */
+
+ void visit (DelegatePtrExp *e)
+ {
+ tree t1 = build_expr (e->e1);
+ this->result_ = delegate_object (t1);
+ }
+
+ /* Build a delegate function pointer expression. This will return the
+ function pointer value as a function type. */
+
+ void visit (DelegateFuncptrExp *e)
+ {
+ tree t1 = build_expr (e->e1);
+ this->result_ = delegate_method (t1);
+ }
+
+ /* Build a slice expression. */
+
+ void visit (SliceExp *e)
+ {
+ Type *tb = e->type->toBasetype ();
+ Type *tb1 = e->e1->type->toBasetype ();
+ gcc_assert (tb->ty == Tarray || tb->ty == Tsarray);
+
+ /* Use convert-to-dynamic-array code if possible. */
+ if (!e->lwr)
+ {
+ tree result = build_expr (e->e1);
+ if (e->e1->type->toBasetype ()->ty == Tsarray)
+ result = convert_expr (result, e->e1->type, e->type);
+
+ this->result_ = result;
+ return;
+ }
+ else
+ gcc_assert (e->upr != NULL);
+
+ /* Get the data pointer and length for static and dynamic arrays. */
+ tree array = d_save_expr (build_expr (e->e1));
+ tree ptr = convert_expr (array, tb1, tb1->nextOf ()->pointerTo ());
+ tree length = NULL_TREE;
+
+ /* Our array is already a SAVE_EXPR if necessary, so we don't make length
+ a SAVE_EXPR which is, at most, a COMPONENT_REF on top of array. */
+ if (tb1->ty != Tpointer)
+ length = get_array_length (array, tb1);
+ else
+ gcc_assert (e->lengthVar == NULL);
+
+ /* The __dollar variable just becomes a placeholder for the
+ actual length. */
+ if (e->lengthVar)
+ e->lengthVar->csym = length;
+
+ /* Generate upper and lower bounds. */
+ tree lwr_tree = d_save_expr (build_expr (e->lwr));
+ tree upr_tree = d_save_expr (build_expr (e->upr));
+
+ /* If the upper bound has any side effects, then the lower bound should be
+ copied to a temporary always. */
+ if (TREE_CODE (upr_tree) == SAVE_EXPR && TREE_CODE (lwr_tree) != SAVE_EXPR)
+ lwr_tree = save_expr (lwr_tree);
+
+ /* Adjust the .ptr offset. */
+ if (!integer_zerop (lwr_tree))
+ {
+ tree ptrtype = TREE_TYPE (ptr);
+ ptr = build_array_index (void_okay_p (ptr), lwr_tree);
+ ptr = build_nop (ptrtype, ptr);
+ }
+ else
+ lwr_tree = NULL_TREE;
+
+ /* Nothing more to do for static arrays, their bounds checking has been
+ done at compile-time. */
+ if (tb->ty == Tsarray)
+ {
+ this->result_ = indirect_ref (build_ctype (e->type), ptr);
+ return;
+ }
+ else
+ gcc_assert (tb->ty == Tarray);
+
+ /* Generate bounds checking code. */
+ tree newlength;
+
+ if (!e->upperIsInBounds)
+ {
+ if (length)
+ {
+ newlength = build_bounds_condition (e->upr->loc, upr_tree,
+ length, true);
+ }
+ else
+ {
+ /* Still need to check bounds lwr <= upr for pointers. */
+ gcc_assert (tb1->ty == Tpointer);
+ newlength = upr_tree;
+ }
+ }
+ else
+ newlength = upr_tree;
+
+ if (lwr_tree)
+ {
+ /* Enforces lwr <= upr. No need to check lwr <= length as
+ we've already ensured that upr <= length. */
+ if (!e->lowerIsLessThanUpper)
+ {
+ tree cond = build_bounds_condition (e->lwr->loc, lwr_tree,
+ upr_tree, true);
+
+ /* When bounds checking is off, the index value is
+ returned directly. */
+ if (cond != lwr_tree)
+ newlength = compound_expr (cond, newlength);
+ }
+
+ /* Need to ensure lwr always gets evaluated first, as it may be a
+ function call. Generates (lwr, upr) - lwr. */
+ newlength = fold_build2 (MINUS_EXPR, TREE_TYPE (newlength),
+ compound_expr (lwr_tree, newlength), lwr_tree);
+ }
+
+ tree result = d_array_value (build_ctype (e->type), newlength, ptr);
+ this->result_ = compound_expr (array, result);
+ }
+
+ /* Build a cast expression, which converts the given unary expression to the
+ type of result. */
+
+ void visit (CastExp *e)
+ {
+ Type *ebtype = e->e1->type->toBasetype ();
+ Type *tbtype = e->to->toBasetype ();
+ tree result = build_expr (e->e1, this->constp_);
+
+ /* Just evaluate e1 if it has any side effects. */
+ if (tbtype->ty == Tvoid)
+ this->result_ = build_nop (build_ctype (tbtype), result);
+ else
+ this->result_ = convert_expr (result, ebtype, tbtype);
+ }
+
+ /* Build a delete expression. */
+
+ void visit (DeleteExp *e)
+ {
+ tree t1 = build_expr (e->e1);
+ Type *tb1 = e->e1->type->toBasetype ();
+
+ if (tb1->ty == Tclass)
+ {
+ /* For class object references, if there is a destructor for that class,
+ the destructor is called for the object instance. */
+ libcall_fn libcall;
+
+ if (e->e1->op == TOKvar)
+ {
+ VarDeclaration *v = ((VarExp *) e->e1)->var->isVarDeclaration ();
+ if (v && v->onstack)
+ {
+ libcall = tb1->isClassHandle ()->isInterfaceDeclaration ()
+ ? LIBCALL_CALLINTERFACEFINALIZER : LIBCALL_CALLFINALIZER;
+
+ this->result_ = build_libcall (libcall, Type::tvoid, 1, t1);
+ return;
+ }
+ }
+
+ /* Otherwise, the garbage collector is called to immediately free the
+ memory allocated for the class instance. */
+ libcall = tb1->isClassHandle ()->isInterfaceDeclaration ()
+ ? LIBCALL_DELINTERFACE : LIBCALL_DELCLASS;
+
+ t1 = build_address (t1);
+ this->result_ = build_libcall (libcall, Type::tvoid, 1, t1);
+ }
+ else if (tb1->ty == Tarray)
+ {
+ /* For dynamic arrays, the garbage collector is called to immediately
+ release the memory. */
+ Type *telem = tb1->nextOf ()->baseElemOf ();
+ tree ti = null_pointer_node;
+
+ if (telem->ty == Tstruct)
+ {
+ /* Might need to run destructor on array contents. */
+ TypeStruct *ts = (TypeStruct *) telem;
+ if (ts->sym->dtor)
+ ti = build_typeinfo (tb1->nextOf ());
+ }
+
+ /* Generate: _delarray_t (&t1, ti); */
+ this->result_ = build_libcall (LIBCALL_DELARRAYT, Type::tvoid, 2,
+ build_address (t1), ti);
+ }
+ else if (tb1->ty == Tpointer)
+ {
+ /* For pointers to a struct instance, if the struct has overloaded
+ operator delete, then that operator is called. */
+ t1 = build_address (t1);
+ Type *tnext = ((TypePointer *)tb1)->next->toBasetype ();
+
+ if (tnext->ty == Tstruct)
+ {
+ TypeStruct *ts = (TypeStruct *)tnext;
+ if (ts->sym->dtor)
+ {
+ this->result_ = build_libcall (LIBCALL_DELSTRUCT, Type::tvoid,
+ 2, t1, build_typeinfo (tnext));
+ return;
+ }
+ }
+
+ /* Otherwise, the garbage collector is called to immediately free the
+ memory allocated for the pointer. */
+ this->result_ = build_libcall (LIBCALL_DELMEMORY, Type::tvoid, 1, t1);
+ }
+ else
+ {
+ error ("don't know how to delete %qs", e->e1->toChars ());
+ this->result_ = error_mark_node;
+ }
+ }
+
+ /* Build a remove expression, which removes a particular key from an
+ associative array. */
+
+ void visit (RemoveExp *e)
+ {
+ /* Check that the array is actually an associative array. */
+ if (e->e1->type->toBasetype ()->ty == Taarray)
+ {
+ Type *tb = e->e1->type->toBasetype ();
+ Type *tkey = ((TypeAArray *) tb)->index->toBasetype ();
+ tree index = convert_expr (build_expr (e->e2), e->e2->type, tkey);
+
+ this->result_ = build_libcall (LIBCALL_AADELX, Type::tbool, 3,
+ build_expr (e->e1),
+ build_typeinfo (tkey),
+ build_address (index));
+ }
+ else
+ {
+ error ("%qs is not an associative array", e->e1->toChars ());
+ this->result_ = error_mark_node;
+ }
+ }
+
+ /* Build an unary not expression. */
+
+ void visit (NotExp *e)
+ {
+ tree result = convert_for_condition (build_expr (e->e1), e->e1->type);
+ /* Need to convert to boolean type or this will fail. */
+ result = fold_build1 (TRUTH_NOT_EXPR, d_bool_type, result);
+
+ this->result_ = d_convert (build_ctype (e->type), result);
+ }
+
+ /* Build a compliment expression, where all the bits in the value are
+ complemented. Note: unlike in C, the usual integral promotions
+ are not performed prior to the complement operation. */
+
+ void visit (ComExp *e)
+ {
+ TY ty1 = e->e1->type->toBasetype ()->ty;
+ gcc_assert (ty1 != Tarray && ty1 != Tsarray);
+
+ this->result_ = fold_build1 (BIT_NOT_EXPR, build_ctype (e->type),
+ build_expr (e->e1));
+ }
+
+ /* Build an unary negation expression. */
+
+ void visit (NegExp *e)
+ {
+ TY ty1 = e->e1->type->toBasetype ()->ty;
+ gcc_assert (ty1 != Tarray && ty1 != Tsarray);
+
+ tree type = build_ctype (e->type);
+ tree expr = build_expr (e->e1);
+
+ /* If the operation needs excess precision. */
+ tree eptype = excess_precision_type (type);
+ if (eptype != NULL_TREE)
+ expr = d_convert (eptype, expr);
+ else
+ eptype = type;
+
+ tree ret = fold_build1 (NEGATE_EXPR, eptype, expr);
+ this->result_ = d_convert (type, ret);
+ }
+
+ /* Build a pointer index expression. */
+
+ void visit (PtrExp *e)
+ {
+ Type *tnext = NULL;
+ size_t offset;
+ tree result;
+
+ if (e->e1->op == TOKadd)
+ {
+ BinExp *be = (BinExp *) e->e1;
+ if (be->e1->op == TOKaddress
+ && be->e2->isConst () && be->e2->type->isintegral ())
+ {
+ Expression *ae = ((AddrExp *) be->e1)->e1;
+ tnext = ae->type->toBasetype ();
+ result = build_expr (ae);
+ offset = be->e2->toUInteger ();
+ }
+ }
+ else if (e->e1->op == TOKsymoff)
+ {
+ SymOffExp *se = (SymOffExp *) e->e1;
+ if (!declaration_reference_p (se->var))
+ {
+ tnext = se->var->type->toBasetype ();
+ result = get_decl_tree (se->var);
+ offset = se->offset;
+ }
+ }
+
+ /* Produce better code by converting *(#record + n) to
+ COMPONENT_REFERENCE. Otherwise, the variable will always be
+ allocated in memory because its address is taken. */
+ if (tnext && tnext->ty == Tstruct)
+ {
+ StructDeclaration *sd = ((TypeStruct *) tnext)->sym;
+
+ for (size_t i = 0; i < sd->fields.dim; i++)
+ {
+ VarDeclaration *field = sd->fields[i];
+
+ if (field->offset == offset
+ && same_type_p (field->type, e->type))
+ {
+ /* Catch errors, backend will ICE otherwise. */
+ if (error_operand_p (result))
+ this->result_ = result;
+ else
+ {
+ result = component_ref (result, get_symbol_decl (field));
+ this->result_ = result;
+ }
+ return;
+ }
+ else if (field->offset > offset)
+ break;
+ }
+ }
+
+ this->result_ = indirect_ref (build_ctype (e->type), build_expr (e->e1));
+ }
+
+ /* Build an unary address expression. */
+
+ void visit (AddrExp *e)
+ {
+ tree type = build_ctype (e->type);
+ tree exp;
+
+ /* The frontend optimizer can convert const symbol into a struct literal.
+ Taking the address of a struct literal is otherwise illegal. */
+ if (e->e1->op == TOKstructliteral)
+ {
+ StructLiteralExp *sle = ((StructLiteralExp *) e->e1)->origin;
+ gcc_assert (sle != NULL);
+
+ /* Build the reference symbol, the decl is built first as the
+ initializer may have recursive references. */
+ if (!sle->sym)
+ {
+ sle->sym = build_artificial_decl (build_ctype (sle->type),
+ NULL_TREE, "S");
+ DECL_INITIAL (sle->sym) = build_expr (sle, true);
+ d_pushdecl (sle->sym);
+ rest_of_decl_compilation (sle->sym, 1, 0);
+ }
+
+ exp = sle->sym;
+ }
+ else
+ exp = build_expr (e->e1, this->constp_);
+
+ TREE_CONSTANT (exp) = 0;
+ this->result_ = d_convert (type, build_address (exp));
+ }
+
+ /* Build a function call expression. */
+
+ void visit (CallExp *e)
+ {
+ Type *tb = e->e1->type->toBasetype ();
+ Expression *e1b = e->e1;
+
+ tree callee = NULL_TREE;
+ tree object = NULL_TREE;
+ tree cleanup = NULL_TREE;
+ TypeFunction *tf = NULL;
+
+ /* Calls to delegates can sometimes look like this. */
+ if (e1b->op == TOKcomma)
+ {
+ e1b = ((CommaExp *) e1b)->e2;
+ gcc_assert (e1b->op == TOKvar);
+
+ Declaration *var = ((VarExp *) e1b)->var;
+ gcc_assert (var->isFuncDeclaration () && !var->needThis ());
+ }
+
+ if (e1b->op == TOKdotvar && tb->ty != Tdelegate)
+ {
+ DotVarExp *dve = (DotVarExp *) e1b;
+
+ /* Don't modify the static initializer for struct literals. */
+ if (dve->e1->op == TOKstructliteral)
+ {
+ StructLiteralExp *sle = (StructLiteralExp *) dve->e1;
+ sle->useStaticInit = false;
+ }
+
+ FuncDeclaration *fd = dve->var->isFuncDeclaration ();
+ if (fd != NULL)
+ {
+ /* Get the correct callee from the DotVarExp object. */
+ tree fndecl = get_symbol_decl (fd);
+ AggregateDeclaration *ad = fd->isThis ();
+
+ /* Static method; ignore the object instance. */
+ if (!ad)
+ callee = build_address (fndecl);
+ else
+ {
+ tree thisexp = build_expr (dve->e1);
+
+ /* When constructing temporaries, if the constructor throws,
+ then the object is destructed even though it is not a fully
+ constructed object yet. And so this call will need to be
+ moved inside the TARGET_EXPR_INITIAL slot. */
+ if (fd->isCtorDeclaration ()
+ && TREE_CODE (thisexp) == COMPOUND_EXPR
+ && TREE_CODE (TREE_OPERAND (thisexp, 0)) == TARGET_EXPR
+ && TARGET_EXPR_CLEANUP (TREE_OPERAND (thisexp, 0)))
+ {
+ cleanup = TREE_OPERAND (thisexp, 0);
+ thisexp = TREE_OPERAND (thisexp, 1);
+ }
+
+ /* Want reference to 'this' object. */
+ if (!POINTER_TYPE_P (TREE_TYPE (thisexp)))
+ thisexp = build_address (thisexp);
+
+ /* Make the callee a virtual call. */
+ if (fd->isVirtual () && !fd->isFinalFunc () && !e->directcall)
+ {
+ tree fntype = build_pointer_type (TREE_TYPE (fndecl));
+ tree thistype = build_ctype (ad->handleType ());
+ thisexp = build_nop (thistype, d_save_expr (thisexp));
+ fndecl = build_vindex_ref (thisexp, fntype, fd->vtblIndex);
+ }
+ else
+ fndecl = build_address (fndecl);
+
+ callee = build_method_call (fndecl, thisexp, fd->type);
+ }
+ }
+ }
+
+ if (callee == NULL_TREE)
+ callee = build_expr (e1b);
+
+ if (METHOD_CALL_EXPR (callee))
+ {
+ /* This could be a delegate expression (TY == Tdelegate), but not
+ actually a delegate variable. */
+ if (e1b->op == TOKdotvar)
+ {
+ /* This gets the true function type, getting the function type
+ from e1->type can sometimes be incorrect, such as when calling
+ a 'ref' return function. */
+ tf = get_function_type (((DotVarExp *) e1b)->var->type);
+ }
+ else
+ tf = get_function_type (tb);
+
+ extract_from_method_call (callee, callee, object);
+ }
+ else if (tb->ty == Tdelegate)
+ {
+ /* Delegate call, extract .object and .funcptr from var. */
+ callee = d_save_expr (callee);
+ tf = get_function_type (tb);
+ object = delegate_object (callee);
+ callee = delegate_method (callee);
+ }
+ else if (e1b->op == TOKvar)
+ {
+ FuncDeclaration *fd = ((VarExp *) e1b)->var->isFuncDeclaration ();
+ gcc_assert (fd != NULL);
+ tf = get_function_type (fd->type);
+
+ if (fd->isNested ())
+ {
+ /* Maybe re-evaluate symbol storage treating 'fd' as public. */
+ if (call_by_alias_p (d_function_chain->function, fd))
+ TREE_PUBLIC (callee) = 1;
+
+ object = get_frame_for_symbol (fd);
+ }
+ else if (fd->needThis ())
+ {
+ error_at (make_location_t (e1b->loc),
+ "need %<this%> to access member %qs", fd->toChars ());
+ /* Continue compiling... */
+ object = null_pointer_node;
+ }
+ }
+ else
+ {
+ /* Normal direct function call. */
+ tf = get_function_type (tb);
+ }
+
+ gcc_assert (tf != NULL);
+
+ /* Now we have the type, callee and maybe object reference,
+ build the call expression. */
+ tree exp = d_build_call (tf, callee, object, e->arguments);
+
+ if (tf->isref)
+ exp = build_deref (exp);
+
+ /* Some library calls are defined to return a generic type.
+ this->type is the real type we want to return. */
+ if (e->type->isTypeBasic ())
+ exp = d_convert (build_ctype (e->type), exp);
+
+ /* If this call was found to be a constructor for a temporary with a
+ cleanup, then move the call inside the TARGET_EXPR. The original
+ initializer is turned into an assignment, to keep its side effect. */
+ if (cleanup != NULL_TREE)
+ {
+ tree init = TARGET_EXPR_INITIAL (cleanup);
+ tree slot = TARGET_EXPR_SLOT (cleanup);
+ d_mark_addressable (slot);
+ init = build_assign (INIT_EXPR, slot, init);
+
+ TARGET_EXPR_INITIAL (cleanup) = compound_expr (init, exp);
+ exp = cleanup;
+ }
+
+ this->result_ = exp;
+ }
+
+ /* Build a delegate expression. */
+
+ void visit (DelegateExp *e)
+ {
+ if (e->func->semanticRun == PASSsemantic3done)
+ {
+ /* Add the function as nested function if it belongs to this module.
+ ie: it is a member of this module, or it is a template instance. */
+ Dsymbol *owner = e->func->toParent ();
+ while (!owner->isTemplateInstance () && owner->toParent ())
+ owner = owner->toParent ();
+ if (owner->isTemplateInstance () || owner == d_function_chain->module)
+ build_decl_tree (e->func);
+ }
+
+ tree fndecl;
+ tree object;
+
+ if (e->func->isNested ())
+ {
+ if (e->e1->op == TOKnull)
+ object = build_expr (e->e1);
+ else
+ object = get_frame_for_symbol (e->func);
+
+ fndecl = build_address (get_symbol_decl (e->func));
+ }
+ else
+ {
+ if (!e->func->isThis ())
+ {
+ error ("delegates are only for non-static functions");
+ this->result_ = error_mark_node;
+ return;
+ }
+
+ object = build_expr (e->e1);
+
+ /* Want reference to `this' object. */
+ if (e->e1->type->ty != Tclass && e->e1->type->ty != Tpointer)
+ object = build_address (object);
+
+ /* Object reference could be the outer `this' field of a class or
+ closure of type `void*'. Cast it to the right type. */
+ if (e->e1->type->ty == Tclass)
+ object = d_convert (build_ctype (e->e1->type), object);
+
+ fndecl = get_symbol_decl (e->func);
+
+ /* Get pointer to function out of the virtual table. */
+ if (e->func->isVirtual () && !e->func->isFinalFunc ()
+ && e->e1->op != TOKsuper && e->e1->op != TOKdottype)
+ {
+ tree fntype = build_pointer_type (TREE_TYPE (fndecl));
+ object = d_save_expr (object);
+ fndecl = build_vindex_ref (object, fntype, e->func->vtblIndex);
+ }
+ else
+ fndecl = build_address (fndecl);
+ }
+
+ this->result_ = build_method_call (fndecl, object, e->type);
+ }
+
+ /* Build a type component expression. */
+
+ void visit (DotTypeExp *e)
+ {
+ /* Just a pass through to underlying expression. */
+ this->result_ = build_expr (e->e1);
+ }
+
+ /* Build a component reference expression. */
+
+ void visit (DotVarExp *e)
+ {
+ VarDeclaration *vd = e->var->isVarDeclaration ();
+
+ /* This could also be a function, but relying on that being taken
+ care of by the visitor interface for CallExp. */
+ if (vd != NULL)
+ {
+ if (!vd->isField ())
+ this->result_ = get_decl_tree (vd);
+ else
+ {
+ tree object = build_expr (e->e1);
+
+ if (e->e1->type->toBasetype ()->ty != Tstruct)
+ object = build_deref (object);
+
+ this->result_ = component_ref (object, get_symbol_decl (vd));
+ }
+ }
+ else
+ {
+ error ("%qs is not a field, but a %qs",
+ e->var->toChars (), e->var->kind ());
+ this->result_ = error_mark_node;
+ }
+ }
+
+ /* Build an assert expression, used to declare conditions that must hold at
+ that a given point in the program. */
+
+ void visit (AssertExp *e)
+ {
+ Type *tb1 = e->e1->type->toBasetype ();
+ tree arg = build_expr (e->e1);
+ tree tmsg = NULL_TREE;
+ tree assert_pass = void_node;
+ tree assert_fail;
+
+ if (global.params.useAssert)
+ {
+ /* Generate: ((bool) e1 ? (void)0 : _d_assert (...))
+ or: (e1 != null ? e1._invariant() : _d_assert (...)) */
+ bool unittest_p = d_function_chain->function->isUnitTestDeclaration ();
+ libcall_fn libcall;
+
+ if (e->msg)
+ {
+ tmsg = build_expr_dtor (e->msg);
+ libcall = unittest_p ? LIBCALL_UNITTEST_MSG : LIBCALL_ASSERT_MSG;
+ }
+ else
+ libcall = unittest_p ? LIBCALL_UNITTEST : LIBCALL_ASSERT;
+
+ /* Build a call to _d_assert(). */
+ assert_fail = d_assert_call (e->loc, libcall, tmsg);
+
+ if (global.params.useInvariants)
+ {
+ /* If the condition is a D class or struct object with an invariant,
+ call it if the condition result is true. */
+ if (tb1->ty == Tclass)
+ {
+ ClassDeclaration *cd = tb1->isClassHandle ();
+ if (!cd->isInterfaceDeclaration () && !cd->isCPPclass ())
+ {
+ arg = d_save_expr (arg);
+ assert_pass = build_libcall (LIBCALL_INVARIANT,
+ Type::tvoid, 1, arg);
+ }
+ }
+ else if (tb1->ty == Tpointer && tb1->nextOf ()->ty == Tstruct)
+ {
+ StructDeclaration *sd = ((TypeStruct *) tb1->nextOf ())->sym;
+ if (sd->inv != NULL)
+ {
+ Expressions args;
+ arg = d_save_expr (arg);
+ assert_pass = d_build_call_expr (sd->inv, arg, &args);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Assert contracts are turned off, if the contract condition has no
+ side effects can still use it as a predicate for the optimizer. */
+ if (TREE_SIDE_EFFECTS (arg))
+ {
+ this->result_ = void_node;
+ return;
+ }
+
+ assert_fail = build_predict_expr (PRED_NORETURN, NOT_TAKEN);
+ }
+
+ /* Build condition that we are asserting in this contract. */
+ tree condition = convert_for_condition (arg, e->e1->type);
+
+ /* We expect the condition to always be true, as what happens if an assert
+ contract is false is undefined behavior. */
+ tree fn = builtin_decl_explicit (BUILT_IN_EXPECT);
+ tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ tree pred_type = TREE_VALUE (arg_types);
+ tree expected_type = TREE_VALUE (TREE_CHAIN (arg_types));
+
+ condition = build_call_expr (fn, 2, d_convert (pred_type, condition),
+ build_int_cst (expected_type, 1));
+ condition = d_truthvalue_conversion (condition);
+
+ this->result_ = build_vcondition (condition, assert_pass, assert_fail);
+ }
+
+ /* Build a declaration expression. */
+
+ void visit (DeclarationExp *e)
+ {
+ /* Compile the declaration. */
+ push_stmt_list ();
+ build_decl_tree (e->declaration);
+ tree result = pop_stmt_list ();
+
+ /* Construction of an array for typesafe-variadic function arguments
+ can cause an empty STMT_LIST here. This can causes problems
+ during gimplification. */
+ if (TREE_CODE (result) == STATEMENT_LIST && !STATEMENT_LIST_HEAD (result))
+ result = build_empty_stmt (input_location);
+
+ this->result_ = result;
+ }
+
+ /* Build a typeid expression. Returns an instance of class TypeInfo
+ corresponding to. */
+
+ void visit (TypeidExp *e)
+ {
+ if (Type *tid = isType (e->obj))
+ {
+ tree ti = build_typeinfo (tid);
+
+ /* If the typeinfo is at an offset. */
+ if (tid->vtinfo->offset)
+ ti = build_offset (ti, size_int (tid->vtinfo->offset));
+
+ this->result_ = build_nop (build_ctype (e->type), ti);
+ }
+ else if (Expression *tid = isExpression (e->obj))
+ {
+ Type *type = tid->type->toBasetype ();
+ assert (type->ty == Tclass);
+
+ /* Generate **classptr to get the classinfo. */
+ tree ci = build_expr (tid);
+ ci = indirect_ref (ptr_type_node, ci);
+ ci = indirect_ref (ptr_type_node, ci);
+
+ /* Add extra indirection for interfaces. */
+ if (((TypeClass *) type)->sym->isInterfaceDeclaration ())
+ ci = indirect_ref (ptr_type_node, ci);
+
+ this->result_ = build_nop (build_ctype (e->type), ci);
+ }
+ else
+ gcc_unreachable ();
+ }
+
+ /* Build a function/lambda expression. */
+
+ void visit (FuncExp *e)
+ {
+ Type *ftype = e->type->toBasetype ();
+
+ /* This check is for lambda's, remove 'vthis' as function isn't nested. */
+ if (e->fd->tok == TOKreserved && ftype->ty == Tpointer)
+ {
+ e->fd->tok = TOKfunction;
+ e->fd->vthis = NULL;
+ }
+
+ /* Compile the function literal body. */
+ build_decl_tree (e->fd);
+
+ /* If nested, this will be a trampoline. */
+ if (e->fd->isNested ())
+ {
+ tree func = build_address (get_symbol_decl (e->fd));
+ tree object;
+
+ if (this->constp_)
+ {
+ /* Static delegate variables have no context pointer. */
+ object = null_pointer_node;
+ this->result_ = build_method_call (func, object, e->fd->type);
+ TREE_CONSTANT (this->result_) = 1;
+ }
+ else
+ {
+ object = get_frame_for_symbol (e->fd);
+ this->result_ = build_method_call (func, object, e->fd->type);
+ }
+ }
+ else
+ {
+ this->result_ = build_nop (build_ctype (e->type),
+ build_address (get_symbol_decl (e->fd)));
+ }
+ }
+
+ /* Build a halt expression. */
+
+ void visit (HaltExp *)
+ {
+ /* Should we use trap() or abort()? */
+ tree ttrap = builtin_decl_explicit (BUILT_IN_TRAP);
+ this->result_ = build_call_expr (ttrap, 0);
+ }
+
+ /* Build a symbol pointer offset expression. */
+
+ void visit (SymOffExp *e)
+ {
+ /* Build the address and offset of the symbol. */
+ size_t soffset = ((SymOffExp *) e)->offset;
+ tree result = get_decl_tree (e->var);
+ TREE_USED (result) = 1;
+
+ if (declaration_reference_p (e->var))
+ gcc_assert (POINTER_TYPE_P (TREE_TYPE (result)));
+ else
+ result = build_address (result);
+
+ if (!soffset)
+ result = d_convert (build_ctype (e->type), result);
+ else
+ {
+ tree offset = size_int (soffset);
+ result = build_nop (build_ctype (e->type),
+ build_offset (result, offset));
+ }
+
+ this->result_ = result;
+ }
+
+ /* Build a variable expression. */
+
+ void visit (VarExp *e)
+ {
+ if (e->var->needThis ())
+ {
+ error ("need %<this%> to access member %qs", e->var->ident->toChars ());
+ this->result_ = error_mark_node;
+ return;
+ }
+ else if (e->var->ident == Identifier::idPool ("__ctfe"))
+ {
+ /* __ctfe is always false at run-time. */
+ this->result_ = integer_zero_node;
+ return;
+ }
+
+ /* This check is same as is done in FuncExp for lambdas. */
+ FuncLiteralDeclaration *fld = e->var->isFuncLiteralDeclaration ();
+ if (fld != NULL)
+ {
+ if (fld->tok == TOKreserved)
+ {
+ fld->tok = TOKfunction;
+ fld->vthis = NULL;
+ }
+
+ /* Compiler the function literal body. */
+ build_decl_tree (fld);
+ }
+
+ if (this->constp_)
+ {
+ /* Want the initializer, not the expression. */
+ VarDeclaration *var = e->var->isVarDeclaration ();
+ SymbolDeclaration *sd = e->var->isSymbolDeclaration ();
+ tree init = NULL_TREE;
+
+ if (var && (var->isConst () || var->isImmutable ())
+ && e->type->toBasetype ()->ty != Tsarray && var->_init)
+ {
+ if (var->inuse)
+ error_at (make_location_t (e->loc), "recursive reference %qs",
+ e->toChars ());
+ else
+ {
+ var->inuse++;
+ init = build_expr (initializerToExpression (var->_init), true);
+ var->inuse--;
+ }
+ }
+ else if (sd && sd->dsym)
+ init = layout_struct_initializer (sd->dsym);
+ else
+ error_at (make_location_t (e->loc), "non-constant expression %qs",
+ e->toChars ());
+
+ if (init != NULL_TREE)
+ this->result_ = init;
+ else
+ this->result_ = error_mark_node;
+ }
+ else
+ {
+ tree result = get_decl_tree (e->var);
+ TREE_USED (result) = 1;
+
+ /* For variables that are references - currently only out/inout
+ arguments; objects don't count - evaluating the variable means
+ we want what it refers to. */
+ if (declaration_reference_p (e->var))
+ result = indirect_ref (build_ctype (e->var->type), result);
+
+ this->result_ = result;
+ }
+ }
+
+ /* Build a this variable expression. */
+
+ void visit (ThisExp *e)
+ {
+ FuncDeclaration *fd = d_function_chain ? d_function_chain->function : NULL;
+ tree result = NULL_TREE;
+
+ if (e->var)
+ result = get_decl_tree (e->var);
+ else
+ {
+ gcc_assert (fd && fd->vthis);
+ result = get_decl_tree (fd->vthis);
+ }
+
+ if (e->type->ty == Tstruct)
+ result = build_deref (result);
+
+ this->result_ = result;
+ }
+
+ /* Build a new expression, which allocates memory either on the garbage
+ collected heap or by using a class or struct specific allocator. */
+
+ void visit (NewExp *e)
+ {
+ Type *tb = e->type->toBasetype ();
+ tree result;
+
+ if (e->allocator)
+ gcc_assert (e->newargs);
+
+ if (tb->ty == Tclass)
+ {
+ /* Allocating a new class. */
+ tb = e->newtype->toBasetype ();
+ gcc_assert (tb->ty == Tclass);
+
+ ClassDeclaration *cd = ((TypeClass *) tb)->sym;
+ tree type = build_ctype (tb);
+ tree setup_exp = NULL_TREE;
+ tree new_call;
+
+ if (e->onstack)
+ {
+ /* If being used as an initializer for a local variable with scope
+ storage class, then the instance is allocated on the stack
+ rather than the heap or using the class specific allocator. */
+ tree var = build_local_temp (TREE_TYPE (type));
+ new_call = build_nop (type, build_address (var));
+ setup_exp = modify_expr (var, aggregate_initializer_decl (cd));
+ }
+ else if (e->allocator)
+ {
+ /* Call class allocator, and copy the initializer into memory. */
+ new_call = d_build_call_expr (e->allocator, NULL_TREE, e->newargs);
+ new_call = d_save_expr (new_call);
+ new_call = build_nop (type, new_call);
+ setup_exp = modify_expr (build_deref (new_call),
+ aggregate_initializer_decl (cd));
+ }
+ else
+ {
+ /* Generate: _d_newclass() */
+ tree arg = build_address (get_classinfo_decl (cd));
+ new_call = build_libcall (LIBCALL_NEWCLASS, tb, 1, arg);
+ }
+
+ /* Set the context pointer for nested classes. */
+ if (cd->isNested ())
+ {
+ tree field = get_symbol_decl (cd->vthis);
+ tree value = NULL_TREE;
+
+ if (e->thisexp)
+ {
+ ClassDeclaration *tcd = e->thisexp->type->isClassHandle ();
+ Dsymbol *outer = cd->toParent2 ();
+ int offset = 0;
+
+ value = build_expr (e->thisexp);
+ if (outer != tcd)
+ {
+ ClassDeclaration *ocd = outer->isClassDeclaration ();
+ gcc_assert (ocd->isBaseOf (tcd, &offset));
+ /* Could just add offset... */
+ value = convert_expr (value, e->thisexp->type, ocd->type);
+ }
+ }
+ else
+ value = build_vthis (cd);
+
+ if (value != NULL_TREE)
+ {
+ /* Generate: (new())->vthis = this; */
+ new_call = d_save_expr (new_call);
+ field = component_ref (build_deref (new_call), field);
+ setup_exp = compound_expr (setup_exp,
+ modify_expr (field, value));
+ }
+ }
+ new_call = compound_expr (setup_exp, new_call);
+
+ /* Call the class constructor. */
+ if (e->member)
+ result = d_build_call_expr (e->member, new_call, e->arguments);
+ else
+ result = new_call;
+
+ if (e->argprefix)
+ result = compound_expr (build_expr (e->argprefix), result);
+ }
+ else if (tb->ty == Tpointer && tb->nextOf ()->toBasetype ()->ty == Tstruct)
+ {
+ /* Allocating memory for a new struct. */
+ Type *htype = e->newtype->toBasetype ();
+ gcc_assert (htype->ty == Tstruct);
+ gcc_assert (!e->onstack);
+
+ TypeStruct *stype = (TypeStruct *) htype;
+ StructDeclaration *sd = stype->sym;
+ tree new_call;
+
+ /* Cannot new an opaque struct. */
+ if (sd->size (e->loc) == 0)
+ {
+ this->result_ = d_convert (build_ctype (e->type),
+ integer_zero_node);
+ return;
+ }
+
+ if (e->allocator)
+ {
+ /* Call struct allocator. */
+ new_call = d_build_call_expr (e->allocator, NULL_TREE, e->newargs);
+ new_call = build_nop (build_ctype (tb), new_call);
+ }
+ else
+ {
+ /* Generate: _d_newitemT() */
+ libcall_fn libcall = htype->isZeroInit ()
+ ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT;
+ tree arg = build_typeinfo (e->newtype);
+ new_call = build_libcall (libcall, tb, 1, arg);
+ }
+
+ if (e->member || !e->arguments)
+ {
+ /* Set the context pointer for nested structs. */
+ if (sd->isNested ())
+ {
+ tree value = build_vthis (sd);
+ tree field = get_symbol_decl (sd->vthis);
+ tree type = build_ctype (stype);
+
+ new_call = d_save_expr (new_call);
+ field = component_ref (indirect_ref (type, new_call), field);
+ new_call = compound_expr (modify_expr (field, value), new_call);
+ }
+
+ /* Call the struct constructor. */
+ if (e->member)
+ result = d_build_call_expr (e->member, new_call, e->arguments);
+ else
+ result = new_call;
+ }
+ else
+ {
+ /* If we have a user supplied initializer, then set-up with a
+ struct literal. */
+ if (e->arguments != NULL && sd->fields.dim != 0)
+ {
+ StructLiteralExp *se = StructLiteralExp::create (e->loc, sd,
+ e->arguments,
+ htype);
+ new_call = d_save_expr (new_call);
+ se->type = sd->type;
+ se->sym = new_call;
+ result = compound_expr (build_expr (se), new_call);
+ }
+ else
+ result = new_call;
+ }
+
+ if (e->argprefix)
+ result = compound_expr (build_expr (e->argprefix), result);
+ }
+ else if (tb->ty == Tarray)
+ {
+ /* Allocating memory for a new D array. */
+ tb = e->newtype->toBasetype ();
+ gcc_assert (tb->ty == Tarray);
+ TypeDArray *tarray = (TypeDArray *) tb;
+
+ gcc_assert (!e->allocator);
+ gcc_assert (e->arguments && e->arguments->dim >= 1);
+
+ if (e->arguments->dim == 1)
+ {
+ /* Single dimension array allocations. */
+ Expression *arg = (*e->arguments)[0];
+
+ if (tarray->next->size () == 0)
+ {
+ /* Array element size is unknown. */
+ this->result_ = d_array_value (build_ctype (e->type),
+ size_int (0), null_pointer_node);
+ return;
+ }
+
+ libcall_fn libcall = tarray->next->isZeroInit ()
+ ? LIBCALL_NEWARRAYT : LIBCALL_NEWARRAYIT;
+ result = build_libcall (libcall, tb, 2,
+ build_typeinfo (e->type),
+ build_expr (arg));
+ }
+ else
+ {
+ /* Multidimensional array allocations. */
+ vec<constructor_elt, va_gc> *elms = NULL;
+ Type *telem = e->newtype->toBasetype ();
+ tree tarray = make_array_type (Type::tsize_t, e->arguments->dim);
+ tree var = create_temporary_var (tarray);
+ tree init = build_constructor (TREE_TYPE (var), NULL);
+
+ for (size_t i = 0; i < e->arguments->dim; i++)
+ {
+ Expression *arg = (*e->arguments)[i];
+ CONSTRUCTOR_APPEND_ELT (elms, size_int (i), build_expr (arg));
+
+ gcc_assert (telem->ty == Tarray);
+ telem = telem->toBasetype ()->nextOf ();
+ gcc_assert (telem);
+ }
+
+ CONSTRUCTOR_ELTS (init) = elms;
+ DECL_INITIAL (var) = init;
+
+ /* Generate: _d_newarraymTX(ti, dims)
+ or: _d_newarraymiTX(ti, dims) */
+ libcall_fn libcall = telem->isZeroInit ()
+ ? LIBCALL_NEWARRAYMTX : LIBCALL_NEWARRAYMITX;
+
+ tree tinfo = build_typeinfo (e->type);
+ tree dims = d_array_value (build_ctype (Type::tsize_t->arrayOf ()),
+ size_int (e->arguments->dim),
+ build_address (var));
+
+ result = build_libcall (libcall, tb, 2, tinfo, dims);
+ result = bind_expr (var, result);
+ }
+
+ if (e->argprefix)
+ result = compound_expr (build_expr (e->argprefix), result);
+ }
+ else if (tb->ty == Tpointer)
+ {
+ /* Allocating memory for a new pointer. */
+ TypePointer *tpointer = (TypePointer *) tb;
+
+ if (tpointer->next->size () == 0)
+ {
+ /* Pointer element size is unknown. */
+ this->result_ = d_convert (build_ctype (e->type),
+ integer_zero_node);
+ return;
+ }
+
+ libcall_fn libcall = tpointer->next->isZeroInit (e->loc)
+ ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT;
+
+ tree arg = build_typeinfo (e->newtype);
+ result = build_libcall (libcall, tb, 1, arg);
+
+ if (e->arguments && e->arguments->dim == 1)
+ {
+ result = d_save_expr (result);
+ tree init = modify_expr (build_deref (result),
+ build_expr ((*e->arguments)[0]));
+ result = compound_expr (init, result);
+ }
+
+ if (e->argprefix)
+ result = compound_expr (build_expr (e->argprefix), result);
+ }
+ else
+ gcc_unreachable ();
+
+ this->result_ = convert_expr (result, tb, e->type);
+ }
+
+ /* Build an integer literal. */
+
+ void visit (IntegerExp *e)
+ {
+ tree ctype = build_ctype (e->type->toBasetype ());
+ this->result_ = build_integer_cst (e->value, ctype);
+ }
+
+ /* Build a floating-point literal. */
+
+ void visit (RealExp *e)
+ {
+ this->result_ = build_float_cst (e->value, e->type->toBasetype ());
+ }
+
+ /* Build a complex literal. */
+
+ void visit (ComplexExp *e)
+ {
+ Type *tnext;
+
+ switch (e->type->toBasetype ()->ty)
+ {
+ case Tcomplex32:
+ tnext = (TypeBasic *) Type::tfloat32;
+ break;
+
+ case Tcomplex64:
+ tnext = (TypeBasic *) Type::tfloat64;
+ break;
+
+ case Tcomplex80:
+ tnext = (TypeBasic *) Type::tfloat80;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ this->result_ = build_complex (build_ctype (e->type),
+ build_float_cst (creall (e->value), tnext),
+ build_float_cst (cimagl (e->value), tnext));
+ }
+
+ /* Build a string literal, all strings are null terminated except for
+ static arrays. */
+
+ void visit (StringExp *e)
+ {
+ Type *tb = e->type->toBasetype ();
+ tree type = build_ctype (e->type);
+
+ if (tb->ty == Tsarray)
+ {
+ /* Turn the string into a constructor for the static array. */
+ vec<constructor_elt, va_gc> *elms = NULL;
+ vec_safe_reserve (elms, e->len);
+ tree etype = TREE_TYPE (type);
+
+ for (size_t i = 0; i < e->len; i++)
+ {
+ tree value = build_integer_cst (e->charAt (i), etype);
+ CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value);
+ }
+
+ tree ctor = build_constructor (type, elms);
+ TREE_CONSTANT (ctor) = 1;
+ this->result_ = ctor;
+ }
+ else
+ {
+ /* Copy the string contents to a null terminated string. */
+ dinteger_t length = (e->len * e->sz);
+ char *string = XALLOCAVEC (char, length + 1);
+ memcpy (string, e->string, length);
+ string[length] = '\0';
+
+ /* String value and type includes the null terminator. */
+ tree value = build_string (length, string);
+ TREE_TYPE (value) = make_array_type (tb->nextOf (), length + 1);
+ value = build_address (value);
+
+ if (tb->ty == Tarray)
+ value = d_array_value (type, size_int (e->len), value);
+
+ TREE_CONSTANT (value) = 1;
+ this->result_ = d_convert (type, value);
+ }
+ }
+
+ /* Build a tuple literal. Just an argument list that may have
+ side effects that need evaluation. */
+
+ void visit (TupleExp *e)
+ {
+ tree result = NULL_TREE;
+
+ if (e->e0)
+ result = build_expr (e->e0);
+
+ for (size_t i = 0; i < e->exps->dim; ++i)
+ {
+ Expression *exp = (*e->exps)[i];
+ result = compound_expr (result, build_expr (exp));
+ }
+
+ if (result == NULL_TREE)
+ result = void_node;
+
+ this->result_ = result;
+ }
+
+ /* Build an array literal. The common type of the all elements is taken to
+ be the type of the array element, and all elements are implicitly
+ converted to that type. */
+
+ void visit (ArrayLiteralExp *e)
+ {
+ Type *tb = e->type->toBasetype ();
+
+ /* Implicitly convert void[n] to ubyte[n]. */
+ if (tb->ty == Tsarray && tb->nextOf ()->toBasetype ()->ty == Tvoid)
+ tb = Type::tuns8->sarrayOf (((TypeSArray *) tb)->dim->toUInteger ());
+
+ gcc_assert (tb->ty == Tarray || tb->ty == Tsarray || tb->ty == Tpointer);
+
+ /* Handle empty array literals. */
+ if (e->elements->dim == 0)
+ {
+ if (tb->ty == Tarray)
+ this->result_ = d_array_value (build_ctype (e->type),
+ size_int (0), null_pointer_node);
+ else
+ this->result_ = build_constructor (make_array_type (tb->nextOf (), 0),
+ NULL);
+
+ return;
+ }
+
+ /* Build an expression that assigns the expressions in ELEMENTS to
+ a constructor. */
+ vec<constructor_elt, va_gc> *elms = NULL;
+ vec_safe_reserve (elms, e->elements->dim);
+ bool constant_p = true;
+ tree saved_elems = NULL_TREE;
+
+ Type *etype = tb->nextOf ();
+ tree satype = make_array_type (etype, e->elements->dim);
+
+ for (size_t i = 0; i < e->elements->dim; i++)
+ {
+ Expression *expr = e->getElement (i);
+ tree value = build_expr (expr, this->constp_);
+
+ /* Only append nonzero values, the backend will zero out the rest
+ of the constructor as we don't set CONSTRUCTOR_NO_CLEARING. */
+ if (!initializer_zerop (value))
+ {
+ if (!TREE_CONSTANT (value))
+ constant_p = false;
+
+ /* Split construction of values out of the constructor if there
+ may be side effects. */
+ tree init = stabilize_expr (&value);
+ if (init != NULL_TREE)
+ saved_elems = compound_expr (saved_elems, init);
+
+ CONSTRUCTOR_APPEND_ELT (elms, size_int (i),
+ convert_expr (value, expr->type, etype));
+ }
+ }
+
+ /* Now return the constructor as the correct type. For static arrays there
+ is nothing else to do. For dynamic arrays, return a two field struct.
+ For pointers, return the address. */
+ tree ctor = build_constructor (satype, elms);
+ tree type = build_ctype (e->type);
+
+ /* Nothing else to do for static arrays. */
+ if (tb->ty == Tsarray || this->constp_)
+ {
+ /* Can't take the address of the constructor, so create an anonymous
+ static symbol, and then refer to it. */
+ if (tb->ty != Tsarray)
+ {
+ tree decl = build_artificial_decl (TREE_TYPE (ctor), ctor, "A");
+ ctor = build_address (decl);
+ if (tb->ty == Tarray)
+ ctor = d_array_value (type, size_int (e->elements->dim), ctor);
+
+ d_pushdecl (decl);
+ rest_of_decl_compilation (decl, 1, 0);
+ }
+
+ /* If the array literal is readonly or static. */
+ if (constant_p)
+ TREE_CONSTANT (ctor) = 1;
+ if (constant_p && initializer_constant_valid_p (ctor, TREE_TYPE (ctor)))
+ TREE_STATIC (ctor) = 1;
+
+ this->result_ = compound_expr (saved_elems, d_convert (type, ctor));
+ }
+ else
+ {
+ /* Allocate space on the memory managed heap. */
+ tree mem = build_libcall (LIBCALL_ARRAYLITERALTX,
+ etype->pointerTo (), 2,
+ build_typeinfo (etype->arrayOf ()),
+ size_int (e->elements->dim));
+ mem = d_save_expr (mem);
+
+ /* Now copy the constructor into memory. */
+ tree tmemcpy = builtin_decl_explicit (BUILT_IN_MEMCPY);
+ tree size = size_mult_expr (size_int (e->elements->dim),
+ size_int (tb->nextOf ()->size ()));
+
+ tree result = build_call_expr (tmemcpy, 3, mem,
+ build_address (ctor), size);
+
+ /* Return the array pointed to by MEM. */
+ result = compound_expr (result, mem);
+
+ if (tb->ty == Tarray)
+ result = d_array_value (type, size_int (e->elements->dim), result);
+
+ this->result_ = compound_expr (saved_elems, result);
+ }
+ }
+
+ /* Build an associative array literal. The common type of the all keys is
+ taken to be the key type, and common type of all values the value type.
+ All keys and values are then implicitly converted as needed. */
+
+ void visit (AssocArrayLiteralExp *e)
+ {
+ /* Want the mutable type for typeinfo reference. */
+ Type *tb = e->type->toBasetype ()->mutableOf ();
+ gcc_assert (tb->ty == Taarray);
+
+ /* Handle empty assoc array literals. */
+ TypeAArray *ta = (TypeAArray *) tb;
+ if (e->keys->dim == 0)
+ {
+ this->result_ = build_constructor (build_ctype (ta), NULL);
+ return;
+ }
+
+ /* Build an expression that assigns all expressions in KEYS
+ to a constructor. */
+ vec<constructor_elt, va_gc> *kelts = NULL;
+ vec_safe_reserve (kelts, e->keys->dim);
+ for (size_t i = 0; i < e->keys->dim; i++)
+ {
+ Expression *key = (*e->keys)[i];
+ tree t = build_expr (key);
+ CONSTRUCTOR_APPEND_ELT (kelts, size_int (i),
+ convert_expr (t, key->type, ta->index));
+ }
+ tree tkeys = make_array_type (ta->index, e->keys->dim);
+ tree akeys = build_constructor (tkeys, kelts);
+
+ /* Do the same with all expressions in VALUES. */
+ vec<constructor_elt, va_gc> *velts = NULL;
+ vec_safe_reserve (velts, e->values->dim);
+ for (size_t i = 0; i < e->values->dim; i++)
+ {
+ Expression *value = (*e->values)[i];
+ tree t = build_expr (value);
+ CONSTRUCTOR_APPEND_ELT (velts, size_int (i),
+ convert_expr (t, value->type, ta->next));
+ }
+ tree tvals = make_array_type (ta->next, e->values->dim);
+ tree avals = build_constructor (tvals, velts);
+
+ /* Generate: _d_assocarrayliteralTX (ti, keys, vals); */
+ tree keys = d_array_value (build_ctype (ta->index->arrayOf ()),
+ size_int (e->keys->dim), build_address (akeys));
+ tree vals = d_array_value (build_ctype (ta->next->arrayOf ()),
+ size_int (e->values->dim),
+ build_address (avals));
+
+ tree mem = build_libcall (LIBCALL_ASSOCARRAYLITERALTX, Type::tvoidptr, 3,
+ build_typeinfo (ta), keys, vals);
+
+ /* Return an associative array pointed to by MEM. */
+ tree aatype = build_ctype (ta);
+ vec<constructor_elt, va_gc> *ce = NULL;
+ CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem);
+
+ this->result_ = build_nop (build_ctype (e->type),
+ build_constructor (aatype, ce));
+ }
+
+ /* Build a struct literal. */
+
+ void visit (StructLiteralExp *e)
+ {
+ /* Handle empty struct literals. */
+ if (e->elements == NULL || e->sd->fields.dim == 0)
+ {
+ this->result_ = build_constructor (build_ctype (e->type), NULL);
+ return;
+ }
+
+ /* Building sinit trees are delayed until after frontend semantic
+ processing has complete. Build the static initializer now. */
+ if (e->useStaticInit && !this->constp_)
+ {
+ this->result_ = aggregate_initializer_decl (e->sd);
+ return;
+ }
+
+ /* Build a constructor that assigns the expressions in ELEMENTS
+ at each field index that has been filled in. */
+ vec<constructor_elt, va_gc> *ve = NULL;
+ tree saved_elems = NULL_TREE;
+
+ /* CTFE may fill the hidden pointer by NullExp. */
+ gcc_assert (e->elements->dim <= e->sd->fields.dim);
+
+ Type *tb = e->type->toBasetype ();
+ gcc_assert (tb->ty == Tstruct);
+
+ for (size_t i = 0; i < e->elements->dim; i++)
+ {
+ Expression *exp = (*e->elements)[i];
+ if (!exp)
+ continue;
+
+ VarDeclaration *field = e->sd->fields[i];
+ Type *type = exp->type->toBasetype ();
+ Type *ftype = field->type->toBasetype ();
+ tree value = NULL_TREE;
+
+ if (ftype->ty == Tsarray && !same_type_p (type, ftype))
+ {
+ /* Initialize a static array with a single element. */
+ tree elem = build_expr (exp, this->constp_);
+ elem = d_save_expr (elem);
+
+ if (initializer_zerop (elem))
+ value = build_constructor (build_ctype (ftype), NULL);
+ else
+ value = build_array_from_val (ftype, elem);
+ }
+ else
+ {
+ value = convert_expr (build_expr (exp, this->constp_),
+ exp->type, field->type);
+ }
+
+ /* Split construction of values out of the constructor. */
+ tree init = stabilize_expr (&value);
+ if (init != NULL_TREE)
+ saved_elems = compound_expr (saved_elems, init);
+
+ CONSTRUCTOR_APPEND_ELT (ve, get_symbol_decl (field), value);
+ }
+
+ /* Maybe setup hidden pointer to outer scope context. */
+ if (e->sd->isNested () && e->elements->dim != e->sd->fields.dim
+ && this->constp_ == false)
+ {
+ tree field = get_symbol_decl (e->sd->vthis);
+ tree value = build_vthis (e->sd);
+ CONSTRUCTOR_APPEND_ELT (ve, field, value);
+ gcc_assert (e->useStaticInit == false);
+ }
+
+ /* Build a constructor in the correct shape of the aggregate type. */
+ tree ctor = build_struct_literal (build_ctype (e->type), ve);
+
+ /* Nothing more to do for constant literals. */
+ if (this->constp_)
+ {
+ /* If the struct literal is a valid for static data. */
+ if (TREE_CONSTANT (ctor)
+ && initializer_constant_valid_p (ctor, TREE_TYPE (ctor)))
+ TREE_STATIC (ctor) = 1;
+
+ this->result_ = compound_expr (saved_elems, ctor);
+ return;
+ }
+
+ if (e->sym != NULL)
+ {
+ tree var = build_deref (e->sym);
+ ctor = compound_expr (modify_expr (var, ctor), var);
+ this->result_ = compound_expr (saved_elems, ctor);
+ }
+ else if (e->sd->isUnionDeclaration ())
+ {
+ /* For unions, use memset to fill holes in the object. */
+ tree var = build_local_temp (TREE_TYPE (ctor));
+ tree tmemset = builtin_decl_explicit (BUILT_IN_MEMSET);
+ tree init = build_call_expr (tmemset, 3, build_address (var),
+ size_zero_node,
+ size_int (e->sd->structsize));
+
+ init = compound_expr (init, saved_elems);
+ init = compound_expr (init, modify_expr (var, ctor));
+ this->result_ = compound_expr (init, var);
+ }
+ else
+ this->result_ = compound_expr (saved_elems, ctor);
+ }
+
+ /* Build a null literal. */
+
+ void visit (NullExp *e)
+ {
+ Type *tb = e->type->toBasetype ();
+ tree value;
+
+ /* Handle certain special case conversions, where the underlying type is an
+ aggregate with a nullable interior pointer. */
+ if (tb->ty == Tarray)
+ {
+ /* For dynamic arrays, set length and pointer fields to zero. */
+ value = d_array_value (build_ctype (e->type), size_int (0),
+ null_pointer_node);
+ }
+ else if (tb->ty == Taarray)
+ {
+ /* For associative arrays, set the pointer field to null. */
+ value = build_constructor (build_ctype (e->type), NULL);
+ }
+ else if (tb->ty == Tdelegate)
+ {
+ /* For delegates, set the frame and function pointer to null. */
+ value = build_delegate_cst (null_pointer_node,
+ null_pointer_node, e->type);
+ }
+ else
+ value = d_convert (build_ctype (e->type), integer_zero_node);
+
+ TREE_CONSTANT (value) = 1;
+ this->result_ = value;
+ }
+
+ /* Build a vector literal. */
+
+ void visit (VectorExp *e)
+ {
+ tree type = build_ctype (e->type);
+ tree etype = TREE_TYPE (type);
+
+ /* First handle array literal expressions. */
+ if (e->e1->op == TOKarrayliteral)
+ {
+ ArrayLiteralExp *ale = ((ArrayLiteralExp *) e->e1);
+ vec<constructor_elt, va_gc> *elms = NULL;
+ bool constant_p = true;
+
+ vec_safe_reserve (elms, ale->elements->dim);
+ for (size_t i = 0; i < ale->elements->dim; i++)
+ {
+ Expression *expr = ale->getElement (i);
+ tree value = d_convert (etype, build_expr (expr, this->constp_));
+ if (!CONSTANT_CLASS_P (value))
+ constant_p = false;
+
+ CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value);
+ }
+
+ /* Build a VECTOR_CST from a constant vector constructor. */
+ if (constant_p)
+ this->result_ = build_vector_from_ctor (type, elms);
+ else
+ this->result_ = build_constructor (type, elms);
+ }
+ else
+ {
+ /* Build constructor from single value. */
+ tree val = d_convert (etype, build_expr (e->e1, this->constp_));
+ this->result_ = build_vector_from_val (type, val);
+ }
+ }
+
+ /* Build a static class literal, return its reference. */
+
+ void visit (ClassReferenceExp *e)
+ {
+ /* The result of build_new_class_expr is a RECORD_TYPE, we want
+ the reference. */
+ tree var = build_address (build_new_class_expr (e));
+
+ /* If the type of this literal is an interface, the we must add the
+ interface offset to symbol. */
+ if (this->constp_)
+ {
+ TypeClass *tc = (TypeClass *) e->type;
+ InterfaceDeclaration *to = tc->sym->isInterfaceDeclaration ();
+
+ if (to != NULL)
+ {
+ ClassDeclaration *from = e->originalClass ();
+ int offset = 0;
+
+ gcc_assert (to->isBaseOf (from, &offset) != 0);
+
+ if (offset != 0)
+ var = build_offset (var, size_int (offset));
+ }
+ }
+
+ this->result_ = var;
+ }
+
+ /* These expressions are mainly just a placeholders in the frontend.
+ We shouldn't see them here. */
+
+ void visit (ScopeExp *e)
+ {
+ error_at (make_location_t (e->loc), "%qs is not an expression",
+ e->toChars ());
+ this->result_ = error_mark_node;
+ }
+
+ void visit (TypeExp *e)
+ {
+ error_at (make_location_t (e->loc), "type %qs is not an expression",
+ e->toChars ());
+ this->result_ = error_mark_node;
+ }
+};
+
+
+/* Main entry point for ExprVisitor interface to generate code for
+ the Expression AST class E. If CONST_P is true, then E is a
+ constant expression. */
+
+tree
+build_expr (Expression *e, bool const_p)
+{
+ ExprVisitor v = ExprVisitor (const_p);
+ location_t saved_location = input_location;
+
+ input_location = make_location_t (e->loc);
+ e->accept (&v);
+ tree expr = v.result ();
+ input_location = saved_location;
+
+ /* Check if initializer expression is valid constant. */
+ if (const_p && !initializer_constant_valid_p (expr, TREE_TYPE (expr)))
+ {
+ error_at (make_location_t (e->loc), "non-constant expression %qs",
+ e->toChars ());
+ return error_mark_node;
+ }
+
+ return expr;
+}
+
+/* Same as build_expr, but also calls destructors on any temporaries. */
+
+tree
+build_expr_dtor (Expression *e)
+{
+ /* Codegen can be improved by determining if no exceptions can be thrown
+ between the ctor and dtor, and eliminating the ctor and dtor. */
+ size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope);
+ tree result = build_expr (e);
+
+ if (saved_vars != vec_safe_length (d_function_chain->vars_in_scope))
+ {
+ result = fold_build_cleanup_point_expr (TREE_TYPE (result), result);
+ vec_safe_truncate (d_function_chain->vars_in_scope, saved_vars);
+ }
+
+ return result;
+}
+
+/* Same as build_expr_dtor, but handles the result of E as a return value. */
+
+tree
+build_return_dtor (Expression *e, Type *type, TypeFunction *tf)
+{
+ size_t saved_vars = vec_safe_length (d_function_chain->vars_in_scope);
+ tree result = build_expr (e);
+
+ /* Convert for initializing the DECL_RESULT. */
+ result = convert_expr (result, e->type, type);
+
+ /* If we are returning a reference, take the address. */
+ if (tf->isref)
+ result = build_address (result);
+
+ /* The decl to store the return expression. */
+ tree decl = DECL_RESULT (cfun->decl);
+
+ /* Split comma expressions, so that the result is returned directly. */
+ tree expr = stabilize_expr (&result);
+ result = build_assign (INIT_EXPR, decl, result);
+ result = compound_expr (expr, return_expr (result));
+
+ /* May nest the return expression inside the try/finally expression. */
+ if (saved_vars != vec_safe_length (d_function_chain->vars_in_scope))
+ {
+ result = fold_build_cleanup_point_expr (TREE_TYPE (result), result);
+ vec_safe_truncate (d_function_chain->vars_in_scope, saved_vars);
+ }
+
+ return result;
+}
+