diff options
author | Martin Liska <mliska@suse.cz> | 2022-08-08 09:05:36 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2022-08-08 09:05:36 +0200 |
commit | b3a187edd33b89acf19ba46f3b8070d7c977ac57 (patch) | |
tree | 43549f6851052eb2844ea76358af30a9fd302ec5 | |
parent | 89eca196c99645ee1abefcf8b4a9dd84edd87ad6 (diff) | |
parent | 2633c8d8f338f1e2b53d3757f3edf4179bfcc218 (diff) | |
download | gcc-b3a187edd33b89acf19ba46f3b8070d7c977ac57.zip gcc-b3a187edd33b89acf19ba46f3b8070d7c977ac57.tar.gz gcc-b3a187edd33b89acf19ba46f3b8070d7c977ac57.tar.bz2 |
Merge branch 'master' into devel/sphinx
293 files changed, 7773 insertions, 1818 deletions
@@ -1,3 +1,12 @@ +2022-08-04 Eugene Rozenfeld <erozen@microsoft.com> + + * MAINTAINERS: Add myself as AutoFDO maintainer. + +2022-08-01 Roger Sayle <roger@nextmovesoftware.com> + Arnaud Charlet <charlet@adacore.com> + + * configure: Regenerate. + 2022-07-31 Roger Sayle <roger@nextmovesoftware.com> PR bootstrap/106472 diff --git a/MAINTAINERS b/MAINTAINERS index 1a37f44..02ced0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,6 +239,7 @@ tree-ssa Andrew MacLeod <amacleod@redhat.com> tree browser/unparser Sebastian Pop <sebpop@gmail.com> scev, data dependence Sebastian Pop <sebpop@gmail.com> profile feedback Jan Hubicka <hubicka@ucw.cz> +AutoFDO Eugene Rozenfeld <erozen@microsoft.com> reload Ulrich Weigand <uweigand@de.ibm.com> RTL optimizers Eric Botcazou <ebotcazou@libertysurf.fr> instruction combiner Segher Boessenkool <segher@kernel.crashing.org> @@ -603,7 +604,6 @@ Craig Rodrigues <rodrigc@gcc.gnu.org> Erven Rohou <erven.rohou@inria.fr> Ira Rosen <irar@il.ibm.com> Yvan Roux <yvan.roux@linaro.org> -Eugene Rozenfeld <erozen@microsoft.com> Silvius Rus <rus@google.com> Matthew Sachs <msachs@apple.com> Ankur Saini <arsenic@sourceware.org> diff --git a/config/ChangeLog b/config/ChangeLog index 7e2ea8d..09fe8c2 100644 --- a/config/ChangeLog +++ b/config/ChangeLog @@ -1,3 +1,9 @@ +2022-08-01 Roger Sayle <roger@nextmovesoftware.com> + Arnaud Charlet <charlet@adacore.com> + + * acx.m4 (AC_PROG_GNAT): Update conftest.adb to include + features required of the host gnat compiler. + 2022-06-01 David Seifert <soap@gentoo.org> PR plugins/95648 diff --git a/config/acx.m4 b/config/acx.m4 index b86c4f9..7efe98a 100644 --- a/config/acx.m4 +++ b/config/acx.m4 @@ -393,9 +393,13 @@ AC_DEFUN([ACX_PROG_GNAT], AC_REQUIRE([AC_PROG_CC]) AC_CHECK_TOOL(GNATBIND, gnatbind, no) AC_CHECK_TOOL(GNATMAKE, gnatmake, no) -AC_CACHE_CHECK([whether compiler driver understands Ada], +AC_CACHE_CHECK([whether compiler driver understands Ada and is recent enough], acx_cv_cc_gcc_supports_ada, [cat >conftest.adb <<EOF +pragma Warnings (Off); +with System.CRTL; +pragma Warnings (On); +use type System.CRTL.int64; procedure conftest is begin null; end conftest; EOF acx_cv_cc_gcc_supports_ada=no @@ -5602,12 +5602,16 @@ else GNATMAKE="$ac_cv_prog_GNATMAKE" fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler driver understands Ada" >&5 -$as_echo_n "checking whether compiler driver understands Ada... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler driver understands Ada and is recent enough" >&5 +$as_echo_n "checking whether compiler driver understands Ada and is recent enough... " >&6; } if ${acx_cv_cc_gcc_supports_ada+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.adb <<EOF +pragma Warnings (Off); +with System.CRTL; +pragma Warnings (On); +use type System.CRTL.int64; procedure conftest is begin null; end conftest; EOF acx_cv_cc_gcc_supports_ada=no diff --git a/contrib/ChangeLog b/contrib/ChangeLog index 7df00c7..a7c4f2f 100644 --- a/contrib/ChangeLog +++ b/contrib/ChangeLog @@ -1,3 +1,7 @@ +2022-08-02 Martin Liska <mliska@suse.cz> + + * gcc-changelog/git_commit.py: Do not deduce changelog for root ChangeLog. + 2022-07-22 Martin Liska <mliska@suse.cz> * git-commit-mklog.py: Do not parse -b argument. diff --git a/contrib/gcc-changelog/git_commit.py b/contrib/gcc-changelog/git_commit.py index a6b5ff0..7f6ff87 100755 --- a/contrib/gcc-changelog/git_commit.py +++ b/contrib/gcc-changelog/git_commit.py @@ -626,7 +626,7 @@ class GitCommit: def deduce_changelog_locations(self): for entry in self.changelog_entries: - if not entry.folder: + if entry.folder is None: changelog = None for file in entry.files: location = self.get_file_changelog_location(file) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 22cf563..2d41c22 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,355 @@ +2022-08-07 Roger Sayle <roger@nextmovesoftware.com> + + * config/i386/i386.md (*cmp<dwi>_doubleword): Change predicate + for x86_64_hilo_general_operand to general operand. Call + force_reg on parts that are not x86_64_immediate_operand. + +2022-08-05 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/105947 + * doc/invoke.texi: Add -Wanalyzer-jump-through-null. + +2022-08-05 Roger Sayle <roger@nextmovesoftware.com> + + * expmed.cc (emit_store_flag_1): Move code to expand double word + equality and inequality against zero or -1, using word operations, + to after trying to use the backend's cstore<mode>4 optab/expander. + +2022-08-05 Tamar Christina <tamar.christina@arm.com> + + PR middle-end/106534 + * tree-ssa-phiopt.cc (tree_ssa_phiopt_worker): Guard the + value_replacement and store_elim from diamonds. + +2022-08-05 Richard Biener <rguenther@suse.de> + + * tree-ssa-threadbackward.cc (back_threader::maybe_register_path): + Check whether the registry register_path rejected the path. + (back_threader_registry::register_path): Return whether + register_jump_thread succeeded. + +2022-08-05 Aldy Hernandez <aldyh@redhat.com> + + PR tree-optimization/106514 + * value-range.cc (unsupported_range::unsupported_range): Move... + * value-range.h (unsupported_range::unsupported_range): ...here. + (unsupported_range::set_undefined): New. + +2022-08-05 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106533 + * tree-loop-distribution.cc (loop_distribution::execute): Continue + analyzing the inner loops when find_seed_stmts_for_distribution + fails. + +2022-08-05 Andrew Pinski <apinski@marvell.com> + + * config/riscv/predicates.md (splittable_const_int_operand): + Remove the check for TARGET_64BIT for single bit const values. + +2022-08-04 Andrew MacLeod <amacleod@redhat.com> + + PR tree-optimization/106514 + * gimple-range-path.cc (path_range_query::compute_ranges_in_block): + Use EXECUTE_IF_AND_IN_BITMAP to loop over 2 bitmaps. + +2022-08-04 Tamar Christina <tamar.christina@arm.com> + + * match.pd: New bit_not rule. + +2022-08-04 Tamar Christina <tamar.christina@arm.com> + + PR middle-end/106519 + * tree-ssa-phiopt.cc (tree_ssa_phiopt_worker): Check final phi edge for + diamond shapes. + +2022-08-04 Sam Feifer <sfeifer@redhat.com> + + PR tree-optimization/106243 + * match.pd (-x & 1): New simplification. + +2022-08-04 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106521 + * gimple-loop-jam.cc (tree_loop_unroll_and_jam): Perform + CFG cleanup manually before rewriting into LC SSA. + +2022-08-04 Richard Biener <rguenther@suse.de> + + * tree-ssa-threadbackward.cc (populate_worklist): Remove. + (back_threader::resolve_phi): Likewise. + (back_threader::find_paths_to_names): Rewrite greedy search. + +2022-08-04 Ilya Leoshkevich <iii@linux.ibm.com> + + * config/s390/vector.md (V_HW_FT): New iterator. + * config/s390/vx-builtins.md (vsel<mode>): Use V_HW_FT instead + of V_HW. + +2022-08-03 Michael Meissner <meissner@linux.ibm.com> + + * config/rs6000/rs6000.cc (rs6000_option_override_internal): Remove code + setting -mblock-ops-vector-pair. + +2022-08-03 Andrew MacLeod <amacleod@redhat.com> + + PR tree-optimization/106514 + * value-relation.cc (path_oracle::killing_def) Do not walk the + equivalence set clearing bits. + +2022-08-03 Tamar Christina <tamar.christina@arm.com> + + * tree-ssa-phiopt.cc (minmax_replacement): Optionally search for the phi + sequence of a three-way conditional. + (replace_phi_edge_with_variable): Support diamonds. + (tree_ssa_phiopt_worker): Detect diamond phi structure for three-way + min/max. + (strip_bit_not, invert_minmax_code): New. + +2022-08-03 Richard Earnshaw <rearnsha@arm.com> + + PR rtl-optimization/106187 + * alias.h (mems_same_for_tbaa_p): Declare. + * alias.cc (mems_same_for_tbaa_p): New function. + * dse.cc (record_store): Use it instead of open-coding + alias check. + * cselib.h (cselib_redundant_set_p): Declare. + * cselib.cc: Include alias.h + (cselib_redundant_set_p): New function. + * cfgcleanup.cc: (mark_effect): Use cselib_redundant_set_p instead + of rtx_equal_for_cselib_p. + * postreload.cc (reload_cse_simplify): Use cselib_redundant_set_p. + (reload_cse_noop_set_p): Delete. + +2022-08-03 Martin Liska <mliska@suse.cz> + + * doc/gcov-dump.texi: Document the new option. + * gcov-dump.cc (main): Parse the new option. + (print_usage): Show the option. + (tag_counters): Sort key:value pairs of TOP N counter. + +2022-08-03 Martin Liska <mliska@suse.cz> + + * profile.cc (compute_branch_probabilities): Do not collect + stats unless TDF_DETAILS. + +2022-08-03 Roger Sayle <roger@nextmovesoftware.com> + Uroš Bizjak <ubizjak@gmail.com> + + PR target/47949 + * config/i386/i386.md (peephole2): New peephole2 to convert + SWI48 moves to/from %rax/%eax where the src is dead to xchg, + when optimizing for minimal size with -Oz. + +2022-08-03 Roger Sayle <roger@nextmovesoftware.com> + + * config/i386/i386.md (*cmp<dwi>_doubleword): Add a special case + to split comparisons against -1 using AND and CMP -1 instructions. + +2022-08-03 Roger Sayle <roger@nextmovesoftware.com> + + * config/i386/i386-features.cc (compute_convert_gain): Add gain + for converting suitable TImode shift to a V1TImode shift. + (timode_scalar_chain::convert_insn): Add support for converting + suitable ASHIFT and LSHIFTRT. + (timode_scalar_to_vector_candidate_p): Consider logical shifts + by integer constants that are multiples of 8 to be candidates. + +2022-08-03 Roger Sayle <roger@nextmovesoftware.com> + Segher Boessenkool <segher@kernel.crashing.org> + Richard Sandiford <richard.sandiford@arm.com> + + * simplify-rtx.cc (simplify_unary_operation_1) <ABS>: Add + optimizations for CLRSB, PARITY, POPCOUNT, SS_ABS and LSHIFTRT + that are all positive to complement the existing FFS and + idempotent ABS simplifications. + <SIGN_EXTEND>: Canonicalize SIGN_EXTEND to ZERO_EXTEND when + val_signbit_known_clear_p is true of the operand. + Simplify sign extensions of SUBREG truncations of operands + that are already suitably (zero) extended. + <ZERO_EXTEND>: Simplify zero extensions of SUBREG truncations + of operands that are already suitably zero extended. + +2022-08-02 Andrew MacLeod <amacleod@redhat.com> + + PR tree-optimization/106510 + * gimple-range-fold.cc (fur_source::register_outgoing_edges): + Check for unsupported statements early. + +2022-08-02 Andrew MacLeod <amacleod@redhat.com> + + PR tree-optimization/106474 + * gimple-range-cache.cc (ranger_cache::fill_block_cache): Query + range of equivalences that may contribute to the range. + +2022-08-02 Jose E. Marchesi <jose.marchesi@oracle.com> + + * btfout.cc (output_asm_btf_vlen_bytes): Do not use the CHAR + encoding bit in BTF. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * gimple-range-fold.cc (fold_using_range::range_of_phi): Remove + irange check. + (tree_lower_bound): New. + (tree_upper_bound): New. + (fold_using_range::range_of_ssa_name_with_loop_info): Convert to + vrange. + * gimple-range-fold.h (range_of_ssa_name_with_loop_info): Change + argument to vrange. + +2022-08-02 Richard Biener <rguenther@suse.de> + + * tree-ssa-threadbackward.cc + (back_threader_profitability::profitable_path_p): Apply + size constraints to all paths again. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * range-op-float.cc (finite_operands_p): New. + (frelop_early_resolve): New. + (default_frelop_fold_range): New. + (class foperator_equal): New. + (class foperator_not_equal): New. + (class foperator_lt): New. + (class foperator_le): New. + (class foperator_gt): New. + (class foperator_ge): New. + (class foperator_unordered): New. + (class foperator_ordered): New. + (class foperator_relop_unknown): New. + (floating_op_table::floating_op_table): Add above classes to + floating op table. + * value-range.h (frange::supports_p): Enable. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * tree-core.h (struct tree_ssa_name): Add frange_info and + reshuffle the rest. + * value-range-storage.cc (vrange_storage::alloc_slot): Add case + for frange. + (vrange_storage::set_vrange): Same. + (vrange_storage::get_vrange): Same. + (vrange_storage::fits_p): Same. + (frange_storage_slot::alloc_slot): New. + (frange_storage_slot::set_frange): New. + (frange_storage_slot::get_frange): New. + (frange_storage_slot::fits_p): New. + * value-range-storage.h (class frange_storage_slot): New. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * ipa-prop.cc (ipa_compute_jump_functions_for_edge): Limit ranger + query to integrals. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * value-range.cc (frange::set): Initialize m_props and cleanup. + +2022-08-02 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106497 + * tree-ssa-threadupdate.cc (fwd_jt_path_registry::update_cfg): + Also verify we can copy EDGE_COPY_SRC_JOINER_BLOCK. + +2022-08-02 Martin Liska <mliska@suse.cz> + + * profile.cc (compute_branch_probabilities): Dump details only + if TDF_DETAILS. + * symtab.cc (symtab_node::dump_base): Do not dump pointer unless + TDF_ADDRESS is used, it makes comparison harder. + +2022-08-02 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106498 + * omp-expand.cc (expand_omp_taskreg): Do not perform virtual + SSA update here. + (expand_omp_for): Or here. + (execute_expand_omp): Instead schedule it here together + with CFG cleanup via TODO. + +2022-08-02 Richard Biener <rguenther@suse.de> + + PR lto/106334 + * dwarf2out.cc (dwarf2out_register_external_die): Adjust + assert. + +2022-08-02 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106495 + * tree-ssa-threadbackward.cc + (back_threader_profitability::profitable_path_p): If known_edge + is probably never executed avoid threading. + +2022-08-01 David Malcolm <dmalcolm@redhat.com> + + * doc/invoke.texi (-Wanalyzer-putenv-of-auto-var): Fix copy&paste + error. + +2022-08-01 Roger Sayle <roger@nextmovesoftware.com> + Uroš Bizjak <ubizjak@gmail.com> + + PR target/106481 + * config/i386/i386-features.cc (timode_scalar_chain::convert_insn): + Convert a CONST_SCALAR_INT_P in a REG_EQUAL note into a V1TImode + CONST_VECTOR. + +2022-08-01 H.J. Lu <hjl.tools@gmail.com> + + PR target/83782 + * config/i386/i386.cc (ix86_ifunc_ref_local_ok): New. + (TARGET_IFUNC_REF_LOCAL_OK): Use it. + +2022-08-01 Jose E. Marchesi <jose.marchesi@oracle.com> + + PR debug/106263 + * ctfc.h (struct ctf_dtdef): Add field linkage. + * ctfc.cc (ctf_add_function): Set ctti_linkage. + * dwarf2ctf.cc (gen_ctf_function_type): Pass a linkage for + function types and subprograms. + * btfout.cc (btf_asm_func_type): Emit linkage information for the + function. + (btf_dtd_emit_preprocess_cb): Propagate the linkage information + for functions. + +2022-08-01 Andrew Stubbs <ams@codesourcery.com> + Jakub Jelinek <jakub@redhat.com> + + * omp-simd-clone.cc (simd_clone_adjust): Convert shift_cnt to match + the mask type. + +2022-08-01 Sam Feifer <sfeifer@redhat.com> + + PR tree-optimization/104992 + * match.pd (x / y * y == x): New simplification. + +2022-08-01 Aldy Hernandez <aldyh@redhat.com> + + * value-range.cc (tree_compare): New. + (frange::set): Make more general. + (frange::normalize_kind): Cleanup and return bool. + (frange::union_): Use normalize_kind return value. + (frange::intersect): Same. + (frange::verify_range): Remove unnecessary else. + * value-range.h (vrp_val_max): Move before frange class. + (vrp_val_min): Same. + (frange::frange): Remove set to m_type. + +2022-08-01 Aldy Hernandez <aldyh@redhat.com> + + * value-range.cc (vrange::supports_type_p): Use const_tree. + (irange::supports_type_p): Same. + (frange::supports_type_p): Same. + * value-range.h (Value_Range::supports_type_p): Same. + (irange::supports_p): Same. + +2022-08-01 Aldy Hernandez <aldyh@redhat.com> + + * gimple-range-fold.cc (fold_using_range::range_of_phi): Only + query SCEV for integers. + (fold_using_range::range_of_ssa_name_with_loop_info): Remove + irange check. + 2022-07-31 Roger Sayle <roger@nextmovesoftware.com> * config/i386/i386.md (define_expand <any_rotate>ti3): For diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index ed14e56..00f22f5 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20220801 +20220808 diff --git a/gcc/alias.cc b/gcc/alias.cc index 8c08452..d54feb1 100644 --- a/gcc/alias.cc +++ b/gcc/alias.cc @@ -389,6 +389,20 @@ refs_same_for_tbaa_p (tree earlier, tree later) || alias_set_subset_of (later_base_set, earlier_base_set)); } +/* Similar to refs_same_for_tbaa_p() but for use on MEM rtxs. */ +bool +mems_same_for_tbaa_p (rtx earlier, rtx later) +{ + gcc_assert (MEM_P (earlier)); + gcc_assert (MEM_P (later)); + + return ((MEM_ALIAS_SET (earlier) == MEM_ALIAS_SET (later) + || alias_set_subset_of (MEM_ALIAS_SET (later), + MEM_ALIAS_SET (earlier))) + && (!MEM_EXPR (earlier) + || refs_same_for_tbaa_p (MEM_EXPR (earlier), MEM_EXPR (later)))); +} + /* Returns a pointer to the alias set entry for ALIAS_SET, if there is such an entry, or NULL otherwise. */ diff --git a/gcc/alias.h b/gcc/alias.h index b259651..ee3db46 100644 --- a/gcc/alias.h +++ b/gcc/alias.h @@ -40,6 +40,7 @@ tree reference_alias_ptr_type_1 (tree *); bool alias_ptr_types_compatible_p (tree, tree); int compare_base_decls (tree, tree); bool refs_same_for_tbaa_p (tree, tree); +bool mems_same_for_tbaa_p (rtx, rtx); /* This alias set can be used to force a memory to conflict with all other memories, creating a barrier across which no memory reference diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index d1cb3ad..0b93219 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,21 @@ +2022-08-05 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/105947 + * analyzer.opt (Wanalyzer-jump-through-null): New option. + * engine.cc (class jump_through_null): New. + (exploded_graph::process_node): Complain about jumps through NULL + function pointers. + +2022-08-02 Immad Mir <mirimmad@outlook.com> + + PR analyzer/106298 + * sm-fd.cc (fd_state_machine::on_open): Add + creat, dup, dup2 and dup3 functions. + (enum dup): New. + (fd_state_machine::valid_to_unchecked_state): New. + (fd_state_machine::on_creat): New. + (fd_state_machine::on_dup): New. + 2022-07-28 David Malcolm <dmalcolm@redhat.com> PR analyzer/105893 diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt index 808ff36..c6d9c53 100644 --- a/gcc/analyzer/analyzer.opt +++ b/gcc/analyzer/analyzer.opt @@ -98,6 +98,10 @@ Wanalyzer-free-of-non-heap Common Var(warn_analyzer_free_of_non_heap) Init(1) Warning Warn about code paths in which a non-heap pointer is freed. +Wanalyzer-jump-through-null +Common Var(warn_analyzer_jump_through_null) Init(1) Warning +Warn about code paths in which a NULL function pointer is called. + Wanalyzer-malloc-leak Common Var(warn_analyzer_malloc_leak) Init(1) Warning Warn about code paths in which a heap-allocated pointer leaks. diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 85b7c5e..e8db00d 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -3705,6 +3705,46 @@ private: bool m_terminate_path; }; +/* A subclass of pending_diagnostic for complaining about jumps through NULL + function pointers. */ + +class jump_through_null : public pending_diagnostic_subclass<jump_through_null> +{ +public: + jump_through_null (const gcall *call) + : m_call (call) + {} + + const char *get_kind () const final override + { + return "jump_through_null"; + } + + bool operator== (const jump_through_null &other) const + { + return m_call == other.m_call; + } + + int get_controlling_option () const final override + { + return OPT_Wanalyzer_jump_through_null; + } + + bool emit (rich_location *rich_loc) final override + { + return warning_at (rich_loc, get_controlling_option (), + "jump through null pointer"); + } + + label_text describe_final_event (const evdesc::final_event &ev) final override + { + return ev.formatted_print ("jump through null pointer here"); + } + +private: + const gcall *m_call; +}; + /* The core of exploded_graph::process_worklist (the main analysis loop), handling one node in the worklist. @@ -4046,6 +4086,15 @@ exploded_graph::process_node (exploded_node *node) logger); if (!call_discovered) { + /* Check for jump through NULL. */ + if (tree fn_ptr = gimple_call_fn (call)) + { + const svalue *fn_ptr_sval + = model->get_rvalue (fn_ptr, &ctxt); + if (fn_ptr_sval->all_zeroes_p ()) + ctxt.warn (new jump_through_null (call)); + } + /* An unknown function or a special function was called at this point, in such case, don't terminate the analysis of the current function. diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index ed923ad..8bb76d7 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -69,6 +69,14 @@ enum access_directions DIRS_WRITE }; +/* An enum for distinguishing between dup, dup2 and dup3. */ +enum dup +{ + DUP_1, + DUP_2, + DUP_3 +}; + class fd_state_machine : public state_machine { public: @@ -114,7 +122,9 @@ public: bool is_readonly_fd_p (state_t s) const; bool is_writeonly_fd_p (state_t s) const; enum access_mode get_access_mode_from_flag (int flag) const; - + /* Function for one-to-one correspondence between valid + and unchecked states. */ + state_t valid_to_unchecked_state (state_t state) const; /* State for a constant file descriptor (>= 0) */ state_t m_constant_fd; @@ -147,6 +157,8 @@ public: private: void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call) const; + void on_creat (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call) const; void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call) const; void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, @@ -170,6 +182,9 @@ private: const gimple *stmt, const gcall *call, const tree callee_fndecl, const char *attr_name, access_directions fd_attr_access_dir) const; + void check_for_dup (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, const tree callee_fndecl, + enum dup kind) const; }; /* Base diagnostic class relative to fd_state_machine. */ @@ -723,6 +738,20 @@ fd_state_machine::is_constant_fd_p (state_t state) const return (state == m_constant_fd); } +fd_state_machine::state_t +fd_state_machine::valid_to_unchecked_state (state_t state) const +{ + if (state == m_valid_read_write) + return m_unchecked_read_write; + else if (state == m_valid_write_only) + return m_unchecked_write_only; + else if (state == m_valid_read_only) + return m_unchecked_read_only; + else + gcc_unreachable (); + return NULL; +} + bool fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, const gimple *stmt) const @@ -736,6 +765,11 @@ fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, return true; } // "open" + if (is_named_call_p (callee_fndecl, "creat", call, 2)) + { + on_creat (sm_ctxt, node, stmt, call); + } // "creat" + if (is_named_call_p (callee_fndecl, "close", call, 1)) { on_close (sm_ctxt, node, stmt, call); @@ -754,6 +788,23 @@ fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, return true; } // "read" + if (is_named_call_p (callee_fndecl, "dup", call, 1)) + { + check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_1); + return true; + } + + if (is_named_call_p (callee_fndecl, "dup2", call, 2)) + { + check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_2); + return true; + } + + if (is_named_call_p (callee_fndecl, "dup3", call, 3)) + { + check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_3); + return true; + } { // Handle __attribute__((fd_arg)) @@ -900,6 +951,78 @@ fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node, } void +fd_state_machine::on_creat (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call) const +{ + tree lhs = gimple_call_lhs (call); + if (lhs) + sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked_write_only); + else + sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE)); +} + +void +fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl, enum dup kind) const +{ + tree lhs = gimple_call_lhs (call); + tree arg_1 = gimple_call_arg (call, 0); + state_t state_arg_1 = sm_ctxt->get_state (stmt, arg_1); + if (state_arg_1 == m_stop) + return; + if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1))) + { + check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, + DIRS_READ_WRITE); + if (kind == DUP_1) + return; + } + switch (kind) + { + case DUP_1: + if (lhs) + { + if (is_constant_fd_p (state_arg_1)) + sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write); + else + sm_ctxt->set_next_state (stmt, lhs, + valid_to_unchecked_state (state_arg_1)); + } + break; + + case DUP_2: + case DUP_3: + tree arg_2 = gimple_call_arg (call, 1); + state_t state_arg_2 = sm_ctxt->get_state (stmt, arg_2); + tree diag_arg_2 = sm_ctxt->get_diagnostic_tree (arg_2); + if (state_arg_2 == m_stop) + return; + /* Check if -1 was passed as second argument to dup2. */ + if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2))) + { + sm_ctxt->warn ( + node, stmt, arg_2, + new fd_use_without_check (*this, diag_arg_2, callee_fndecl)); + return; + } + /* dup2 returns value of its second argument on success.But, the + access mode of the returned file descriptor depends on the duplicated + file descriptor i.e the first argument. */ + if (lhs) + { + if (is_constant_fd_p (state_arg_1)) + sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write); + else + sm_ctxt->set_next_state (stmt, lhs, + valid_to_unchecked_state (state_arg_1)); + } + + break; + } +} + +void fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, const gcall *call) const { @@ -964,6 +1087,8 @@ fd_state_machine::check_for_open_fd ( } switch (callee_fndecl_dir) { + case DIRS_READ_WRITE: + break; case DIRS_READ: if (is_writeonly_fd_p (state)) { @@ -984,8 +1109,6 @@ fd_state_machine::check_for_open_fd ( *this, diag_arg, DIRS_READ, callee_fndecl)); } break; - default: - gcc_unreachable (); } } } diff --git a/gcc/btfout.cc b/gcc/btfout.cc index 31af505..997a33f 100644 --- a/gcc/btfout.cc +++ b/gcc/btfout.cc @@ -463,6 +463,7 @@ btf_dtd_emit_preprocess_cb (ctf_container_ref ctfc, ctf_dtdef_ref dtd) ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> (); func_dtd->dtd_data = dtd->dtd_data; func_dtd->dtd_data.ctti_type = dtd->dtd_type; + func_dtd->linkage = dtd->linkage; vec_safe_push (funcs, func_dtd); num_types_created++; @@ -740,7 +741,10 @@ static void btf_asm_func_type (ctf_dtdef_ref dtd) { dw2_asm_output_data (4, dtd->dtd_data.ctti_name, "btt_name"); - dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_FUNC, 0, 0), "btt_info"); + dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_FUNC, 0, + dtd->linkage), + "btt_info: kind=%u, kflag=%u, linkage=%u", + BTF_KIND_FUNC, 0, dtd->linkage); dw2_asm_output_data (4, get_btf_id (dtd->dtd_data.ctti_type), "btt_type"); } @@ -914,6 +918,10 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd) if (dtd->dtd_data.ctti_size < 1) break; + /* In BTF the CHAR `encoding' seems to not be used, so clear it + here. */ + dtd->dtd_u.dtu_enc.cte_format &= ~BTF_INT_CHAR; + encoding = BTF_INT_DATA (dtd->dtd_u.dtu_enc.cte_format, dtd->dtd_u.dtu_enc.cte_offset, dtd->dtd_u.dtu_enc.cte_bits); diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 3aa672b..cffb462 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,10 @@ +2022-08-01 David Malcolm <dmalcolm@redhat.com> + + * c-typeck.cc (build_c_cast): Quote names of address spaces in + diagnostics. + (convert_for_assignment): Add a note to address space mismatch + diagnostics, specifying the expected and actual types. + 2022-07-10 Lewis Hyatt <lhyatt@gmail.com> PR preprocessor/97498 diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index fd0a7f8..8514488 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -6032,18 +6032,18 @@ build_c_cast (location_t loc, tree type, tree expr) if (!addr_space_superset (as_to, as_from, &as_common)) { if (ADDR_SPACE_GENERIC_P (as_from)) - warning_at (loc, 0, "cast to %s address space pointer " + warning_at (loc, 0, "cast to %qs address space pointer " "from disjoint generic address space pointer", c_addr_space_name (as_to)); else if (ADDR_SPACE_GENERIC_P (as_to)) warning_at (loc, 0, "cast to generic address space pointer " - "from disjoint %s address space pointer", + "from disjoint %qs address space pointer", c_addr_space_name (as_from)); else - warning_at (loc, 0, "cast to %s address space pointer " - "from disjoint %s address space pointer", + warning_at (loc, 0, "cast to %qs address space pointer " + "from disjoint %qs address space pointer", c_addr_space_name (as_to), c_addr_space_name (as_from)); } @@ -7252,6 +7252,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, if (!null_pointer_constant_p (rhs) && asr != asl && !targetm.addr_space.subset_p (asr, asl)) { + auto_diagnostic_group d; + bool diagnosed = true; switch (errtype) { case ic_argpass: @@ -7259,7 +7261,8 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, const char msg[] = G_("passing argument %d of %qE from " "pointer to non-enclosed address space"); if (warnopt) - warning_at (expr_loc, warnopt, msg, parmnum, rname); + diagnosed + = warning_at (expr_loc, warnopt, msg, parmnum, rname); else error_at (expr_loc, msg, parmnum, rname); break; @@ -7269,7 +7272,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, const char msg[] = G_("assignment from pointer to " "non-enclosed address space"); if (warnopt) - warning_at (location, warnopt, msg); + diagnosed = warning_at (location, warnopt, msg); else error_at (location, msg); break; @@ -7280,7 +7283,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, const char msg[] = G_("initialization from pointer to " "non-enclosed address space"); if (warnopt) - warning_at (location, warnopt, msg); + diagnosed = warning_at (location, warnopt, msg); else error_at (location, msg); break; @@ -7290,7 +7293,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, const char msg[] = G_("return from pointer to " "non-enclosed address space"); if (warnopt) - warning_at (location, warnopt, msg); + diagnosed = warning_at (location, warnopt, msg); else error_at (location, msg); break; @@ -7298,6 +7301,14 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type, default: gcc_unreachable (); } + if (diagnosed) + { + if (errtype == ic_argpass) + inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype); + else + inform (location, "expected %qT but pointer is of type %qT", + type, rhstype); + } return error_mark_node; } diff --git a/gcc/cfgcleanup.cc b/gcc/cfgcleanup.cc index 18047da..a8b0139 100644 --- a/gcc/cfgcleanup.cc +++ b/gcc/cfgcleanup.cc @@ -208,7 +208,7 @@ mark_effect (rtx exp, regset nonequal) return false; case SET: - if (rtx_equal_for_cselib_p (SET_DEST (exp), SET_SRC (exp))) + if (cselib_redundant_set_p (exp)) return false; dest = SET_DEST (exp); if (dest == pc_rtx) diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc index e4cc4a3..5e3a7ff 100644 --- a/gcc/config/i386/i386-features.cc +++ b/gcc/config/i386/i386-features.cc @@ -1221,6 +1221,13 @@ timode_scalar_chain::compute_convert_gain () igain = COSTS_N_INSNS (1); break; + case ASHIFT: + case LSHIFTRT: + /* For logical shifts by constant multiples of 8. */ + igain = optimize_insn_for_size_p () ? COSTS_N_BYTES (4) + : COSTS_N_INSNS (1); + break; + default: break; } @@ -1353,8 +1360,15 @@ timode_scalar_chain::convert_insn (rtx_insn *insn) if (GET_MODE (dst) == V1TImode) { tmp = find_reg_equal_equiv_note (insn); - if (tmp && GET_MODE (XEXP (tmp, 0)) == TImode) - PUT_MODE (XEXP (tmp, 0), V1TImode); + if (tmp) + { + if (GET_MODE (XEXP (tmp, 0)) == TImode) + PUT_MODE (XEXP (tmp, 0), V1TImode); + else if (CONST_SCALAR_INT_P (XEXP (tmp, 0))) + XEXP (tmp, 0) + = gen_rtx_CONST_VECTOR (V1TImode, + gen_rtvec (1, XEXP (tmp, 0))); + } } break; case MEM: @@ -1462,6 +1476,12 @@ timode_scalar_chain::convert_insn (rtx_insn *insn) src = convert_compare (XEXP (src, 0), XEXP (src, 1), insn); break; + case ASHIFT: + case LSHIFTRT: + convert_op (&XEXP (src, 0), insn); + PUT_MODE (src, V1TImode); + break; + default: gcc_unreachable (); } @@ -1796,6 +1816,14 @@ timode_scalar_to_vector_candidate_p (rtx_insn *insn) case NOT: return REG_P (XEXP (src, 0)) || timode_mem_p (XEXP (src, 0)); + case ASHIFT: + case LSHIFTRT: + /* Handle logical shifts by integer constants between 0 and 120 + that are multiples of 8. */ + return REG_P (XEXP (src, 0)) + && CONST_INT_P (XEXP (src, 1)) + && (INTVAL (XEXP (src, 1)) & ~0x78) == 0; + default: return false; } diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index e03f86d..5e30dc8 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -16070,6 +16070,19 @@ ix86_call_use_plt_p (rtx call_op) return true; } +/* Implement TARGET_IFUNC_REF_LOCAL_OK. If this hook returns true, + the PLT entry will be used as the function address for local IFUNC + functions. When the PIC register is needed for PLT call, indirect + call via the PLT entry will fail since the PIC register may not be + set up properly for indirect call. In this case, we should return + false. */ + +static bool +ix86_ifunc_ref_local_ok (void) +{ + return !flag_pic || (TARGET_64BIT && ix86_cmodel != CM_LARGE_PIC); +} + /* Return true if the function being called was marked with attribute "noplt" or using -fno-plt and we are compiling for non-PIC. We need to handle the non-PIC case in the backend because there is no easy @@ -24953,7 +24966,7 @@ ix86_libgcc_floating_mode_supported_p ix86_get_multilib_abi_name #undef TARGET_IFUNC_REF_LOCAL_OK -#define TARGET_IFUNC_REF_LOCAL_OK hook_bool_void_true +#define TARGET_IFUNC_REF_LOCAL_OK ix86_ifunc_ref_local_ok #if !TARGET_MACHO && !TARGET_DLLIMPORT_DECL_ATTRIBUTES # undef TARGET_ASM_RELOC_RW_MASK diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index f1158e1..fd30c57 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -1510,7 +1510,7 @@ (define_insn_and_split "*cmp<dwi>_doubleword" [(set (reg:CCZ FLAGS_REG) (compare:CCZ (match_operand:<DWI> 0 "nonimmediate_operand") - (match_operand:<DWI> 1 "x86_64_hilo_general_operand")))] + (match_operand:<DWI> 1 "general_operand")))] "ix86_pre_reload_split ()" "#" "&& 1" @@ -1526,6 +1526,15 @@ operands[i] = force_reg (<MODE>mode, operands[i]); operands[4] = gen_reg_rtx (<MODE>mode); + + /* Special case comparisons against -1. */ + if (operands[1] == constm1_rtx && operands[3] == constm1_rtx) + { + emit_insn (gen_and<mode>3 (operands[4], operands[0], operands[2])); + emit_insn (gen_cmp_1 (<MODE>mode, operands[4], constm1_rtx)); + DONE; + } + if (operands[1] == const0_rtx) emit_move_insn (operands[4], operands[0]); else if (operands[0] == const0_rtx) @@ -1535,7 +1544,12 @@ else if (operands[0] == constm1_rtx) emit_insn (gen_one_cmpl<mode>2 (operands[4], operands[1])); else - emit_insn (gen_xor<mode>3 (operands[4], operands[0], operands[1])); + { + if (CONST_SCALAR_INT_P (operands[1]) + && !x86_64_immediate_operand (operands[1], <MODE>mode)) + operands[1] = force_reg (<MODE>mode, operands[1]); + emit_insn (gen_xor<mode>3 (operands[4], operands[0], operands[1])); + } if (operands[3] == const0_rtx) operands[5] = operands[2]; @@ -1549,7 +1563,12 @@ else if (operands[2] == constm1_rtx) emit_insn (gen_one_cmpl<mode>2 (operands[5], operands[3])); else - emit_insn (gen_xor<mode>3 (operands[5], operands[2], operands[3])); + { + if (CONST_SCALAR_INT_P (operands[3]) + && !x86_64_immediate_operand (operands[3], <MODE>mode)) + operands[3] = force_reg (<MODE>mode, operands[3]); + emit_insn (gen_xor<mode>3 (operands[5], operands[2], operands[3])); + } } }) @@ -3018,6 +3037,18 @@ [(parallel [(set (match_dup 1) (match_dup 2)) (set (match_dup 2) (match_dup 1))])]) +;; Convert moves to/from AX_REG into xchg with -Oz. +(define_peephole2 + [(set (match_operand:SWI48 0 "general_reg_operand") + (match_operand:SWI48 1 "general_reg_operand"))] + "optimize_size > 1 + && (REGNO (operands[0]) == AX_REG + || REGNO (operands[1]) == AX_REG) + && optimize_insn_for_size_p () + && peep2_reg_dead_p (1, operands[1])" + [(parallel [(set (match_dup 0) (match_dup 1)) + (set (match_dup 1) (match_dup 0))])]) + (define_expand "movstrict<mode>" [(set (strict_low_part (match_operand:SWI12 0 "register_operand")) (match_operand:SWI12 1 "general_operand"))] diff --git a/gcc/config/riscv/predicates.md b/gcc/config/riscv/predicates.md index 90db5df..e98db2c 100644 --- a/gcc/config/riscv/predicates.md +++ b/gcc/config/riscv/predicates.md @@ -76,7 +76,7 @@ /* Check whether the constant can be loaded in a single instruction with zbs extensions. */ - if (TARGET_64BIT && TARGET_ZBS && SINGLE_BIT_MASK_OPERAND (INTVAL (op))) + if (TARGET_ZBS && SINGLE_BIT_MASK_OPERAND (INTVAL (op))) return false; /* Otherwise check whether the constant can be loaded in a single diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc index 4b727d2..df491be 100644 --- a/gcc/config/rs6000/rs6000.cc +++ b/gcc/config/rs6000/rs6000.cc @@ -4094,17 +4094,6 @@ rs6000_option_override_internal (bool global_init_p) rs6000_isa_flags &= ~OPTION_MASK_BLOCK_OPS_UNALIGNED_VSX; } - if (!(rs6000_isa_flags_explicit & OPTION_MASK_BLOCK_OPS_VECTOR_PAIR)) - { - /* Do not generate lxvp and stxvp on power10 since there are some - performance issues. */ - if (TARGET_MMA && TARGET_EFFICIENT_UNALIGNED_VSX - && rs6000_tune != PROCESSOR_POWER10) - rs6000_isa_flags |= OPTION_MASK_BLOCK_OPS_VECTOR_PAIR; - else - rs6000_isa_flags &= ~OPTION_MASK_BLOCK_OPS_VECTOR_PAIR; - } - /* Use long double size to select the appropriate long double. We use TYPE_PRECISION to differentiate the 3 different long double types. We map 128 into the precision used for TFmode. */ diff --git a/gcc/config/s390/vector.md b/gcc/config/s390/vector.md index a6c4b4e..6247298 100644 --- a/gcc/config/s390/vector.md +++ b/gcc/config/s390/vector.md @@ -63,6 +63,12 @@ V1DF V2DF (V1TF "TARGET_VXE") (TF "TARGET_VXE")]) +; All modes present in V_HW and VFT. +(define_mode_iterator V_HW_FT [V16QI V8HI V4SI V2DI (V1TI "TARGET_VXE") V1DF + V2DF (V1SF "TARGET_VXE") (V2SF "TARGET_VXE") + (V4SF "TARGET_VXE") (V1TF "TARGET_VXE") + (TF "TARGET_VXE")]) + ; FP vector modes directly supported by the HW. This does not include ; vector modes using only part of a vector register and should be used ; for instructions which might trigger IEEE exceptions. diff --git a/gcc/config/s390/vx-builtins.md b/gcc/config/s390/vx-builtins.md index d513079..98ee08b 100644 --- a/gcc/config/s390/vx-builtins.md +++ b/gcc/config/s390/vx-builtins.md @@ -517,12 +517,12 @@ ; swapped in s390-c.cc when we get here. (define_insn "vsel<mode>" - [(set (match_operand:V_HW 0 "register_operand" "=v") - (ior:V_HW - (and:V_HW (match_operand:V_HW 1 "register_operand" "v") - (match_operand:V_HW 3 "register_operand" "v")) - (and:V_HW (not:V_HW (match_dup 3)) - (match_operand:V_HW 2 "register_operand" "v"))))] + [(set (match_operand:V_HW_FT 0 "register_operand" "=v") + (ior:V_HW_FT + (and:V_HW_FT (match_operand:V_HW_FT 1 "register_operand" "v") + (match_operand:V_HW_FT 3 "register_operand" "v")) + (and:V_HW_FT (not:V_HW_FT (match_dup 3)) + (match_operand:V_HW_FT 2 "register_operand" "v"))))] "TARGET_VX" "vsel\t%v0,%1,%2,%3" [(set_attr "op_type" "VRR")]) diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 4d7761c2..ed83b97 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2022-08-07 Jakub Jelinek <jakub@redhat.com> + + PR c++/88174 + * constexpr.cc (cxx_eval_store_expression): Handle REALPART_EXPR + and IMAGPART_EXPR. Change ctors from releasing_vec to + auto_vec<tree *>, adjust all uses. For !preeval, update ctors + vector. + 2022-07-31 Lewis Hyatt <lhyatt@gmail.com> PR c++/66290 diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 5e0d339..c047fe4 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -5732,6 +5732,20 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, } break; + case REALPART_EXPR: + gcc_assert (probe == target); + vec_safe_push (refs, probe); + vec_safe_push (refs, TREE_TYPE (probe)); + probe = TREE_OPERAND (probe, 0); + break; + + case IMAGPART_EXPR: + gcc_assert (probe == target); + vec_safe_push (refs, probe); + vec_safe_push (refs, TREE_TYPE (probe)); + probe = TREE_OPERAND (probe, 0); + break; + default: if (evaluated) object = probe; @@ -5770,7 +5784,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, type = TREE_TYPE (object); bool no_zero_init = true; - releasing_vec ctors, indexes; + auto_vec<tree *> ctors; + releasing_vec indexes; auto_vec<int> index_pos_hints; bool activated_union_member_p = false; bool empty_base = false; @@ -5810,14 +5825,36 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, *valp = ary_ctor; } - /* If the value of object is already zero-initialized, any new ctors for - subobjects will also be zero-initialized. */ - no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp); - enum tree_code code = TREE_CODE (type); tree reftype = refs->pop(); tree index = refs->pop(); + if (code == COMPLEX_TYPE) + { + if (TREE_CODE (*valp) == COMPLEX_CST) + *valp = build2 (COMPLEX_EXPR, type, TREE_REALPART (*valp), + TREE_IMAGPART (*valp)); + else if (TREE_CODE (*valp) == CONSTRUCTOR + && CONSTRUCTOR_NELTS (*valp) == 0 + && CONSTRUCTOR_NO_CLEARING (*valp)) + { + tree r = build_constructor (reftype, NULL); + CONSTRUCTOR_NO_CLEARING (r) = 1; + *valp = build2 (COMPLEX_EXPR, type, r, r); + } + gcc_assert (TREE_CODE (*valp) == COMPLEX_EXPR); + ctors.safe_push (valp); + vec_safe_push (indexes, index); + valp = &TREE_OPERAND (*valp, TREE_CODE (index) == IMAGPART_EXPR); + gcc_checking_assert (refs->is_empty ()); + type = reftype; + break; + } + + /* If the value of object is already zero-initialized, any new ctors for + subobjects will also be zero-initialized. */ + no_zero_init = CONSTRUCTOR_NO_CLEARING (*valp); + if (code == RECORD_TYPE && is_empty_field (index)) /* Don't build a sub-CONSTRUCTOR for an empty base or field, as they have no data and might have an offset lower than previously declared @@ -5860,7 +5897,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, no_zero_init = true; } - vec_safe_push (ctors, *valp); + ctors.safe_push (valp); vec_safe_push (indexes, index); constructor_elt *cep @@ -5922,11 +5959,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, semantics are not applied on an object under construction. They come into effect when the constructor for the most derived object ends." */ - for (tree elt : *ctors) + for (tree *elt : ctors) if (same_type_ignoring_top_level_qualifiers_p - (TREE_TYPE (const_object_being_modified), TREE_TYPE (elt))) + (TREE_TYPE (const_object_being_modified), TREE_TYPE (*elt))) { - fail = TREE_READONLY (elt); + fail = TREE_READONLY (*elt); break; } } @@ -5967,6 +6004,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, valp = ctx->global->values.get (object); for (unsigned i = 0; i < vec_safe_length (indexes); i++) { + ctors[i] = valp; constructor_elt *cep = get_or_insert_ctor_field (*valp, indexes[i], index_pos_hints[i]); valp = &cep->value; @@ -6029,17 +6067,45 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, CONSTRUCTORs, if any. */ bool c = TREE_CONSTANT (init); bool s = TREE_SIDE_EFFECTS (init); + if (!indexes->is_empty ()) + { + tree last = indexes->last (); + if (TREE_CODE (last) == REALPART_EXPR + || TREE_CODE (last) == IMAGPART_EXPR) + { + /* And canonicalize COMPLEX_EXPR into COMPLEX_CST if + possible. */ + tree *cexpr = ctors.last (); + if (tree c = const_binop (COMPLEX_EXPR, TREE_TYPE (*cexpr), + TREE_OPERAND (*cexpr, 0), + TREE_OPERAND (*cexpr, 1))) + *cexpr = c; + else + { + TREE_CONSTANT (*cexpr) + = (TREE_CONSTANT (TREE_OPERAND (*cexpr, 0)) + & TREE_CONSTANT (TREE_OPERAND (*cexpr, 1))); + TREE_SIDE_EFFECTS (*cexpr) + = (TREE_SIDE_EFFECTS (TREE_OPERAND (*cexpr, 0)) + | TREE_SIDE_EFFECTS (TREE_OPERAND (*cexpr, 1))); + } + c = TREE_CONSTANT (*cexpr); + s = TREE_SIDE_EFFECTS (*cexpr); + } + } if (!c || s || activated_union_member_p) - for (tree elt : *ctors) + for (tree *elt : ctors) { + if (TREE_CODE (*elt) != CONSTRUCTOR) + continue; if (!c) - TREE_CONSTANT (elt) = false; + TREE_CONSTANT (*elt) = false; if (s) - TREE_SIDE_EFFECTS (elt) = true; + TREE_SIDE_EFFECTS (*elt) = true; /* Clear CONSTRUCTOR_NO_CLEARING since we've activated a member of this union. */ - if (TREE_CODE (TREE_TYPE (elt)) == UNION_TYPE) - CONSTRUCTOR_NO_CLEARING (elt) = false; + if (TREE_CODE (TREE_TYPE (*elt)) == UNION_TYPE) + CONSTRUCTOR_NO_CLEARING (*elt) = false; } if (lval) diff --git a/gcc/cselib.cc b/gcc/cselib.cc index 6769bee..6a56097 100644 --- a/gcc/cselib.cc +++ b/gcc/cselib.cc @@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see #include "dumpfile.h" #include "cselib.h" #include "function-abi.h" +#include "alias.h" /* A list of cselib_val structures. */ struct elt_list @@ -1157,6 +1158,75 @@ rtx_equal_for_cselib_1 (rtx x, rtx y, machine_mode memmode, int depth) return 1; } +/* Wrapper for rtx_equal_for_cselib_p to determine whether a SET is + truly redundant, taking into account aliasing information. */ +bool +cselib_redundant_set_p (rtx set) +{ + gcc_assert (GET_CODE (set) == SET); + rtx dest = SET_DEST (set); + if (cselib_reg_set_mode (dest) != GET_MODE (dest)) + return false; + + if (!rtx_equal_for_cselib_p (dest, SET_SRC (set))) + return false; + + while (GET_CODE (dest) == SUBREG + || GET_CODE (dest) == ZERO_EXTRACT + || GET_CODE (dest) == STRICT_LOW_PART) + dest = XEXP (dest, 0); + + if (!flag_strict_aliasing || !MEM_P (dest)) + return true; + + /* For a store we need to check that suppressing it will not change + the effective alias set. */ + rtx dest_addr = XEXP (dest, 0); + + /* Lookup the equivalents to the original dest (rather than just the + MEM). */ + cselib_val *src_val = cselib_lookup (SET_DEST (set), + GET_MODE (SET_DEST (set)), + 0, VOIDmode); + + if (src_val) + { + /* Walk the list of source equivalents to find the MEM accessing + the same location. */ + for (elt_loc_list *l = src_val->locs; l; l = l->next) + { + rtx src_equiv = l->loc; + while (GET_CODE (src_equiv) == SUBREG + || GET_CODE (src_equiv) == ZERO_EXTRACT + || GET_CODE (src_equiv) == STRICT_LOW_PART) + src_equiv = XEXP (src_equiv, 0); + + if (MEM_P (src_equiv)) + { + /* Match the MEMs by comparing the addresses. We can + only remove the later store if the earlier aliases at + least all the accesses of the later one. */ + if (rtx_equal_for_cselib_1 (dest_addr, XEXP (src_equiv, 0), + GET_MODE (dest), 0)) + return mems_same_for_tbaa_p (src_equiv, dest); + } + } + } + + /* We failed to find a recorded value in the cselib history, so try + the source of this set; this catches cases such as *p = *q when p + and q have the same value. */ + rtx src = SET_SRC (set); + while (GET_CODE (src) == SUBREG) + src = XEXP (src, 0); + + if (MEM_P (src) + && rtx_equal_for_cselib_1 (dest_addr, XEXP (src, 0), GET_MODE (dest), 0)) + return mems_same_for_tbaa_p (src, dest); + + return false; +} + /* Helper function for cselib_hash_rtx. Arguments like for cselib_hash_rtx, except that it hashes (plus:P x c). */ diff --git a/gcc/cselib.h b/gcc/cselib.h index 9ae65e6..b090505 100644 --- a/gcc/cselib.h +++ b/gcc/cselib.h @@ -83,6 +83,7 @@ extern void cselib_process_insn (rtx_insn *); extern bool fp_setter_insn (rtx_insn *); extern machine_mode cselib_reg_set_mode (const_rtx); extern int rtx_equal_for_cselib_1 (rtx, rtx, machine_mode, int); +extern bool cselib_redundant_set_p (rtx); extern int references_value_p (const_rtx, int); extern rtx cselib_expand_value_rtx (rtx, bitmap, int); typedef rtx (*cselib_expand_callback)(rtx, bitmap, int, void *); diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc index f24e7bf..9773358 100644 --- a/gcc/ctfc.cc +++ b/gcc/ctfc.cc @@ -777,7 +777,7 @@ ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func, ctf_id_t ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name, const ctf_funcinfo_t * ctc, dw_die_ref die, - bool from_global_func) + bool from_global_func, int linkage) { ctf_dtdef_ref dtd; ctf_id_t type; @@ -791,6 +791,7 @@ ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name, type = ctf_add_generic (ctfc, flag, name, &dtd, die); dtd->from_global_func = from_global_func; + dtd->linkage = linkage; dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FUNCTION, flag, vlen); /* Caller must make sure CTF types for ctc->ctc_return are already added. */ dtd->dtd_data.ctti_type = (uint32_t) ctc->ctc_return; @@ -161,6 +161,7 @@ struct GTY ((for_user)) ctf_dtdef ctf_itype_t dtd_data; /* Type node. */ bool from_global_func; /* Whether this type was added from a global function. */ + uint32_t linkage; /* Used in function types. 0=local, 1=global. */ union GTY ((desc ("ctf_dtu_d_union_selector (&%1)"))) { /* struct, union, or enum. */ @@ -423,7 +424,7 @@ extern ctf_id_t ctf_add_forward (ctf_container_ref, uint32_t, const char *, extern ctf_id_t ctf_add_typedef (ctf_container_ref, uint32_t, const char *, ctf_id_t, dw_die_ref); extern ctf_id_t ctf_add_function (ctf_container_ref, uint32_t, const char *, - const ctf_funcinfo_t *, dw_die_ref, bool); + const ctf_funcinfo_t *, dw_die_ref, bool, int); extern ctf_id_t ctf_add_sou (ctf_container_ref, uint32_t, const char *, uint32_t, size_t, dw_die_ref); diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog index f1d1f00..41e2809 100644 --- a/gcc/d/ChangeLog +++ b/gcc/d/ChangeLog @@ -1,3 +1,18 @@ +2022-08-03 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd d7772a2369. + * dmd/VERSION: Bump version to v2.100.1. + * d-codegen.cc (get_frameinfo): Check whether decision to generate + closure changed since semantic finished. + * d-lang.cc (d_handle_option): Remove handling of -fdebug=level and + -fversion=level. + * decl.cc (DeclVisitor::visit (VarDeclaration *)): Generate evaluation + of noreturn variable initializers before throw. + * expr.cc (ExprVisitor::visit (AssignExp *)): Don't generate + assignment for noreturn types, only evaluate for side effects. + * lang.opt (fdebug=): Undocument -fdebug=level. + (fversion=): Undocument -fversion=level. + 2022-07-06 Iain Buclaw <ibuclaw@gdcproject.org> * dmd/MERGE: Merge upstream dmd 56589f0f4. diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index 2d90899..3fd4bee 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -2826,8 +2826,15 @@ get_frameinfo (FuncDeclaration *fd) DECL_LANG_FRAMEINFO (fds) = ffi; + const bool requiresClosure = fd->requiresClosure; if (fd->needsClosure ()) { + /* This can shift due to templates being expanded that access alias + symbols, give it a decent error for now. */ + if (requiresClosure != fd->requiresClosure + && (fd->nrvo_var || global.params.betterC)) + fd->checkClosure (); + /* Set-up a closure frame, this will be allocated on the heap. */ FRAMEINFO_CREATES_FRAME (ffi) = 1; FRAMEINFO_IS_CLOSURE (ffi) = 1; diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 6e4350f..04147ed 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -456,16 +456,6 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, break; case OPT_fdebug_: - if (ISDIGIT (arg[0])) - { - int level = integral_argument (arg); - if (level != -1) - { - global.params.debuglevel = level; - break; - } - } - if (Identifier::isValidIdentifier (CONST_CAST (char *, arg))) { if (!global.params.debugids) @@ -713,16 +703,6 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, break; case OPT_fversion_: - if (ISDIGIT (arg[0])) - { - int level = integral_argument (arg); - if (level != -1) - { - global.params.versionlevel = level; - break; - } - } - if (Identifier::isValidIdentifier (CONST_CAST (char *, arg))) { if (!global.params.versionids) diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 3caa465..58cea4d 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -646,9 +646,12 @@ public: if (!d->isDataseg () && !d->isMember () && d->_init && !d->_init->isVoidInitializer ()) { + /* Evaluate RHS for side effects first. */ + Expression *ie = initializerToExpression (d->_init); + add_stmt (build_expr (ie)); + Expression *e = d->type->defaultInitLiteral (d->loc); - tree exp = build_expr (e); - add_stmt (exp); + add_stmt (build_expr (e)); } return; diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index 8324c1c..c358b69 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -56589f0f4d724c1c8022c57509a243f16a04228a +d7772a236983ec37b92d21b28bad3cd2de57b945 The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index 5ea2ba0..83a14f5 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.100.0 +v2.100.1 diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index 16cbe62..272e751 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -111,8 +111,8 @@ bool checkNonAssignmentArrayOp(Expression e, bool suggestion = false) * evaluation order as the actual array operations have no * side-effect. * References: - * https://github.com/dlang/druntime/blob/master/src/object.d#L3944 - * https://github.com/dlang/druntime/blob/master/src/core/internal/array/operations.d + * https://github.com/dlang/dmd/blob/cdfadf8a18f474e6a1b8352af2541efe3e3467cc/druntime/src/object.d#L4694 + * https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/array/operations.d */ Expression arrayOp(BinExp e, Scope* sc) { diff --git a/gcc/d/dmd/chkformat.d b/gcc/d/dmd/chkformat.d index a3f3bc4..e118d70 100644 --- a/gcc/d/dmd/chkformat.d +++ b/gcc/d/dmd/chkformat.d @@ -62,7 +62,7 @@ import dmd.target; bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list) { //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr); - size_t n, gnu_m_count; // index in args / number of Format.GNU_m + size_t n; // index in args for (size_t i = 0; i < format.length;) { if (format[i] != '%') @@ -79,6 +79,8 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre if (fmt == Format.percent) continue; // "%%", no arguments + if (fmt == Format.GNU_m) + continue; // "%m", no arguments if (isVa_list) { @@ -88,14 +90,11 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre continue; } - if (fmt == Format.GNU_m) - ++gnu_m_count; - Expression getNextArg(ref bool skip) { if (n == args.length) { - if (args.length < (n + 1) - gnu_m_count) + if (args.length < (n + 1)) deprecation(loc, "more format specifiers than %d arguments", cast(int)n); else skip = true; @@ -207,7 +206,6 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre errorMsg(null, e, "ptrdiff_t", t); break; - case Format.GNU_a: // Format.GNU_a is only for scanf case Format.lg: case Format.g: // double if (t.ty != Tfloat64 && t.ty != Timaginary64) @@ -289,8 +287,8 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.GNU_m: - break; // not assert(0) because it may go through it if there are extra arguments - + case Format.POSIX_ms: + case Format.POSIX_mls: case Format.percent: assert(0); } @@ -481,8 +479,6 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres errorMsg(null, e, "real*", t); break; - case Format.GNU_a: - case Format.GNU_m: case Format.c: case Format.s: // pointer to char string if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint8 || tnext.ty == Tuns8))) @@ -500,10 +496,23 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres errorMsg(null, e, "void**", t); break; + case Format.POSIX_ms: // pointer to pointer to char string + Type tnext2 = tnext ? tnext.nextOf() : null; + if (!(t.ty == Tpointer && tnext.ty == Tpointer && (tnext2.ty == Tchar || tnext2.ty == Tint8 || tnext2.ty == Tuns8))) + errorMsg(null, e, "char**", t); + break; + + case Format.POSIX_mls: // pointer to pointer to wchar_t string + Type tnext2 = tnext ? tnext.nextOf() : null; + if (!(t.ty == Tpointer && tnext.ty == Tpointer && tnext2.ty.isSomeChar && tnext2.size() == target.c.wchar_tsize)) + errorMsg(null, e, "wchar_t**", t); + break; + case Format.error: deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast(int)slice.length, slice.ptr); break; + case Format.GNU_m: case Format.percent: assert(0); } @@ -567,35 +576,97 @@ Format parseScanfFormatSpecifier(scope const char[] format, ref size_t idx, return error(); } - /* Read the scanset - * A scanset can be anything, so we just check that it is paired + /* Read the specifier */ - if (format[i] == '[') + Format specifier; + Modifier flags = Modifier.none; + switch (format[i]) { - while (i < length) - { - if (format[i] == ']') - break; + case 'm': + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/scanf.html + // POSIX.1-2017 C Extension (CX) + flags = Modifier.m; ++i; - } + if (i == length) + return error(); + if (format[i] == 'l') + { + ++i; + if (i == length) + return error(); + flags = Modifier.ml; + } - // no `]` found - if (i == length) - return error(); + // Check valid conversion types for %m. + if (format[i] == 'c' || format[i] == 's') + specifier = flags == Modifier.ml ? Format.POSIX_mls : + Format.POSIX_ms; + else if (format[i] == 'C' || format[i] == 'S') + specifier = flags == Modifier.m ? Format.POSIX_mls : + Format.error; + else if (format[i] == '[') + goto case '['; + else + specifier = Format.error; + ++i; + break; - ++i; - // no specifier after `]` - // it could be mixed with the one above, but then idx won't have the right index - if (i == length) - return error(); - } + case 'l': + // Look for wchar_t scanset %l[..] + immutable j = i + 1; + if (j < length && format[j] == '[') + { + i = j; + flags = Modifier.l; + goto case '['; + } + goto default; - /* Read the specifier - */ - char genSpec; - Format specifier = parseGenericFormatSpecifier(format, i, genSpec); - if (specifier == Format.error) - return error(); + case '[': + // Read the scanset + i++; + if (i == length) + return error(); + // If the conversion specifier begins with `[]` or `[^]`, the right + // bracket character is not the terminator, but in the scanlist. + if (format[i] == '^') + { + i++; + if (i == length) + return error(); + } + if (format[i] == ']') + { + i++; + if (i == length) + return error(); + } + // A scanset can be anything, so we just check that it is paired + while (i < length) + { + if (format[i] == ']') + break; + ++i; + } + // no `]` found + if (i == length) + return error(); + + specifier = flags == Modifier.none ? Format.s : + flags == Modifier.l ? Format.ls : + flags == Modifier.m ? Format.POSIX_ms : + flags == Modifier.ml ? Format.POSIX_mls : + Format.error; + ++i; + break; + + default: + char genSpec; + specifier = parseGenericFormatSpecifier(format, i, genSpec); + if (specifier == Format.error) + return error(); + break; + } idx = i; return specifier; // success @@ -613,11 +684,13 @@ Format parseScanfFormatSpecifier(scope const char[] format, ref size_t idx, * even if `Format.error` is returned * widthStar = set if * for width * precisionStar = set if * for precision + * useGNUExts = true if parsing GNU format extensions * Returns: * Format */ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx, - out bool widthStar, out bool precisionStar) nothrow pure @safe + out bool widthStar, out bool precisionStar, bool useGNUExts = + findCondition(global.versionids, Identifier.idPool("CRuntime_Glibc"))) nothrow pure @safe { auto i = idx; assert(format[i] == '%'); @@ -730,14 +803,33 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx, /* Read the specifier */ char genSpec; - Format specifier = parseGenericFormatSpecifier(format, i, genSpec); - if (specifier == Format.error) - return error(); + Format specifier; + switch (format[i]) + { + case 'm': + // https://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html + if (useGNUExts) + { + specifier = Format.GNU_m; + genSpec = format[i]; + ++i; + break; + } + goto default; + + default: + specifier = parseGenericFormatSpecifier(format, i, genSpec); + if (specifier == Format.error) + return error(); + break; + } switch (genSpec) { case 'c': case 's': + case 'C': + case 'S': if (hash || zero) return error(); break; @@ -748,6 +840,11 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx, return error(); break; + case 'm': + if (hash || zero || flags) + return error(); + break; + case 'n': if (hash || zero || precision || width || flags) return error(); @@ -761,6 +858,22 @@ Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx, return specifier; // success } +/* Different kinds of conversion modifiers. */ +enum Modifier +{ + none, + h, // short + hh, // char + j, // intmax_t + l, // wint_t/wchar_t + ll, // long long int + L, // long double + m, // char** + ml, // wchar_t** + t, // ptrdiff_t + z // size_t +} + /* Different kinds of formatting specifications, variations we don't care about are merged. (Like we don't care about the difference between f, e, g, a, etc.) @@ -799,8 +912,9 @@ enum Format jn, // pointer to intmax_t zn, // pointer to size_t tn, // pointer to ptrdiff_t - GNU_a, // GNU ext. : address to a string with no maximum size (scanf) - GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) / length modifier (scanf) + GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) + POSIX_ms, // POSIX ext. : dynamically allocated char string (scanf) + POSIX_mls, // POSIX ext. : dynamically allocated wchar_t string (scanf) percent, // %% (i.e. no argument) error, // invalid format specification } @@ -820,38 +934,48 @@ enum Format * Format */ Format parseGenericFormatSpecifier(scope const char[] format, - ref size_t idx, out char genSpecifier, bool useGNUExts = - findCondition(global.versionids, Identifier.idPool("CRuntime_Glibc"))) nothrow pure @trusted + ref size_t idx, out char genSpecifier) nothrow pure @safe { const length = format.length; /* Read the `length modifier` */ const lm = format[idx]; - bool lm1; // if jztL - bool lm2; // if `hh` or `ll` - if (lm == 'j' || - lm == 'z' || - lm == 't' || - lm == 'L') + Modifier flags; + switch (lm) { - ++idx; - if (idx == length) - return Format.error; - lm1 = true; - } - else if (lm == 'h' || lm == 'l') - { - ++idx; - if (idx == length) - return Format.error; - lm2 = lm == format[idx]; - if (lm2) - { + case 'j': + case 'z': + case 't': + case 'L': + flags = lm == 'j' ? Modifier.j : + lm == 'z' ? Modifier.z : + lm == 't' ? Modifier.t : + Modifier.L; ++idx; if (idx == length) return Format.error; - } + break; + + case 'h': + case 'l': + ++idx; + if (idx == length) + return Format.error; + if (lm == format[idx]) + { + flags = lm == 'h' ? Modifier.hh : Modifier.ll; + ++idx; + if (idx == length) + return Format.error; + } + else + flags = lm == 'h' ? Modifier.h : Modifier.l; + break; + + default: + flags = Modifier.none; + break; } /* Read the `specifier` @@ -863,103 +987,88 @@ Format parseGenericFormatSpecifier(scope const char[] format, { case 'd': case 'i': - if (lm == 'L') - specifier = Format.error; - else - specifier = lm == 'h' && lm2 ? Format.hhd : - lm == 'h' ? Format.hd : - lm == 'l' && lm2 ? Format.lld : - lm == 'l' ? Format.ld : - lm == 'j' ? Format.jd : - lm == 'z' ? Format.zd : - lm == 't' ? Format.td : - Format.d; + specifier = flags == Modifier.none ? Format.d : + flags == Modifier.hh ? Format.hhd : + flags == Modifier.h ? Format.hd : + flags == Modifier.ll ? Format.lld : + flags == Modifier.l ? Format.ld : + flags == Modifier.j ? Format.jd : + flags == Modifier.z ? Format.zd : + flags == Modifier.t ? Format.td : + Format.error; break; case 'u': case 'o': case 'x': case 'X': - if (lm == 'L') - specifier = Format.error; - else - specifier = lm == 'h' && lm2 ? Format.hhu : - lm == 'h' ? Format.hu : - lm == 'l' && lm2 ? Format.llu : - lm == 'l' ? Format.lu : - lm == 'j' ? Format.ju : - lm == 'z' ? Format.zd : - lm == 't' ? Format.td : - Format.u; + specifier = flags == Modifier.none ? Format.u : + flags == Modifier.hh ? Format.hhu : + flags == Modifier.h ? Format.hu : + flags == Modifier.ll ? Format.llu : + flags == Modifier.l ? Format.lu : + flags == Modifier.j ? Format.ju : + flags == Modifier.z ? Format.zd : + flags == Modifier.t ? Format.td : + Format.error; break; - case 'a': - if (useGNUExts) - { - // https://www.gnu.org/software/libc/manual/html_node/Dynamic-String-Input.html - specifier = Format.GNU_a; - break; - } - goto case; - case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': - if (lm == 'L') - specifier = Format.Lg; - else if (lm1 || lm2 || lm == 'h') - specifier = Format.error; - else - specifier = lm == 'l' ? Format.lg : Format.g; + specifier = flags == Modifier.none ? Format.g : + flags == Modifier.L ? Format.Lg : + flags == Modifier.l ? Format.lg : + Format.error; break; case 'c': - if (lm1 || lm2 || lm == 'h') - specifier = Format.error; - else - specifier = lm == 'l' ? Format.lc : Format.c; + specifier = flags == Modifier.none ? Format.c : + flags == Modifier.l ? Format.lc : + Format.error; break; case 's': - if (lm1 || lm2 || lm == 'h') - specifier = Format.error; - else - specifier = lm == 'l' ? Format.ls : Format.s; + specifier = flags == Modifier.none ? Format.s : + flags == Modifier.l ? Format.ls : + Format.error; break; case 'p': - if (lm1 || lm2 || lm == 'h' || lm == 'l') - specifier = Format.error; - else - specifier = Format.p; + specifier = flags == Modifier.none ? Format.p : + Format.error; break; case 'n': - if (lm == 'L') - specifier = Format.error; - else - specifier = lm == 'l' && lm2 ? Format.lln : - lm == 'l' ? Format.ln : - lm == 'h' && lm2 ? Format.hhn : - lm == 'h' ? Format.hn : - lm == 'j' ? Format.jn : - lm == 'z' ? Format.zn : - lm == 't' ? Format.tn : - Format.n; + specifier = flags == Modifier.none ? Format.n : + flags == Modifier.ll ? Format.lln : + flags == Modifier.l ? Format.ln : + flags == Modifier.hh ? Format.hhn : + flags == Modifier.h ? Format.hn : + flags == Modifier.j ? Format.jn : + flags == Modifier.z ? Format.zn : + flags == Modifier.t ? Format.tn : + Format.error; break; - case 'm': - if (useGNUExts) - { - // https://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html - specifier = Format.GNU_m; - break; - } - goto default; + case 'C': + // POSIX.1-2017 X/Open System Interfaces (XSI) + // %C format is equivalent to %lc + specifier = flags == Modifier.none ? Format.lc : + Format.error; + break; + + case 'S': + // POSIX.1-2017 X/Open System Interfaces (XSI) + // %S format is equivalent to %ls + specifier = flags == Modifier.none ? Format.ls : + Format.error; + break; default: specifier = Format.error; @@ -1126,11 +1235,14 @@ unittest assert(idx == 2); idx = 0; - Format g = parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar); - assert(g == Format.g || g == Format.GNU_a); + assert(parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar) == Format.g); assert(idx == 2); idx = 0; + assert(parsePrintfFormatSpecifier("%La", idx, widthStar, precisionStar) == Format.Lg); + assert(idx == 3); + + idx = 0; assert(parsePrintfFormatSpecifier("%A", idx, widthStar, precisionStar) == Format.g); assert(idx == 2); @@ -1296,8 +1408,7 @@ unittest assert(idx == 2); idx = 0; - g = parseScanfFormatSpecifier("%a", idx, asterisk); - assert(g == Format.g || g == Format.GNU_a); + assert(parseScanfFormatSpecifier("%a", idx, asterisk) == Format.g); assert(idx == 2); idx = 0; @@ -1322,15 +1433,25 @@ unittest // scansets idx = 0; - assert(parseScanfFormatSpecifier("%[a-zA-Z]s", idx, asterisk) == Format.s); - assert(idx == 10); + assert(parseScanfFormatSpecifier("%[a-zA-Z]", idx, asterisk) == Format.s); + assert(idx == 9); assert(!asterisk); idx = 0; - assert(parseScanfFormatSpecifier("%*25[a-z]hhd", idx, asterisk) == Format.hhd); - assert(idx == 12); + assert(parseScanfFormatSpecifier("%*25l[a-z]", idx, asterisk) == Format.ls); + assert(idx == 10); assert(asterisk); + idx = 0; + assert(parseScanfFormatSpecifier("%[]]", idx, asterisk) == Format.s); + assert(idx == 4); + assert(!asterisk); + + idx = 0; + assert(parseScanfFormatSpecifier("%[^]]", idx, asterisk) == Format.s); + assert(idx == 5); + assert(!asterisk); + // Too short formats foreach (s; ["%", "% ", "%#", "%0", "%*", "%1", "%19", "%j", "%z", "%t", "%l", "%h", "%ll", "%hh", "%K"]) @@ -1354,11 +1475,108 @@ unittest } // Invalid scansets - foreach (s; ["%[]", "%[s", "%[0-9lld", "%[", "%[a-z]"]) + foreach (s; ["%[]", "%[^", "%[^]", "%[s", "%[0-9lld", "%[", "%l[^]"]) + { + idx = 0; + assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error); + assert(idx == s.length); + } + + // Posix extensions + foreach (s; ["%jm", "%zm", "%tm", "%Lm", "%hm", "%hhm", "%lm", "%llm", + "%m", "%ma", "%md", "%ml", "%mm", "%mlb", "%mlj", "%mlr", "%mlz", + "%LC", "%lC", "%llC", "%jC", "%tC", "%hC", "%hhC", "%zC", + "%LS", "%lS", "%llS", "%jS", "%tS", "%hS", "%hhS", "%zS"]) { idx = 0; assert(parseScanfFormatSpecifier(s, idx, asterisk) == Format.error); assert(idx == s.length); } + idx = 0; + assert(parseScanfFormatSpecifier("%mc", idx, asterisk) == Format.POSIX_ms); + assert(idx == 3); + + idx = 0; + assert(parseScanfFormatSpecifier("%ms", idx, asterisk) == Format.POSIX_ms); + assert(idx == 3); + + idx = 0; + assert(parseScanfFormatSpecifier("%m[0-9]", idx, asterisk) == Format.POSIX_ms); + assert(idx == 7); + + idx = 0; + assert(parseScanfFormatSpecifier("%mlc", idx, asterisk) == Format.POSIX_mls); + assert(idx == 4); + + idx = 0; + assert(parseScanfFormatSpecifier("%mls", idx, asterisk) == Format.POSIX_mls); + assert(idx == 4); + + idx = 0; + assert(parseScanfFormatSpecifier("%ml[^0-9]", idx, asterisk) == Format.POSIX_mls); + assert(idx == 9); + + idx = 0; + assert(parseScanfFormatSpecifier("%mC", idx, asterisk) == Format.POSIX_mls); + assert(idx == 3); + + idx = 0; + assert(parseScanfFormatSpecifier("%mS", idx, asterisk) == Format.POSIX_mls); + assert(idx == 3); + + idx = 0; + assert(parsePrintfFormatSpecifier("%C", idx, widthStar, precisionStar) == Format.lc); + assert(idx == 2); + + idx = 0; + assert(parseScanfFormatSpecifier("%C", idx, asterisk) == Format.lc); + assert(idx == 2); + + idx = 0; + assert(parsePrintfFormatSpecifier("%S", idx, widthStar, precisionStar) == Format.ls); + assert(idx == 2); + + idx = 0; + assert(parseScanfFormatSpecifier("%S", idx, asterisk) == Format.ls); + assert(idx == 2); + + // GNU extensions: explicitly toggle ISO/GNU flag. + // ISO printf() + bool useGNUExts = false; + { + foreach (s; ["%jm", "%zm", "%tm", "%Lm", "%hm", "%hhm", "%lm", "%llm", + "%#m", "%+m", "%-m", "% m", "%0m"]) + { + idx = 0; + assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.error); + assert(idx == s.length); + } + foreach (s; ["%m", "%md", "%mz", "%mc", "%mm", "%msyz", "%ml", "%mlz", "%mlc", "%mlm"]) + { + idx = 0; + assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.error); + assert(idx == 2); + } + } + + // GNU printf() + useGNUExts = true; + { + foreach (s; ["%jm", "%zm", "%tm", "%Lm", "%hm", "%hhm", "%lm", "%llm", + "%#m", "%+m", "%-m", "% m", "%0m"]) + { + idx = 0; + assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.error); + assert(idx == s.length); + } + + // valid cases, all parsed as `%m` + foreach (s; ["%m", "%md", "%mz", "%mc", "%mm", "%msyz", "%ml", "%mlz", "%mlc", "%mlm"]) + { + idx = 0; + assert(parsePrintfFormatSpecifier(s, idx, widthStar, precisionStar, useGNUExts) == Format.GNU_m); + assert(idx == 2); + } + } } diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index cf4ccbb..1a26eaa 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -1121,6 +1121,10 @@ private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) if (!dtor) return null; + // Don't try to call `@disable`d dtors + if (dtor.storage_class & STC.disable) + return null; + // Generate shim only when ABI incompatible on target platform if (ad.classKind != ClassKind.cpp || !target.cpp.wrapDtorInExternD) return dtor; diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index d90542f..f4e44e8 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -234,99 +234,9 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) { - UnionExp ue = void; - if (type.isreal()) - { - emplaceExp!(RealExp)(&ue, loc, e1.toReal() - e2.toReal(), type); - } - else if (type.isimaginary()) - { - emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() - e2.toImaginary(), type); - } - else if (type.iscomplex()) - { - // This rigamarole is necessary so that -0.0 doesn't get - // converted to +0.0 by doing an extraneous add with +0.0 - auto c1 = complex_t(CTFloat.zero); - real_t r1 = CTFloat.zero; - real_t i1 = CTFloat.zero; - auto c2 = complex_t(CTFloat.zero); - real_t r2 = CTFloat.zero; - real_t i2 = CTFloat.zero; - auto v = complex_t(CTFloat.zero); - int x; - if (e1.type.isreal()) - { - r1 = e1.toReal(); - x = 0; - } - else if (e1.type.isimaginary()) - { - i1 = e1.toImaginary(); - x = 3; - } - else - { - c1 = e1.toComplex(); - x = 6; - } - if (e2.type.isreal()) - { - r2 = e2.toReal(); - } - else if (e2.type.isimaginary()) - { - i2 = e2.toImaginary(); - x += 1; - } - else - { - c2 = e2.toComplex(); - x += 2; - } - switch (x) - { - case 0 + 0: - v = complex_t(r1 - r2); - break; - case 0 + 1: - v = complex_t(r1, -i2); - break; - case 0 + 2: - v = complex_t(r1 - creall(c2), -cimagl(c2)); - break; - case 3 + 0: - v = complex_t(-r2, i1); - break; - case 3 + 1: - v = complex_t(CTFloat.zero, i1 - i2); - break; - case 3 + 2: - v = complex_t(-creall(c2), i1 - cimagl(c2)); - break; - case 6 + 0: - v = complex_t(creall(c1) - r2, cimagl(c1)); - break; - case 6 + 1: - v = complex_t(creall(c1), cimagl(c1) - i2); - break; - case 6 + 2: - v = c1 - c2; - break; - default: - assert(0); - } - emplaceExp!(ComplexExp)(&ue, loc, v, type); - } - else if (SymOffExp soe = e1.isSymOffExp()) - { - emplaceExp!(SymOffExp)(&ue, loc, soe.var, soe.offset - e2.toInteger()); - ue.exp().type = type; - } - else - { - emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() - e2.toInteger(), type); - } + // Compute e1-e2 as e1+(-e2) + UnionExp neg = Neg(e2.type, e2); + UnionExp ue = Add(loc, type, e1, neg.exp()); return ue; } @@ -1213,6 +1123,10 @@ UnionExp ArrayLength(Type type, Expression e1) Expression e = (cast(TypeSArray)e1.type.toBasetype()).dim; emplaceExp!(UnionExp)(&ue, e); } + else if (e1.isNullExp()) + { + emplaceExp!(IntegerExp)(&ue, loc, 0, type); + } else cantExp(ue); return ue; @@ -1505,17 +1419,11 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) Type t2 = e2.type.toBasetype(); //printf("Cat(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); //printf("\tt1 = %s, t2 = %s, type = %s\n", t1.toChars(), t2.toChars(), type.toChars()); - if (e1.op == EXP.null_ && (e2.op == EXP.int64 || e2.op == EXP.structLiteral)) - { - e = e2; - t = t1; - goto L2; - } - else if ((e1.op == EXP.int64 || e1.op == EXP.structLiteral) && e2.op == EXP.null_) + + /* e is the non-null operand, t is the type of the null operand + */ + UnionExp catNull(Expression e, Type t) { - e = e1; - t = t2; - L2: Type tn = e.type.toBasetype(); if (tn.ty.isSomeChar) { @@ -1545,6 +1453,15 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) assert(ue.exp().type); return ue; } + + if (e1.op == EXP.null_ && (e2.op == EXP.int64 || e2.op == EXP.structLiteral)) + { + return catNull(e2, t1); + } + else if ((e1.op == EXP.int64 || e1.op == EXP.structLiteral) && e2.op == EXP.null_) + { + return catNull(e1, t2); + } else if (e1.op == EXP.null_ && e2.op == EXP.null_) { if (type == e1.type) diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index a3bebb7..2679a63 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -1675,7 +1675,7 @@ final class CParser(AST) : Parser!AST auto stags = applySpecifier(stag, specifier); symbols.push(stags); - if (tt.tok == TOK.enum_) + if (0 && tt.tok == TOK.enum_) // C11 proscribes enums with no members, but we allow it { if (!tt.members) error(tt.loc, "`enum %s` has no members", stag.toChars()); diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 12051d9..afd19f3 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -1106,9 +1106,14 @@ MATCH implicitConvTo(Expression e, Type t) MATCH visitCond(CondExp e) { - auto result = visit(e); - if (result != MATCH.nomatch) - return result; + e.econd = e.econd.optimize(WANTvalue); + const opt = e.econd.toBool(); + if (opt.isPresent()) + { + auto result = visit(e); + if (result != MATCH.nomatch) + return result; + } MATCH m1 = e.e1.implicitConvTo(t); MATCH m2 = e.e2.implicitConvTo(t); @@ -2942,6 +2947,9 @@ Lagain: t1 = Type.basic[ty1]; t2 = Type.basic[ty2]; + + if (!(t1 && t2)) + return null; e1 = e1.castTo(sc, t1); e2 = e2.castTo(sc, t2); return Lret(Type.basic[ty]); diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 2c7d381..c8f6c2a 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -684,6 +684,7 @@ public: const char *kind() const override; bool isUnique(); bool needsClosure(); + bool checkClosure(); bool hasNestedFrameRefs(); ParameterList getParameterList(); diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 5841a25..890c3b6 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -2352,6 +2352,7 @@ public: if (ExpInitializer ie = v._init.isExpInitializer()) { result = interpretRegion(ie.exp, istate, goal); + return; } else if (v._init.isVoidInitializer()) { @@ -2359,12 +2360,16 @@ public: // There is no AssignExp for void initializers, // so set it here. setValue(v, result); + return; } - else + else if (v._init.isArrayInitializer()) { - e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); - result = CTFEExp.cantexp; + result = v._init.initializerToExpression(v.type); + if (result !is null) + return; } + e.error("declaration `%s` is not yet implemented in CTFE", e.toChars()); + result = CTFEExp.cantexp; } else if (v.type.size() == 0) { diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 0be938f..5e802da 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -619,7 +619,7 @@ extern (C++) final class Module : Package else { // if module is not named 'package' but we're trying to read 'package.d', we're looking for a package module - bool isPackageMod = (strcmp(toChars(), "package") != 0) && (strcmp(srcfile.name(), package_d) == 0 || (strcmp(srcfile.name(), package_di) == 0)); + bool isPackageMod = (strcmp(toChars(), "package") != 0) && isPackageFileName(srcfile); if (isPackageMod) .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars()); else @@ -824,8 +824,7 @@ extern (C++) final class Module : Package const(char)* srcname = srcfile.toChars(); //printf("Module::parse(srcname = '%s')\n", srcname); - isPackageFile = (strcmp(srcfile.name(), package_d) == 0 || - strcmp(srcfile.name(), package_di) == 0); + isPackageFile = isPackageFileName(srcfile); const(char)[] buf = cast(const(char)[]) this.src; bool needsReencoding = true; @@ -1032,8 +1031,7 @@ extern (C++) final class Module : Package } assert(dst); Module m = ppack ? ppack.isModule() : null; - if (m && (strcmp(m.srcfile.name(), package_d) != 0 && - strcmp(m.srcfile.name(), package_di) != 0)) + if (m && !isPackageFileName(m.srcfile)) { .error(md.loc, "package name '%s' conflicts with usage as a module name in file %s", ppack.toPrettyChars(), m.srcfile.toChars()); } diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index 2b608f6..c940ff0 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -2441,6 +2441,15 @@ Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) auto sd = s.isScopeDsymbol(); // new declaration auto sd2 = s2.isScopeDsymbol(); // existing declaration + static if (log) void print(EnumDeclaration sd) + { + printf("members: %p\n", sd.members); + printf("symtab: %p\n", sd.symtab); + printf("endlinnum: %d\n", sd.endlinnum); + printf("type: %s\n", sd.type.toChars()); + printf("memtype: %s\n", sd.memtype.toChars()); + } + if (!sd2) { /* Look in tag table @@ -2473,6 +2482,23 @@ Dsymbol handleTagSymbols(ref Scope sc, Dsymbol s, Dsymbol s2, ScopeDsymbol sds) { sd2.members = sd.members; // transfer definition to sd2 sd.members = null; + if (auto ed2 = sd2.isEnumDeclaration()) + { + auto ed = sd.isEnumDeclaration(); + if (ed.memtype != ed2.memtype) + return null; // conflict + + // transfer ed's members to sd2 + ed2.members.foreachDsymbol( (s) + { + if (auto em = s.isEnumMember()) + em.ed = ed2; + }); + + ed2.type = ed.type; + ed2.memtype = ed.memtype; + ed2.added = false; + } return sd2; } else diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index 11a51f1..7f57cbe 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -2023,7 +2023,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(EnumDeclaration ed) { //printf("EnumDeclaration::semantic(sd = %p, '%s') %s\n", sc.scopesym, sc.scopesym.toChars(), ed.toChars()); - //printf("EnumDeclaration::semantic() %p %s\n", this, ed.toChars()); + //printf("EnumDeclaration::semantic() %p %s\n", ed, ed.toChars()); if (ed.semanticRun >= PASS.semanticdone) return; // semantic() already completed if (ed.semanticRun == PASS.semantic) @@ -4442,7 +4442,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor invd.semanticRun < PASS.semantic && !ad.isUnionDeclaration() // users are on their own with union fields ) + { + invd.fixupInvariantIdent(ad.invs.length); ad.invs.push(invd); + } if (!invd.type) invd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, invd.storage_class); @@ -5713,6 +5716,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor */ void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) { + //printf("addEnumMembers(ed: %p)\n", ed); if (ed.added) return; ed.added = true; @@ -5736,6 +5740,7 @@ void addEnumMembers(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) em.ed = ed; if (isCEnum) { + //printf("adding EnumMember %s to %p\n", em.toChars(), ed); em.addMember(sc, ed); // add em to ed's symbol table em.addMember(sc, sds); // add em to symbol table that ed is in em.parent = ed; // restore it after previous addMember() changed it diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index c0a8e9f..02f12e4 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -7323,7 +7323,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol errors = true; } L1: - //printf("\tnested inside %s\n", enclosing.toChars()); + //printf("\tnested inside %s as it references %s\n", enclosing.toChars(), sa.toChars()); nested |= 1; } } diff --git a/gcc/d/dmd/entity.d b/gcc/d/dmd/entity.d index ef2fdef..c29d499 100644 --- a/gcc/d/dmd/entity.d +++ b/gcc/d/dmd/entity.d @@ -17,18 +17,27 @@ import core.stdc.ctype; nothrow: -public int HtmlNamedEntity(const(char)* p, size_t length) +/********************************** + * See if `name` is an HTML Named Entity + * Params: + * name = name of the entity + * Returns: + * code point corresponding to the named entity + * ~0 for not recognized as a named entity + */ +public uint HtmlNamedEntity(scope const char[] name) pure @nogc @safe { - int tableIndex = tolower(*p) - 'a'; - if (tableIndex >= 0 && tableIndex < 26) + const firstC = tolower(name[0]); + if (firstC >= 'a' && firstC <= 'z') { - foreach (entity; namesTable[tableIndex]) + // Linear search (use hash table instead?) + foreach (entity; namesTable[firstC - 'a']) { - if (entity.name == p[0 .. length]) + if (entity.name == name) return entity.value; } } - return -1; + return ~0; } private: diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index 0646f57..fb5e092 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -351,7 +351,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, STC { unsafeAssign!"scope variable"(v); } - else if (v.storage_class & STC.variadic && p == sc.func) + else if (v.isTypesafeVariadicParameter && p == sc.func) { Type tb = v.type.toBasetype(); if (tb.ty == Tarray || tb.ty == Tsarray) @@ -649,7 +649,8 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) Dsymbol p = v.toParent2(); if (va && !vaIsRef && !va.isScope() && !v.isScope() && - (va.storage_class & v.storage_class & (STC.maybescope | STC.variadic)) == STC.maybescope && + !v.isTypesafeVariadicParameter && !va.isTypesafeVariadicParameter && + (va.storage_class & v.storage_class & STC.maybescope) && p == fd) { /* Add v to va's list of dependencies @@ -663,7 +664,8 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) !(v.storage_class & STC.return_) && v.isParameter() && fd.flags & FUNCFLAG.returnInprocess && - p == fd) + p == fd && + !v.isTypesafeVariadicParameter) { if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), fd.toChars()); inferReturn(fd, v, /*returnScope:*/ true); // infer addition of 'return' to make `return scope` @@ -735,7 +737,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) } result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1); } - else if (v.storage_class & STC.variadic && p == fd) + else if (v.isTypesafeVariadicParameter && p == fd) { Type tb = v.type.toBasetype(); if (tb.ty == Tarray || tb.ty == Tsarray) @@ -1022,7 +1024,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) continue; } } - else if (v.storage_class & STC.variadic && p == sc.func) + else if (v.isTypesafeVariadicParameter && p == sc.func) { Type tb = v.type.toBasetype(); if (tb.ty == Tarray || tb.ty == Tsarray) @@ -1194,7 +1196,8 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) v.isParameter() && !v.doNotInferReturn && sc.func.flags & FUNCFLAG.returnInprocess && - p == sc.func) + p == sc.func && + !v.isTypesafeVariadicParameter) { inferReturn(sc.func, v, /*returnScope:*/ true); // infer addition of 'return' continue; @@ -1250,7 +1253,7 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } } } - else if (v.storage_class & STC.variadic && p == sc.func) + else if (v.isTypesafeVariadicParameter && p == sc.func) { Type tb = v.type.toBasetype(); if (tb.ty == Tarray || tb.ty == Tsarray) @@ -1627,7 +1630,7 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re { if (tb.ty == Tsarray) return; - if (v.storage_class & STC.variadic) + if (v.isTypesafeVariadicParameter) { er.byvalue.push(v); return; @@ -1943,7 +1946,7 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR VarDeclaration v = ve.var.isVarDeclaration(); if (tb.ty == Tarray || tb.ty == Tsarray) { - if (v && v.storage_class & STC.variadic) + if (v && v.isTypesafeVariadicParameter) { er.pushRef(v, retRefTransition); return; @@ -2586,3 +2589,15 @@ private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool g return sc.setUnsafeDIP1000(gag, e.loc, "cannot take address of `scope` variable `%s` since `scope` applies to first indirection only", v); } + +/**************************** + * Determine if `v` is a typesafe variadic parameter. + * Params: + * v = variable to check + * Returns: + * true if `v` is a variadic parameter + */ +bool isTypesafeVariadicParameter(VarDeclaration v) +{ + return !!(v.storage_class & STC.variadic); +} diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 35ba5fa..30baabd 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -3463,8 +3463,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.arguments && exp.newtype.isTypeSArray()) { auto ts = exp.newtype.isTypeSArray(); - edim = ts.dim; - exp.newtype = ts.next; + // check `new Value[Key]` + ts.dim = ts.dim.expressionSemantic(sc); + if (ts.dim.op == EXP.type) + { + exp.newtype = new TypeAArray(ts.next, ts.dim.isTypeExp().type); + } + else + { + edim = ts.dim; + exp.newtype = ts.next; + } } ClassDeclaration cdthis = null; @@ -3518,18 +3527,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { return setError(); } - //https://issues.dlang.org/show_bug.cgi?id=20547 - //exp.arguments are the "parameters" to [], not to a real function - //so the errors that come from preFunctionParameters are misleading - if (originalNewtype.ty == Tsarray) - { - if (preFunctionParameters(sc, exp.arguments, false)) - { - exp.error("cannot create a `%s` with `new`", originalNewtype.toChars()); - return setError(); - } - } - else if (preFunctionParameters(sc, exp.arguments)) + if (preFunctionParameters(sc, exp.arguments)) { return setError(); } @@ -3885,6 +3883,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = exp.type.pointerTo(); } + else if (tb.ty == Taarray) + { + // e.g. `new Alias(args)` + if (nargs) + { + exp.error("`new` cannot take arguments for an associative array"); + return setError(); + } + } else { exp.error("cannot create a `%s` with `new`", exp.type.toChars()); @@ -5019,7 +5026,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); err = true; } - if (tf.trust <= TRUST.system && sc.setUnsafe()) + if (tf.trust <= TRUST.system && sc.setUnsafe(true, exp.loc, + "`@safe` function `%s` cannot call `@system` `%s`", sc.func, exp.e1)) { exp.error("`@safe` %s `%s` cannot call `@system` %s `%s`", sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); @@ -7588,11 +7596,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // Check for unsafe casts - if (!isSafeCast(ex, t1b, tob) && - (!sc.func && sc.stc & STC.safe || sc.setUnsafe())) + if (!isSafeCast(ex, t1b, tob)) { - exp.error("cast from `%s` to `%s` not allowed in safe code", exp.e1.type.toChars(), exp.to.toChars()); - return setError(); + // This is an ad-hoc fix for https://issues.dlang.org/show_bug.cgi?id=19646 + // Should be replaced by a more general @system variables implementation + if (!sc.func && sc.stc & STC.safe) + { + exp.error("cast from `%s` to `%s` not allowed in safe code", exp.e1.type.toChars(), exp.to.toChars()); + return setError(); + } + + if (sc.setUnsafe(false, exp.loc, "cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to)) + { + return setError(); + } } // `object.__ArrayCast` is a rewrite of an old runtime hook `_d_arraycast`. `_d_arraycast` was not built @@ -8900,6 +8917,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if ((t1.ty != Tstruct && t1.ty != Tclass && e2x.checkValue()) || e2x.checkSharedAccess(sc)) return setError(); + + if (e2x.type.isTypeNoreturn() && !e2x.isAssertExp() && !e2x.isThrowExp() && !e2x.isCallExp()) + { + auto msg = new StringExp(e2x.loc, "Accessed expression of type `noreturn`"); + msg.type = Type.tstring; + e2x = new AssertExp(e2x.loc, IntegerExp.literal!0, msg); + e2x.type = Type.tnoreturn; + return setResult(e2x); + } exp.e2 = e2x; } @@ -9896,9 +9922,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ae.e2.type.nextOf && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf); + /* Unlike isArrayCtor above, lower all Rvalues. If the RHS is a literal, + * then we do want to make a temporary for it and call its destructor. + */ const isArraySetCtor = (ae.e1.isSliceExp || ae.e1.type.ty == Tsarray) && - ae.e2.isLvalue && (ae.e2.type.ty == Tstruct || ae.e2.type.ty == Tsarray) && ae.e1.type.nextOf && ae.e1.type.nextOf.equivalent(ae.e2.type); @@ -10302,6 +10330,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // `__appendtmp*` will be destroyed together with the array `exp.e1`. auto vd = eValue2.isDeclarationExp().declaration.isVarDeclaration(); vd.storage_class |= STC.nodtor; + // Be more explicit that this "declaration" is local to the expression + vd.storage_class |= STC.exptemp; } auto ale = new ArrayLengthExp(exp.loc, value1); @@ -11870,6 +11900,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { //printf("Lowering to __equals %s %s\n", exp.e1.toChars(), exp.e2.toChars()); + // https://issues.dlang.org/show_bug.cgi?id=22390 + // Equality comparison between array of noreturns simply lowers to length equality comparison + if (t1.nextOf.isTypeNoreturn() && t2.nextOf.isTypeNoreturn()) + { + Expression exp_l1 = new DotIdExp(exp.e1.loc, exp.e1, Id.length); + Expression exp_l2 = new DotIdExp(exp.e2.loc, exp.e2, Id.length); + auto e = new EqualExp(EXP.equal, exp.loc, exp_l1, exp_l2); + result = e.expressionSemantic(sc); + return; + } + if (!verifyHookExist(exp.loc, *sc, Id.__equals, "equal checks on arrays")) return setError(); @@ -12638,7 +12679,7 @@ Expression semanticY(DotIdExp exp, Scope* sc, int flag) e = new CommaExp(exp.loc, eleft, e); e.type = Type.tvoid; // ambiguous type? } - return e; + return e.expressionSemantic(sc); } if (auto o = s.isOverloadSet()) { @@ -13131,26 +13172,24 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) { //printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars()); - if (v) + if (v is null) + return true; + + if (!v.canTakeAddressOf()) + { + exp.error("cannot take address of `%s`", exp.toChars()); + return false; + } + if (sc.func && !sc.intypeof && !v.isDataseg()) { - if (!v.canTakeAddressOf()) + v.storage_class &= ~STC.maybescope; + v.doNotInferScope = true; + if (global.params.useDIP1000 != FeatureState.enabled && + !(v.storage_class & STC.temp) && + sc.setUnsafe(false, exp.loc, "cannot take address of local `%s` in `@safe` function `%s`", v, sc.func)) { - exp.error("cannot take address of `%s`", exp.toChars()); return false; } - if (sc.func && !sc.intypeof && !v.isDataseg()) - { - const(char)* p = v.isParameter() ? "parameter" : "local"; - v.storage_class &= ~STC.maybescope; - v.doNotInferScope = true; - if (global.params.useDIP1000 != FeatureState.enabled && - !(v.storage_class & STC.temp) && - sc.setUnsafe()) - { - exp.error("cannot take address of %s `%s` in `@safe` function `%s`", p, v.toChars(), sc.func.toChars()); - return false; - } - } } return true; } diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d index 9fe40bf..0ea7303 100644 --- a/gcc/d/dmd/file_manager.d +++ b/gcc/d/dmd/file_manager.d @@ -20,6 +20,12 @@ import dmd.identifier; enum package_d = "package." ~ mars_ext; enum package_di = "package." ~ hdr_ext; +/// Returns: whether a file with `name` is a special "package.d" module +bool isPackageFileName(scope FileName fileName) nothrow +{ + return FileName.equals(fileName.name, package_d) || FileName.equals(fileName.name, package_di); +} + final class FileManager { private StringTable!(const(ubyte)[]) files; diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 83bc2ea..7475cb4 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -867,6 +867,8 @@ extern (C++) class FuncDeclaration : Declaration auto f = s.isFuncDeclaration(); if (!f) return 0; + if (f.storage_class & STC.disable) + return 0; if (t.equals(f.type)) { fd = f; @@ -2048,9 +2050,11 @@ extern (C++) class FuncDeclaration : Declaration } if (!found) { - //printf("\tadding sibling %s\n", fdthis.toPrettyChars()); + //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); if (!sc.intypeof && !(sc.flags & SCOPE.compile)) + { siblingCallers.push(fdthis); + } } } @@ -2164,7 +2168,6 @@ extern (C++) class FuncDeclaration : Declaration return false; Lyes: - //printf("\tneeds closure\n"); return true; } @@ -2176,14 +2179,21 @@ extern (C++) class FuncDeclaration : Declaration * Returns: * true if any errors occur. */ - extern (D) final bool checkClosure() + extern (C++) final bool checkClosure() { + //printf("checkClosure() %s\n", toChars()); if (!needsClosure()) return false; if (setGC()) { - error("is `@nogc` yet allocates closures with the GC"); + error("is `@nogc` yet allocates closure for `%s()` with the GC", toChars()); + if (global.gag) // need not report supplemental errors + return true; + } + else if (global.params.betterC) + { + error("is `-betterC` yet allocates closure for `%s()` with the GC", toChars()); if (global.gag) // need not report supplemental errors return true; } @@ -2216,7 +2226,7 @@ extern (C++) class FuncDeclaration : Declaration break LcheckAncestorsOfANestedRef; } a.push(f); - .errorSupplemental(f.loc, "%s closes over variable %s at %s", + .errorSupplemental(f.loc, "`%s` closes over variable `%s` at %s", f.toPrettyChars(), v.toChars(), v.loc.toChars()); break LcheckAncestorsOfANestedRef; } @@ -3293,7 +3303,8 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, td.kind(), td.parent.toPrettyChars(), td.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); - printCandidates(loc, td, sc.isDeprecated()); + if (!global.gag || global.params.showGaggedErrors) + printCandidates(loc, td, sc.isDeprecated()); return null; } /* This case used to happen when several ctors are mixed in an agregate. @@ -3331,7 +3342,8 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, { .error(loc, "none of the overloads of `%s` are callable using a %sobject", fd.ident.toChars(), thisBuf.peekChars()); - printCandidates(loc, fd, sc.isDeprecated()); + if (!global.gag || global.params.showGaggedErrors) + printCandidates(loc, fd, sc.isDeprecated()); return null; } @@ -3361,18 +3373,23 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, { .error(loc, "none of the overloads of `%s` are callable using argument types `%s`", fd.toChars(), fargsBuf.peekChars()); - printCandidates(loc, fd, sc.isDeprecated()); + if (!global.gag || global.params.showGaggedErrors) + printCandidates(loc, fd, sc.isDeprecated()); return null; } .error(loc, "%s `%s%s%s` is not callable using argument types `%s`", fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), tf.modToChars(), fargsBuf.peekChars()); + // re-resolve to check for supplemental message - const(char)* failMessage; - functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage); - if (failMessage) - errorSupplemental(loc, failMessage); + if (!global.gag || global.params.showGaggedErrors) + { + const(char)* failMessage; + functionResolve(m, orig_s, loc, sc, tiargs, tthis, fargs, &failMessage); + if (failMessage) + errorSupplemental(loc, failMessage); + } return null; } @@ -4220,6 +4237,7 @@ extern (C++) final class InvariantDeclaration : FuncDeclaration { extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id, Statement fbody) { + // Make a unique invariant for now; we'll fix it up as we add it to the aggregate invariant list. super(loc, endloc, id ? id : Identifier.generateId("__invariant"), stc, null); this.fbody = fbody; } @@ -4256,6 +4274,15 @@ extern (C++) final class InvariantDeclaration : FuncDeclaration { v.visit(this); } + + extern (D) void fixupInvariantIdent(size_t offset) + { + OutBuffer idBuf; + idBuf.writestring("__invariant"); + idBuf.print(offset); + + ident = Identifier.idPool(idBuf[]); + } } @@ -4447,12 +4474,15 @@ void errorSupplementalInferredSafety(FuncDeclaration fd, int maxDepth, bool depr errorFunc(s.loc, s.fmtStr, s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); } - else if (FuncDeclaration fd2 = cast(FuncDeclaration) s.arg0) + else if (s.arg0.dyncast() == DYNCAST.dsymbol) { - if (maxDepth > 0) + if (FuncDeclaration fd2 = (cast(Dsymbol) s.arg0).isFuncDeclaration()) { - errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation); + if (maxDepth > 0) + { + errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); + errorSupplementalInferredSafety(fd2, maxDepth - 1, deprecation); + } } } } diff --git a/gcc/d/dmd/impcnvtab.d b/gcc/d/dmd/impcnvtab.d index ab46f5e..832c331 100644 --- a/gcc/d/dmd/impcnvtab.d +++ b/gcc/d/dmd/impcnvtab.d @@ -64,6 +64,57 @@ enum ImpCnvTab impCnvTab = generateImpCnvTab(); ImpCnvTab generateImpCnvTab() { + TY[TMAX] typeTYs = + [ + Tarray, + Tsarray, + Taarray, + Tpointer, + Treference, + Tfunction, + Tident, + Tclass, + Tstruct, + Tenum, + Tdelegate, + Tnone, + Tvoid, + Tint8, + Tuns8, + Tint16, + Tuns16, + Tint32, + Tuns32, + Tint64, + Tuns64, + Tfloat32, + Tfloat64, + Tfloat80, + Timaginary32, + Timaginary64, + Timaginary80, + Tcomplex32, + Tcomplex64, + Tcomplex80, + Tbool, + Tchar, + Twchar, + Tdchar, + Terror, + Tinstance, + Ttypeof, + Ttuple, + Tslice, + Treturn, + Tnull, + Tvector, + Tint128, + Tuns128, + Ttraits, + Tmixin, + Tnoreturn, + Ttag, + ]; ImpCnvTab impCnvTab; // Set conversion tables @@ -375,5 +426,9 @@ ImpCnvTab generateImpCnvTab() X(Tcomplex80,Tcomplex80, Tcomplex80,Tcomplex80, Tcomplex80); + // "No type is implicitly convertible to noreturn, but noreturn is implicitly convertible to every other type" + foreach(convertToTy; typeTYs) + X(Tnoreturn, convertToTy, convertToTy, convertToTy, convertToTy); + return impCnvTab; } diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index a1963da..a576712 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -567,18 +567,40 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ i.exp = e.optimize(WANTvalue); } } + + // Look for the case of statically initializing an array with a single member. + // Recursively strip static array / enum layers until a compatible element is found, + // and return an `ArrayLiteralExp` repeating the initializer, or `null` if no match found + // int[2][3] = 7 => [[7, 7], [7, 7], [7, 7]] + // int[2] = new Object => null + Expression sarrayRepeat(Type tb) { - // Look for the case of statically initializing an array - // with a single member. - auto tba = tb.isTypeSArray(); - if (tba && !tba.next.equals(ti.toBasetype().nextOf()) && i.exp.implicitConvTo(tba.next)) + auto tsa = tb.isTypeSArray(); + if (!tsa) + return null; + + // printf("i.exp = %s, tsa = %s\n", i.exp.toChars(), tsa.toChars()); + Expression elem = null; + if (i.exp.implicitConvTo(tb.nextOf())) + elem = i.exp.implicitCastTo(sc, tb.nextOf()); + else if (auto ae = sarrayRepeat(tb.nextOf().toBasetype())) + elem = ae; + else + return null; + + auto arrayElements = new Expressions(cast(size_t) tsa.dim.toInteger()); + foreach (ref e; *arrayElements) + e = elem; + return new ArrayLiteralExp(i.exp.loc, tb, elem, arrayElements); + } + + if (auto sa = sarrayRepeat(tb)) { - /* If the variable is not actually used in compile time, array creation is - * redundant. So delay it until invocation of toExpression() or toDt(). - */ - t = tb.nextOf(); + // printf("sa = %s\n", sa.toChars()); + i.exp = sa; } + { auto tta = t.isTypeSArray(); if (i.exp.implicitConvTo(t)) { @@ -595,6 +617,7 @@ extern(C++) Initializer initializerSemantic(Initializer init, Scope* sc, ref Typ } else { + auto tba = tb.isTypeSArray(); // Look for mismatch of compile-time known length to emit // better diagnostic message, as same as AssignExp::semantic. if (tba && i.exp.implicitConvTo(tba.next.arrayOf()) > MATCH.nomatch) diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index ef918e2..11afcdd 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -1326,7 +1326,7 @@ class Lexer switch (*p) { case ';': - c = HtmlNamedEntity(idstart, p - idstart); + c = HtmlNamedEntity(idstart[0 .. p - idstart]); if (c == ~0) { error(loc, "unnamed character entity &%.*s;", cast(int)(p - idstart), idstart); diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d index 4eb4228..369d60e 100644 --- a/gcc/d/dmd/mustuse.d +++ b/gcc/d/dmd/mustuse.d @@ -98,7 +98,7 @@ void checkMustUseReserved(Dsymbol sym) */ private bool isAssignment(Expression e) { - if (e.isAssignExp || e.isBinAssignExp) + if (e.isAssignExp || e.isBinAssignExp || e.isConstructExp || e.isBlitExp) return true; if (auto ce = e.isCallExp()) { diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index 2b7b9ac..be28d08 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -1120,7 +1120,7 @@ Expression Expression_optimize(Expression e, int result, bool keepLvalue) e.e1 = ci; } } - if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray) + if (e.e1.op == EXP.string_ || e.e1.op == EXP.arrayLiteral || e.e1.op == EXP.assocArrayLiteral || e.e1.type.toBasetype().ty == Tsarray || e.e1.op == EXP.null_) { ret = ArrayLength(e.type, e.e1).copy(); } diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 4e3fd53..a2c364e 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -2186,7 +2186,13 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.identifier) s = new AST.DebugSymbol(token.loc, token.ident); else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) + { + // @@@DEPRECATED_2.111@@@ + // Deprecated in 2.101, remove in 2.111 + deprecation("`debug = <integer>` is deprecated, use debug identifiers instead"); + s = new AST.DebugSymbol(token.loc, cast(uint)token.unsvalue); + } else { error("identifier or integer expected, not `%s`", token.toChars()); @@ -2215,7 +2221,13 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.identifier) id = token.ident; else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) + { + // @@@DEPRECATED_2.111@@@ + // Deprecated in 2.101, remove in 2.111 + deprecation("`debug( <integer> )` is deprecated, use debug identifiers instead"); + level = cast(uint)token.unsvalue; + } else error("identifier or integer expected inside `debug(...)`, not `%s`", token.toChars()); loc = token.loc; @@ -2235,7 +2247,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.identifier) s = new AST.VersionSymbol(token.loc, token.ident); else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) + { + // @@@DEPRECATED_2.111@@@ + // Deprecated in 2.101, remove in 2.111 + deprecation("`version = <integer>` is deprecated, use version identifiers instead"); s = new AST.VersionSymbol(token.loc, cast(uint)token.unsvalue); + } else { error("identifier or integer expected, not `%s`", token.toChars()); @@ -2269,7 +2286,13 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.identifier) id = token.ident; else if (token.value == TOK.int32Literal || token.value == TOK.int64Literal) + { + // @@@DEPRECATED_2.111@@@ + // Deprecated in 2.101, remove in 2.111 + deprecation("`version( <integer> )` is deprecated, use version identifiers instead"); + level = cast(uint)token.unsvalue; + } else if (token.value == TOK.unittest_) id = Identifier.idPool(Token.toString(TOK.unittest_)); else if (token.value == TOK.assert_) @@ -9312,13 +9335,10 @@ LagainStc: { AST.TypeAArray taa = cast(AST.TypeAArray)t; AST.Type index = taa.index; + // `new Type[expr]` is a static array auto edim = AST.typeToExpression(index); - if (!edim) - { - error("cannot create a `%s` with `new`", t.toChars); - return new AST.NullExp(loc); - } - t = new AST.TypeSArray(taa.next, edim); + if (edim) + t = new AST.TypeSArray(taa.next, edim); } else if (token.value == TOK.leftParenthesis && t.ty != Tsarray) { diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index c5d7667..6f37770 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -470,14 +470,6 @@ private extern(C++) final class Semantic3Visitor : Visitor if (f.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) { stc |= STC.variadic; - auto vtypeb = vtype.toBasetype(); - if (vtypeb.ty == Tarray || vtypeb.ty == Tclass) - { - /* Since it'll be pointing into the stack for the array - * contents, it needs to be `scope` - */ - stc |= STC.scope_; - } } if ((funcdecl.flags & FUNCFLAG.inferScope) && !(fparam.storageClass & STC.scope_)) @@ -1379,7 +1371,7 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.flags &= ~FUNCFLAG.semantic3Errors; if (funcdecl.type.ty == Terror) funcdecl.errors = true; - //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent.toChars(), toChars(), sc, loc.toChars()); + //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", funcdecl.parent.toChars(), funcdecl.toChars(), sc, funcdecl.loc.toChars()); //fflush(stdout); } diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index f23b988..e5e5753 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -733,9 +733,26 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor { assert(oaggr.type); - fs.error("invalid `foreach` aggregate `%s` of type `%s`", oaggr.toChars(), oaggr.type.toPrettyChars()); - if (isAggregate(fs.aggr.type)) - fs.loc.errorSupplemental("maybe define `opApply()`, range primitives, or use `.tupleof`"); + fs.error("invalid `%s` aggregate `%s` of type `%s`", + Token.toChars(fs.op), oaggr.toChars(), oaggr.type.toPrettyChars()); + + if (auto ad = isAggregate(fs.aggr.type)) + { + if (fs.op == TOK.foreach_reverse_) + { + fs.loc.errorSupplemental("`foreach_reverse` works with bidirectional ranges"~ + " (implementing `back` and `popBack`), aggregates implementing" ~ + " `opApplyReverse`, or the result of an aggregate's `.tupleof` property"); + fs.loc.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isBidirectionalRange"); + } + else + { + fs.loc.errorSupplemental("`foreach` works with input ranges"~ + " (implementing `front` and `popFront`), aggregates implementing" ~ + " `opApply`, or the result of an aggregate's `.tupleof` property"); + fs.loc.errorSupplemental("https://dlang.org/phobos/std_range_primitives.html#isInputRange"); + } + } return setError(); } @@ -2828,10 +2845,20 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor rs.error("`return` statements cannot be in contracts"); errors = true; } - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.os) { - rs.error("`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok)); - errors = true; + // @@@DEPRECATED_2.112@@@ + // Deprecated in 2.100, transform into an error in 2.112 + if (sc.os.tok == TOK.onScopeFailure) + { + rs.deprecation("`return` statements cannot be in `scope(failure)` bodies."); + deprecationSupplemental(rs.loc, "Use try-catch blocks for this purpose"); + } + else + { + rs.error("`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok)); + errors = true; + } } if (sc.tf) { @@ -2913,6 +2940,17 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor rs.exp.type = texp; } + // @@@DEPRECATED_2.111@@@ + const olderrors = global.startGagging(); + // uncomment to turn deprecation into an error when + // deprecation cycle is over + if (discardValue(rs.exp)) + { + //errors = true; + } + if (global.endGagging(olderrors)) + rs.exp.deprecation("`%s` has no effect", rs.exp.toChars()); + /* Replace: * return exp; * with: diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/transitivevisitor.d index 25dee7f..5791a88 100644 --- a/gcc/d/dmd/transitivevisitor.d +++ b/gcc/d/dmd/transitivevisitor.d @@ -862,6 +862,12 @@ package mixin template ParseVisitMethods(AST) visitFuncBody(d); } + override void visit(AST.CtorDeclaration d) + { + //printf("Visiting CtorDeclaration\n"); + visitFuncBody(d); + } + override void visit(AST.StaticCtorDeclaration d) { //printf("Visiting StaticCtorDeclaration\n"); diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index 0469b92..b1f1b1f 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -1272,6 +1272,16 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) errors = true; } + const bool isTypesafeVariadic = i + 1 == dim && + tf.parameterList.varargs == VarArg.typesafe && + (t.isTypeDArray() || t.isTypeClass()); + if (isTypesafeVariadic) + { + /* typesafe variadic arguments are constructed on the stack, so must be `scope` + */ + fparam.storageClass |= STC.scope_ | STC.scopeinferred; + } + if (fparam.storageClass & STC.return_) { if (fparam.isReference()) @@ -1300,8 +1310,7 @@ extern(C++) Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } } - if (i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe && - (t.isTypeDArray() || t.isTypeClass())) + if (isTypesafeVariadic) { /* This is because they can be constructed on the stack * https://dlang.org/spec/function.html#typesafe_variadic_functions diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index 1bb10a8..40c2689 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -873,6 +873,17 @@ public: gcc_unreachable (); } + /* Look for exp = noreturn; */ + if (e->e2->type->isTypeNoreturn ()) + { + /* If the RHS is a `noreturn' expression, there is no point generating + any code for the assignment, just evaluate side effects. */ + tree t1 = build_expr (e->e1); + tree t2 = build_expr (e->e2); + this->result_ = compound_expr (t1, t2); + return; + } + /* Look for array[] = n; */ if (e->e1->op == EXP::slice) { diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index bd9c61d..da65239 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -249,7 +249,7 @@ Compile in debug code. fdebug= D Joined RejectNegative --fdebug=<level|ident> Compile in debug code, code <= <level>, or code identified by <ident>. +-fdebug=<ident> Compile in debug code identified by <ident>. fdoc D @@ -466,7 +466,7 @@ Compile in unittest code. fversion= D Joined RejectNegative --fversion=<level|ident> Compile in version code >= <level> or identified by <ident>. +-fversion=<ident> Compile in version code identified by <ident>. fweak-templates D Var(flag_weak_templates) Init(1) diff --git a/gcc/doc/gcov-dump.texi b/gcc/doc/gcov-dump.texi index 2e82a18..34f2ef7 100644 --- a/gcc/doc/gcov-dump.texi +++ b/gcc/doc/gcov-dump.texi @@ -19,6 +19,7 @@ gcov-dump [@option{-l}|@option{--long}] [@option{-p}|@option{--positions}] [@option{-r}|@option{--raw}] + [@option{-s}|@option{--stable}] [@var{gcovfiles}] @subsubheading Options @@ -41,6 +42,10 @@ Dump positions of records. @itemx --raw Print content records in raw format. +@item -s +@itemx --stable +Print content in stable format usable for comparison. + @item -v @itemx --version Display the @command{gcov-dump} version number (on the standard output), diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 696ac2f..a48cca5 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -416,6 +416,7 @@ Objective-C and Objective-C++ Dialects}. -Wno-analyzer-fd-use-without-check @gol -Wno-analyzer-file-leak @gol -Wno-analyzer-free-of-non-heap @gol +-Wno-analyzer-jump-through-null @gol -Wno-analyzer-malloc-leak @gol -Wno-analyzer-mismatching-deallocation @gol -Wno-analyzer-null-argument @gol @@ -9719,6 +9720,7 @@ Enabling this option effectively enables the following warnings: -Wanalyzer-fd-use-without-check @gol -Wanalyzer-file-leak @gol -Wanalyzer-free-of-non-heap @gol +-Wanalyzer-jump-through-null @gol -Wanalyzer-malloc-leak @gol -Wanalyzer-mismatching-deallocation @gol -Wanalyzer-null-argument @gol @@ -9905,6 +9907,16 @@ is called on a non-heap pointer (e.g. an on-stack buffer, or a global). See @uref{https://cwe.mitre.org/data/definitions/590.html, CWE-590: Free of Memory not on the Heap}. +@item -Wno-analyzer-jump-through-null +@opindex Wanalyzer-jump-through-null +@opindex Wno-analyzer-jump-through-null +This warning requires @option{-fanalyzer}, which enables it; use +@option{-Wno-analyzer-jump-through-null} +to disable it. + +This diagnostic warns for paths through the code in which a @code{NULL} +function pointer is called. + @item -Wno-analyzer-malloc-leak @opindex Wanalyzer-malloc-leak @opindex Wno-analyzer-malloc-leak @@ -9986,7 +9998,7 @@ See @uref{https://cwe.mitre.org/data/definitions/476.html, CWE-476: NULL Pointer @opindex Wanalyzer-putenv-of-auto-var @opindex Wno-analyzer-putenv-of-auto-var This warning requires @option{-fanalyzer}, which enables it; use -@option{-Wno-analyzer-possible-null-dereference} to disable it. +@option{-Wno-analyzer-putenv-of-auto-var} to disable it. This diagnostic warns for paths through the code in which a call to @code{putenv} is passed a pointer to an automatic variable @@ -1570,12 +1570,7 @@ record_store (rtx body, bb_info_t bb_info) width) /* We can only remove the later store if the earlier aliases at least all accesses the later one. */ - && ((MEM_ALIAS_SET (mem) == MEM_ALIAS_SET (s_info->mem) - || alias_set_subset_of (MEM_ALIAS_SET (mem), - MEM_ALIAS_SET (s_info->mem))) - && (!MEM_EXPR (s_info->mem) - || refs_same_for_tbaa_p (MEM_EXPR (s_info->mem), - MEM_EXPR (mem))))) + && mems_same_for_tbaa_p (s_info->mem, mem)) { if (GET_MODE (mem) == BLKmode) { diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc index a6329ab..3971000 100644 --- a/gcc/dwarf2ctf.cc +++ b/gcc/dwarf2ctf.cc @@ -644,6 +644,7 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function, ctf_funcinfo_t func_info; uint32_t num_args = 0; + int linkage = get_AT_flag (function, DW_AT_external); ctf_id_t return_type_id; ctf_id_t function_type_id; @@ -687,7 +688,8 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function, function_name, (const ctf_funcinfo_t *)&func_info, function, - from_global_func); + from_global_func, + linkage); /* Second pass on formals: generate the CTF types corresponding to them and add them as CTF function args. */ diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc index 3ac39c1..cfea9cf 100644 --- a/gcc/dwarf2out.cc +++ b/gcc/dwarf2out.cc @@ -6069,11 +6069,12 @@ dwarf2out_register_external_die (tree decl, const char *sym, if (!external_die_map) external_die_map = hash_map<tree, sym_off_pair>::create_ggc (1000); - /* When we do tree merging during WPA we can end up re-using GC memory - as there's currently no way to unregister external DIEs. Ideally - we'd register them only after merging finished but allowing override - here is easiest. See PR106334. */ - gcc_checking_assert (flag_wpa || !external_die_map->get (decl)); + /* When we do tree merging during WPA or with -flto-partition=none we + can end up re-using GC memory as there's currently no way to unregister + external DIEs. Ideally we'd register them only after merging finished + but allowing override here is easiest. See PR106334. */ + gcc_checking_assert (!(in_lto_p && !flag_wpa) + || !external_die_map->get (decl)); sym_off_pair p = { IDENTIFIER_POINTER (get_identifier (sym)), off }; external_die_map->put (decl, p); } diff --git a/gcc/expmed.cc b/gcc/expmed.cc index 9b01b5a..8d7418b 100644 --- a/gcc/expmed.cc +++ b/gcc/expmed.cc @@ -5662,63 +5662,9 @@ emit_store_flag_1 (rtx target, enum rtx_code code, rtx op0, rtx op1, break; } - /* If we are comparing a double-word integer with zero or -1, we can - convert the comparison into one involving a single word. */ - scalar_int_mode int_mode; - if (is_int_mode (mode, &int_mode) - && GET_MODE_BITSIZE (int_mode) == BITS_PER_WORD * 2 - && (!MEM_P (op0) || ! MEM_VOLATILE_P (op0))) - { - rtx tem; - if ((code == EQ || code == NE) - && (op1 == const0_rtx || op1 == constm1_rtx)) - { - rtx op00, op01; - - /* Do a logical OR or AND of the two words and compare the - result. */ - op00 = simplify_gen_subreg (word_mode, op0, int_mode, 0); - op01 = simplify_gen_subreg (word_mode, op0, int_mode, UNITS_PER_WORD); - tem = expand_binop (word_mode, - op1 == const0_rtx ? ior_optab : and_optab, - op00, op01, NULL_RTX, unsignedp, - OPTAB_DIRECT); - - if (tem != 0) - tem = emit_store_flag (NULL_RTX, code, tem, op1, word_mode, - unsignedp, normalizep); - } - else if ((code == LT || code == GE) && op1 == const0_rtx) - { - rtx op0h; - - /* If testing the sign bit, can just test on high word. */ - op0h = simplify_gen_subreg (word_mode, op0, int_mode, - subreg_highpart_offset (word_mode, - int_mode)); - tem = emit_store_flag (NULL_RTX, code, op0h, op1, word_mode, - unsignedp, normalizep); - } - else - tem = NULL_RTX; - - if (tem) - { - if (target_mode == VOIDmode || GET_MODE (tem) == target_mode) - return tem; - if (!target) - target = gen_reg_rtx (target_mode); - - convert_move (target, tem, - !val_signbit_known_set_p (word_mode, - (normalizep ? normalizep - : STORE_FLAG_VALUE))); - return target; - } - } - /* If this is A < 0 or A >= 0, we can do this by taking the ones complement of A (for GE) and shifting the sign bit to the low bit. */ + scalar_int_mode int_mode; if (op1 == const0_rtx && (code == LT || code == GE) && is_int_mode (mode, &int_mode) && (normalizep || STORE_FLAG_VALUE == 1 @@ -5764,6 +5710,7 @@ emit_store_flag_1 (rtx target, enum rtx_code code, rtx op0, rtx op1, return op0; } + /* Next try expanding this via the backend's cstore<mode>4. */ mclass = GET_MODE_CLASS (mode); FOR_EACH_MODE_FROM (compare_mode, mode) { @@ -5788,6 +5735,60 @@ emit_store_flag_1 (rtx target, enum rtx_code code, rtx op0, rtx op1, } } + /* If we are comparing a double-word integer with zero or -1, we can + convert the comparison into one involving a single word. */ + if (is_int_mode (mode, &int_mode) + && GET_MODE_BITSIZE (int_mode) == BITS_PER_WORD * 2 + && (!MEM_P (op0) || ! MEM_VOLATILE_P (op0))) + { + rtx tem; + if ((code == EQ || code == NE) + && (op1 == const0_rtx || op1 == constm1_rtx)) + { + rtx op00, op01; + + /* Do a logical OR or AND of the two words and compare the + result. */ + op00 = simplify_gen_subreg (word_mode, op0, int_mode, 0); + op01 = simplify_gen_subreg (word_mode, op0, int_mode, UNITS_PER_WORD); + tem = expand_binop (word_mode, + op1 == const0_rtx ? ior_optab : and_optab, + op00, op01, NULL_RTX, unsignedp, + OPTAB_DIRECT); + + if (tem != 0) + tem = emit_store_flag (NULL_RTX, code, tem, op1, word_mode, + unsignedp, normalizep); + } + else if ((code == LT || code == GE) && op1 == const0_rtx) + { + rtx op0h; + + /* If testing the sign bit, can just test on high word. */ + op0h = simplify_gen_subreg (word_mode, op0, int_mode, + subreg_highpart_offset (word_mode, + int_mode)); + tem = emit_store_flag (NULL_RTX, code, op0h, op1, word_mode, + unsignedp, normalizep); + } + else + tem = NULL_RTX; + + if (tem) + { + if (target_mode == VOIDmode || GET_MODE (tem) == target_mode) + return tem; + if (!target) + target = gen_reg_rtx (target_mode); + + convert_move (target, tem, + !val_signbit_known_set_p (word_mode, + (normalizep ? normalizep + : STORE_FLAG_VALUE))); + return target; + } + } + return 0; } diff --git a/gcc/gcov-dump.cc b/gcc/gcov-dump.cc index 0804c79..85b1be8 100644 --- a/gcc/gcov-dump.cc +++ b/gcc/gcov-dump.cc @@ -28,6 +28,10 @@ along with Gcov; see the file COPYING3. If not see #include "gcov-io.h" #include "gcov-io.cc" +#include <vector> + +using namespace std; + static void dump_gcov_file (const char *); static void print_prefix (const char *, unsigned, gcov_position_t); static void print_usage (void); @@ -50,6 +54,7 @@ typedef struct tag_format static int flag_dump_contents = 0; static int flag_dump_positions = 0; static int flag_dump_raw = 0; +static int flag_dump_stable = 0; static const struct option options[] = { @@ -57,7 +62,9 @@ static const struct option options[] = { "version", no_argument, NULL, 'v' }, { "long", no_argument, NULL, 'l' }, { "positions", no_argument, NULL, 'o' }, - { 0, 0, 0, 0 } + { "raw", no_argument, NULL, 'r' }, + { "stable", no_argument, NULL, 's' }, + {} }; #define VALUE_PADDING_PREFIX " " @@ -96,7 +103,7 @@ main (int argc ATTRIBUTE_UNUSED, char **argv) diagnostic_initialize (global_dc, 0); - while ((opt = getopt_long (argc, argv, "hlprvw", options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "hlprsvw", options, NULL)) != -1) { switch (opt) { @@ -115,6 +122,9 @@ main (int argc ATTRIBUTE_UNUSED, char **argv) case 'r': flag_dump_raw = 1; break; + case 's': + flag_dump_stable = 1; + break; default: fprintf (stderr, "unknown flag `%c'\n", opt); } @@ -134,6 +144,8 @@ print_usage (void) printf (" -l, --long Dump record contents too\n"); printf (" -p, --positions Dump record positions\n"); printf (" -r, --raw Print content records in raw format\n"); + printf (" -s, --stable Print content in stable " + "format usable for comparison\n"); printf (" -v, --version Print version number\n"); printf ("\nFor bug reporting instructions, please see:\n%s.\n", bug_report_url); @@ -439,16 +451,52 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED, int n_counts = GCOV_TAG_COUNTER_NUM (length); bool has_zeros = n_counts < 0; n_counts = abs (n_counts); + unsigned counter_idx = GCOV_COUNTER_FOR_TAG (tag); printf (" %s %u counts%s", - counter_names[GCOV_COUNTER_FOR_TAG (tag)], n_counts, + counter_names[counter_idx], n_counts, has_zeros ? " (all zero)" : ""); if (flag_dump_contents) { + vector<gcov_type> counters; for (int ix = 0; ix != n_counts; ix++) + counters.push_back (has_zeros ? 0 : gcov_read_counter ()); + + /* Make stable sort for TOP N counters. */ + if (flag_dump_stable) + if (counter_idx == GCOV_COUNTER_V_INDIR + || counter_idx == GCOV_COUNTER_V_TOPN) + { + unsigned start = 0; + while (start < counters.size ()) + { + unsigned n = counters[start + 1]; + + /* Use bubble sort. */ + for (unsigned i = 1; i <= n; ++i) + for (unsigned j = i; j <= n; ++j) + { + gcov_type key1 = counters[start + 2 * i]; + gcov_type value1 = counters[start + 2 * i + 1]; + gcov_type key2 = counters[start + 2 * j]; + gcov_type value2 = counters[start + 2 * j + 1]; + + if (value1 < value2 || (value1 == value2 && key1 < key2)) + { + std::swap (counters[start + 2 * i], + counters[start + 2 * j]); + std::swap (counters[start + 2 * i + 1], + counters[start + 2 * j + 1]); + } + } + start += 2 * (n + 1); + } + if (start != counters.size ()) + abort (); + } + + for (unsigned ix = 0; ix < counters.size (); ++ix) { - gcov_type count; - if (flag_dump_raw) { if (ix == 0) @@ -461,8 +509,7 @@ tag_counters (const char *filename ATTRIBUTE_UNUSED, printf (VALUE_PADDING_PREFIX VALUE_PREFIX, ix); } - count = has_zeros ? 0 : gcov_read_counter (); - printf ("%" PRId64 " ", count); + printf ("%" PRId64 " ", counters[ix]); } } } diff --git a/gcc/gimple-loop-jam.cc b/gcc/gimple-loop-jam.cc index 8cde6c7..a8a57d3 100644 --- a/gcc/gimple-loop-jam.cc +++ b/gcc/gimple-loop-jam.cc @@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-loop-ivopts.h" #include "tree-vectorizer.h" #include "tree-ssa-sccvn.h" +#include "tree-cfgcleanup.h" /* Unroll and Jam transformation @@ -609,9 +610,16 @@ tree_loop_unroll_and_jam (void) if (todo) { + free_dominance_info (CDI_DOMINATORS); + /* We need to cleanup the CFG first since otherwise SSA form can + be not up-to-date from the VN run. */ + if (todo & TODO_cleanup_cfg) + { + cleanup_tree_cfg (); + todo &= ~TODO_cleanup_cfg; + } rewrite_into_loop_closed_ssa (NULL, 0); scev_reset (); - free_dominance_info (CDI_DOMINATORS); } return todo; } diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc index d9e160c..4782d47 100644 --- a/gcc/gimple-range-cache.cc +++ b/gcc/gimple-range-cache.cc @@ -1211,13 +1211,56 @@ ranger_cache::fill_block_cache (tree name, basic_block bb, basic_block def_bb) // Check if a dominators can supply the range. if (range_from_dom (block_result, name, bb, RFD_FILL)) { - m_on_entry.set_bb_range (name, bb, block_result); if (DEBUG_RANGE_CACHE) { fprintf (dump_file, "Filled from dominator! : "); block_result.dump (dump_file); fprintf (dump_file, "\n"); } + // See if any equivalences can refine it. + if (m_oracle) + { + unsigned i; + bitmap_iterator bi; + // Query equivalences in read-only mode. + const_bitmap equiv = m_oracle->equiv_set (name, bb); + EXECUTE_IF_SET_IN_BITMAP (equiv, 0, i, bi) + { + if (i == SSA_NAME_VERSION (name)) + continue; + tree equiv_name = ssa_name (i); + basic_block equiv_bb = gimple_bb (SSA_NAME_DEF_STMT (equiv_name)); + + // Check if the equiv has any ranges calculated. + if (!m_gori.has_edge_range_p (equiv_name)) + continue; + + // Check if the equiv definition dominates this block + if (equiv_bb == bb || + (equiv_bb && !dominated_by_p (CDI_DOMINATORS, bb, equiv_bb))) + continue; + + Value_Range equiv_range (TREE_TYPE (equiv_name)); + if (range_from_dom (equiv_range, equiv_name, bb, RFD_READ_ONLY)) + { + if (block_result.intersect (equiv_range)) + { + if (DEBUG_RANGE_CACHE) + { + fprintf (dump_file, "Equivalence update! : "); + print_generic_expr (dump_file, equiv_name, TDF_SLIM); + fprintf (dump_file, "had range : "); + equiv_range.dump (dump_file); + fprintf (dump_file, " refining range to :"); + block_result.dump (dump_file); + fprintf (dump_file, "\n"); + } + } + } + } + } + + m_on_entry.set_bb_range (name, bb, block_result); gcc_checking_assert (m_workback.length () == 0); return; } diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 6f907df..689d827 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -853,12 +853,13 @@ fold_using_range::range_of_phi (vrange &r, gphi *phi, fur_source &src) } // If SCEV is available, query if this PHI has any knonwn values. - if (scev_initialized_p () && !POINTER_TYPE_P (TREE_TYPE (phi_def))) + if (scev_initialized_p () + && !POINTER_TYPE_P (TREE_TYPE (phi_def))) { - value_range loop_range; class loop *l = loop_containing_stmt (phi); if (l && loop_outer (l)) { + Value_Range loop_range (type); range_of_ssa_name_with_loop_info (loop_range, phi_def, l, phi, src); if (!loop_range.varying_p ()) { @@ -1328,37 +1329,60 @@ fold_using_range::range_of_cond_expr (vrange &r, gassign *s, fur_source &src) return true; } +// Return the lower bound of R as a tree. + +static inline tree +tree_lower_bound (const vrange &r, tree type) +{ + if (is_a <irange> (r)) + return wide_int_to_tree (type, as_a <irange> (r).lower_bound ()); + // ?? Handle floats when they contain endpoints. + return NULL; +} + +// Return the upper bound of R as a tree. + +static inline tree +tree_upper_bound (const vrange &r, tree type) +{ + if (is_a <irange> (r)) + return wide_int_to_tree (type, as_a <irange> (r).upper_bound ()); + // ?? Handle floats when they contain endpoints. + return NULL; +} + // If SCEV has any information about phi node NAME, return it as a range in R. void -fold_using_range::range_of_ssa_name_with_loop_info (irange &r, tree name, +fold_using_range::range_of_ssa_name_with_loop_info (vrange &r, tree name, class loop *l, gphi *phi, fur_source &src) { gcc_checking_assert (TREE_CODE (name) == SSA_NAME); tree min, max, type = TREE_TYPE (name); - // FIXME: Remove the supports_p() once all this can handle floats, etc. - if (irange::supports_p (type) - && bounds_of_var_in_loop (&min, &max, src.query (), l, phi, name)) + if (bounds_of_var_in_loop (&min, &max, src.query (), l, phi, name)) { - if (TREE_CODE (min) != INTEGER_CST) + if (!is_gimple_constant (min)) { if (src.query ()->range_of_expr (r, min, phi) && !r.undefined_p ()) - min = wide_int_to_tree (type, r.lower_bound ()); + min = tree_lower_bound (r, type); else min = vrp_val_min (type); } - if (TREE_CODE (max) != INTEGER_CST) + if (!is_gimple_constant (max)) { if (src.query ()->range_of_expr (r, max, phi) && !r.undefined_p ()) - max = wide_int_to_tree (type, r.upper_bound ()); + max = tree_upper_bound (r, type); else max = vrp_val_max (type); } - r.set (min, max); + if (min && max) + { + r.set (min, max); + return; + } } - else - r.set_varying (type); + r.set_varying (type); } // ----------------------------------------------------------------------- @@ -1472,6 +1496,10 @@ fur_source::register_outgoing_edges (gcond *s, irange &lhs_range, edge e0, edge tree name; basic_block bb = gimple_bb (s); + range_op_handler handler (s); + if (!handler) + return; + if (e0) { // If this edge is never taken, ignore it. @@ -1500,8 +1528,6 @@ fur_source::register_outgoing_edges (gcond *s, irange &lhs_range, edge e0, edge tree ssa2 = gimple_range_ssa_p (gimple_range_operand2 (s)); if (ssa1 && ssa2) { - range_op_handler handler (s); - gcc_checking_assert (handler); if (e0) { relation_kind relation = handler.op1_op2_relation (e0_range); diff --git a/gcc/gimple-range-fold.h b/gcc/gimple-range-fold.h index fbf6627..c2f381d 100644 --- a/gcc/gimple-range-fold.h +++ b/gcc/gimple-range-fold.h @@ -173,7 +173,7 @@ protected: void range_of_builtin_ubsan_call (irange &r, gcall *call, tree_code code, fur_source &src); bool range_of_phi (vrange &r, gphi *phi, fur_source &src); - void range_of_ssa_name_with_loop_info (irange &, tree, class loop *, gphi *, + void range_of_ssa_name_with_loop_info (vrange &, tree, class loop *, gphi *, fur_source &src); void relation_fold_and_or (irange& lhs_range, gimple *s, fur_source &src); }; diff --git a/gcc/gimple-range-path.cc b/gcc/gimple-range-path.cc index e1b9683..43e7526 100644 --- a/gcc/gimple-range-path.cc +++ b/gcc/gimple-range-path.cc @@ -479,32 +479,28 @@ path_range_query::compute_ranges_in_block (basic_block bb) p->set_root_oracle (nullptr); } - EXECUTE_IF_SET_IN_BITMAP (m_imports, 0, i, bi) + gori_compute &g = m_ranger->gori (); + bitmap exports = g.exports (bb); + EXECUTE_IF_AND_IN_BITMAP (m_imports, exports, 0, i, bi) { tree name = ssa_name (i); - gori_compute &g = m_ranger->gori (); - bitmap exports = g.exports (bb); - - if (bitmap_bit_p (exports, i)) + Value_Range r (TREE_TYPE (name)); + if (g.outgoing_edge_range_p (r, e, name, *this)) { - Value_Range r (TREE_TYPE (name)); - if (g.outgoing_edge_range_p (r, e, name, *this)) + Value_Range cached_range (TREE_TYPE (name)); + if (get_cache (cached_range, name)) + r.intersect (cached_range); + + set_cache (r, name); + if (DEBUG_SOLVER) { - Value_Range cached_range (TREE_TYPE (name)); - if (get_cache (cached_range, name)) - r.intersect (cached_range); - - set_cache (r, name); - if (DEBUG_SOLVER) - { - fprintf (dump_file, "outgoing_edge_range_p for "); - print_generic_expr (dump_file, name, TDF_SLIM); - fprintf (dump_file, " on edge %d->%d ", - e->src->index, e->dest->index); - fprintf (dump_file, "is "); - r.dump (dump_file); - fprintf (dump_file, "\n"); - } + fprintf (dump_file, "outgoing_edge_range_p for "); + print_generic_expr (dump_file, name, TDF_SLIM); + fprintf (dump_file, " on edge %d->%d ", + e->src->index, e->dest->index); + fprintf (dump_file, "is "); + r.dump (dump_file); + fprintf (dump_file, "\n"); } } } diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc index fb8f973..ca5b9f3 100644 --- a/gcc/ipa-prop.cc +++ b/gcc/ipa-prop.cc @@ -2303,6 +2303,10 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi, { if (TREE_CODE (arg) == SSA_NAME && param_type + /* Limit the ranger query to integral types as the rest + of this file uses value_range's, which only hold + integers and pointers. */ + && irange::supports_p (TREE_TYPE (arg)) && get_range_query (cfun)->range_of_expr (vr, arg) && !vr.undefined_p ()) { diff --git a/gcc/match.pd b/gcc/match.pd index 330c1db..f82f94a 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -1308,6 +1308,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (simplify (bit_not (plus:c (bit_not @0) @1)) (minus @0 @1)) +/* (~X - ~Y) -> Y - X. */ +(simplify + (minus (bit_not @0) (bit_not @1)) + (with { tree utype = unsigned_type_for (type); } + (convert (minus (convert:utype @1) (convert:utype @0))))) /* ~(X - Y) -> ~X + Y. */ (simplify @@ -3982,6 +3987,12 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (plus:c (mult:c (div @0 @1) @1) (mod @0 @1)) @0)) +/* x / y * y == x -> x % y == 0. */ +(simplify + (eq:c (mult:c (trunc_div:s @0 @1) @1) @0) + (if (TREE_CODE (TREE_TYPE (@0)) != COMPLEX_TYPE) + (eq (trunc_mod @0 @1) { build_zero_cst (TREE_TYPE (@0)); }))) + /* ((X /[ex] A) +- B) * A --> X +- A * B. */ (for op (plus minus) (simplify @@ -8055,3 +8066,8 @@ and, (if (TYPE_UNSIGNED (TREE_TYPE (@0))) (bit_and @0 @1) (cond (le @0 @1) @0 (bit_and @0 @1)))))) + +/* -x & 1 -> x & 1. */ +(simplify + (bit_and (negate @0) integer_onep@1) + (bit_and @0 @1)) diff --git a/gcc/omp-expand.cc b/gcc/omp-expand.cc index 936adff..64e6308 100644 --- a/gcc/omp-expand.cc +++ b/gcc/omp-expand.cc @@ -1530,8 +1530,6 @@ expand_omp_taskreg (struct omp_region *region) expand_teams_call (new_bb, as_a <gomp_teams *> (entry_stmt)); else expand_task_call (region, new_bb, as_a <gomp_task *> (entry_stmt)); - if (gimple_in_ssa_p (cfun)) - update_ssa (TODO_update_ssa_only_virtuals); } /* Information about members of an OpenACC collapsed loop nest. */ @@ -8191,9 +8189,6 @@ expand_omp_for (struct omp_region *region, gimple *inner_stmt) (enum built_in_function) next_ix, sched_arg, inner_stmt); } - - if (gimple_in_ssa_p (cfun)) - update_ssa (TODO_update_ssa_only_virtuals); } /* Expand code for an OpenMP sections directive. In pseudo code, we generate @@ -10591,13 +10586,10 @@ execute_expand_omp (void) expand_omp (root_omp_region); - if (flag_checking && !loops_state_satisfies_p (LOOPS_NEED_FIXUP)) - verify_loop_structure (); - cleanup_tree_cfg (); - omp_free_regions (); - return 0; + return (TODO_cleanup_cfg + | (gimple_in_ssa_p (cfun) ? TODO_update_ssa_only_virtuals : 0)); } /* OMP expansion -- the default pass, run before creation of SSA form. */ diff --git a/gcc/omp-simd-clone.cc b/gcc/omp-simd-clone.cc index 32649bc..58bd68b 100644 --- a/gcc/omp-simd-clone.cc +++ b/gcc/omp-simd-clone.cc @@ -1305,8 +1305,16 @@ simd_clone_adjust (struct cgraph_node *node) build_int_cst (TREE_TYPE (iter1), c)); gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); } + tree shift_cnt_conv = shift_cnt; + if (!useless_type_conversion_p (TREE_TYPE (mask), + TREE_TYPE (shift_cnt))) + { + shift_cnt_conv = make_ssa_name (TREE_TYPE (mask)); + g = gimple_build_assign (shift_cnt_conv, NOP_EXPR, shift_cnt); + gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); + } g = gimple_build_assign (make_ssa_name (TREE_TYPE (mask)), - RSHIFT_EXPR, mask, shift_cnt); + RSHIFT_EXPR, mask, shift_cnt_conv); gsi_insert_after (&gsi, g, GSI_CONTINUE_LINKING); mask = gimple_assign_lhs (g); g = gimple_build_assign (make_ssa_name (TREE_TYPE (mask)), diff --git a/gcc/postreload.cc b/gcc/postreload.cc index d1c99fe..41f61d3 100644 --- a/gcc/postreload.cc +++ b/gcc/postreload.cc @@ -43,7 +43,6 @@ along with GCC; see the file COPYING3. If not see #include "function-abi.h" #include "rtl-iter.h" -static int reload_cse_noop_set_p (rtx); static bool reload_cse_simplify (rtx_insn *, rtx); static void reload_cse_regs_1 (void); static int reload_cse_simplify_set (rtx, rtx_insn *); @@ -74,16 +73,6 @@ reload_cse_regs (rtx_insn *first ATTRIBUTE_UNUSED) } } -/* See whether a single set SET is a noop. */ -static int -reload_cse_noop_set_p (rtx set) -{ - if (cselib_reg_set_mode (SET_DEST (set)) != GET_MODE (SET_DEST (set))) - return 0; - - return rtx_equal_for_cselib_p (SET_DEST (set), SET_SRC (set)); -} - /* Try to simplify INSN. Return true if the CFG may have changed. */ static bool reload_cse_simplify (rtx_insn *insn, rtx testreg) @@ -118,7 +107,7 @@ reload_cse_simplify (rtx_insn *insn, rtx testreg) this out, so it's safer to simplify before we delete. */ count += reload_cse_simplify_set (body, insn); - if (!count && reload_cse_noop_set_p (body)) + if (!count && cselib_redundant_set_p (body)) { if (check_for_inc_dec (insn)) delete_insn_and_edges (insn); @@ -157,7 +146,7 @@ reload_cse_simplify (rtx_insn *insn, rtx testreg) rtx part = XVECEXP (body, 0, i); if (GET_CODE (part) == SET) { - if (! reload_cse_noop_set_p (part)) + if (! cselib_redundant_set_p (part)) break; if (REG_P (SET_DEST (part)) && REG_FUNCTION_VALUE_P (SET_DEST (part))) diff --git a/gcc/profile.cc b/gcc/profile.cc index 08af512..96121d6 100644 --- a/gcc/profile.cc +++ b/gcc/profile.cc @@ -753,7 +753,8 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum) bb->count = profile_count::from_gcov_type (bb_gcov_count (bb)); else bb->count = profile_count::guessed_zero (); - if (dump_file && bb->index >= 0) + + if (dump_file && (dump_flags & TDF_DETAILS) && bb->index >= 0) { double freq1 = cnt.to_sreal_scale (old_entry_cnt).to_double (); double freq2 = bb->count.to_sreal_scale @@ -766,7 +767,7 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum) sum2 += freq2; } } - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) { double nsum1 = 0, nsum2 = 0; stats.qsort (cmp_stats); @@ -776,8 +777,8 @@ compute_branch_probabilities (unsigned cfg_checksum, unsigned lineno_checksum) nsum2 += stat.feedback; fprintf (dump_file, " Basic block %4i guessed freq: %12.3f" - " cummulative:%6.2f%% " - " feedback freq: %12.3f cummulative:%7.2f%%" + " cumulative:%6.2f%% " + " feedback freq: %12.3f cumulative:%7.2f%%" " cnt: 10%" PRId64 "\n", stat.bb->index, stat.guessed, nsum1 * 100 / sum1, diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 8e9d83e..4fbd96a 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -150,6 +150,50 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons return VREL_VARYING; } +// Return TRUE if OP1 and OP2 are known to be free of NANs. + +static inline bool +finite_operands_p (const frange &op1, const frange &op2) +{ + return (flag_finite_math_only + || (op1.get_nan ().no_p () + && op2.get_nan ().no_p ())); +} + +// Floating version of relop_early_resolve that takes into account NAN +// and -ffinite-math-only. + +inline bool +frelop_early_resolve (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel, relation_kind my_rel) +{ + // If either operand is undefined, return VARYING. + if (empty_range_varying (r, type, op1, op2)) + return true; + + // We can fold relations from the oracle when we know both operands + // are free of NANs, or when -ffinite-math-only. + return (finite_operands_p (op1, op2) + && relop_early_resolve (r, type, op1, op2, rel, my_rel)); +} + +// Default implementation of fold_range for relational operators. +// This amounts to passing on any known relations from the oracle, iff +// we know the operands are not NAN or -ffinite-math-only holds. + +static inline bool +default_frelop_fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel, relation_kind my_rel) +{ + if (frelop_early_resolve (r, type, op1, op2, rel, my_rel)) + return true; + + r.set_varying (type); + return true; +} + class foperator_identity : public range_operator_float { using range_operator_float::fold_range; @@ -172,6 +216,509 @@ class foperator_identity : public range_operator_float public: } fop_identity; +class foperator_equal : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override + { + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_EQ); + } + relation_kind op1_op2_relation (const irange &lhs) const final override + { + return equal_op1_op2_relation (lhs); + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override + { + return op1_range (r, type, lhs, op1, rel); + } +} fop_equal; + +bool +foperator_equal::op1_range (frange &r, tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind rel) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 == op2 implies op1 is !NAN. + r.set_nan (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + // The FALSE side of op1 == op1 implies op1 is a NAN. + if (rel == VREL_EQ) + r.set_nan (fp_prop::YES); + break; + + default: + break; + } + return true; +} + +class foperator_not_equal : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override + { + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_NE); + } + relation_kind op1_op2_relation (const irange &lhs) const final override + { + return not_equal_op1_op2_relation (lhs); + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; +} fop_not_equal; + +bool +foperator_not_equal::op1_range (frange &r, tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + break; + + case BRS_FALSE: + r.set_varying (type); + // The FALSE side of op1 != op2 implies op1 is !NAN. + r.set_nan (fp_prop::NO); + break; + + default: + break; + } + return true; +} + +class foperator_lt : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override + { + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LT); + } + relation_kind op1_op2_relation (const irange &lhs) const final override + { + return lt_op1_op2_relation (lhs); + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override; +} fop_lt; + +bool +foperator_lt::op1_range (frange &r, + tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 < op2 implies op1 is !NAN and !INF. + r.set_nan (fp_prop::NO); + r.set_inf (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + break; + + default: + break; + } + return true; +} + +bool +foperator_lt::op2_range (frange &r, + tree type, + const irange &lhs, + const frange &op1 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 < op2 implies op2 is !NAN and !NINF. + r.set_nan (fp_prop::NO); + r.set_ninf (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + break; + + default: + break; + } + return true; +} + +class foperator_le : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override + { + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LE); + } + relation_kind op1_op2_relation (const irange &lhs) const final override + { + return le_op1_op2_relation (lhs); + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override + { + return op1_range (r, type, lhs, op1, rel); + } +} fop_le; + +bool +foperator_le::op1_range (frange &r, + tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 <= op2 implies op1 is !NAN. + r.set_nan (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + break; + + default: + break; + } + return true; +} + +class foperator_gt : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override + { + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GT); + } + relation_kind op1_op2_relation (const irange &lhs) const final override + { + return gt_op1_op2_relation (lhs); + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override; +} fop_gt; + +bool +foperator_gt::op1_range (frange &r, + tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 > op2 implies op1 is !NAN and !NINF. + r.set_nan (fp_prop::NO); + r.set_ninf (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + break; + + default: + break; + } + return true; +} + +bool +foperator_gt::op2_range (frange &r, + tree type, + const irange &lhs, + const frange &op1 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 > op2 implies op2 is !NAN and !INF. + r.set_nan (fp_prop::NO); + r.set_inf (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + break; + + default: + break; + } + return true; +} + +class foperator_ge : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override + { + return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GE); + } + relation_kind op1_op2_relation (const irange &lhs) const final override + { + return ge_op1_op2_relation (lhs); + } + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override + { + return op1_range (r, type, lhs, op1, rel); + } +} fop_ge; + +bool +foperator_ge::op1_range (frange &r, + tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 >= op2 implies op1 is !NAN. + r.set_nan (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + break; + + default: + break; + } + return true; +} + +// UNORDERED_EXPR comparison. + +class foperator_unordered : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + +public: + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override; + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override + { + return op1_range (r, type, lhs, op1, rel); + } +} fop_unordered; + +bool +foperator_unordered::fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind) const +{ + // UNORDERED is TRUE if either operand is a NAN. + if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ()) + r = range_true (type); + // UNORDERED is FALSE if neither operand is a NAN. + else if (op1.get_nan ().no_p () && op2.get_nan ().no_p ()) + r = range_false (type); + else + r = range_true_and_false (type); + return true; +} + +bool +foperator_unordered::op1_range (frange &r, tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // Since at least one operand must be NAN, if one of them is + // not, the other must be. + if (op2.get_nan ().no_p ()) + r.set_nan (fp_prop::YES); + break; + + case BRS_FALSE: + r.set_varying (type); + // A false UNORDERED means both operands are !NAN. + r.set_nan (fp_prop::NO); + break; + + default: + break; + } + return true; +} + +// ORDERED_EXPR comparison. + +class foperator_ordered : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + using range_operator_float::op2_range; + +public: + bool fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind rel) const final override; + bool op1_range (frange &r, tree type, + const irange &lhs, const frange &op2, + relation_kind rel) const final override; + bool op2_range (frange &r, tree type, + const irange &lhs, const frange &op1, + relation_kind rel) const final override + { + return op1_range (r, type, lhs, op1, rel); + } +} fop_ordered; + +bool +foperator_ordered::fold_range (irange &r, tree type, + const frange &op1, const frange &op2, + relation_kind) const +{ + // ORDERED is TRUE if neither operand is a NAN. + if (op1.get_nan ().no_p () && op2.get_nan ().no_p ()) + r = range_true (type); + // ORDERED is FALSE if either operand is a NAN. + else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ()) + r = range_false (type); + else + r = range_true_and_false (type); + return true; +} + +bool +foperator_ordered::op1_range (frange &r, tree type, + const irange &lhs, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind rel) const +{ + switch (get_bool_state (r, lhs, type)) + { + case BRS_TRUE: + r.set_varying (type); + // The TRUE side of op1 ORDERED op2 implies op1 is !NAN. + r.set_nan (fp_prop::NO); + break; + + case BRS_FALSE: + r.set_varying (type); + // The FALSE side of op1 ORDERED op1 implies op1 is !NAN. + if (rel == VREL_EQ) + r.set_nan (fp_prop::NO); + break; + + default: + break; + } + return true; +} + +// Placeholder for unimplemented relational operators. + +class foperator_relop_unknown : public range_operator_float +{ + using range_operator_float::fold_range; + +public: + bool fold_range (irange &r, tree type, + const frange &, const frange &, + relation_kind) const final override + { + r.set_varying (type); + return true; + } +} fop_relop_unknown; + // Instantiate a range_op_table for floating point operations. static floating_op_table global_floating_table; @@ -185,6 +732,23 @@ floating_op_table::floating_op_table () set (PAREN_EXPR, fop_identity); set (OBJ_TYPE_REF, fop_identity); set (REAL_CST, fop_identity); + + // All the relational operators are expected to work, because the + // calculation of ranges on outgoing edges expect the handlers to be + // present. + set (EQ_EXPR, fop_equal); + set (NE_EXPR, fop_not_equal); + set (LT_EXPR, fop_lt); + set (LE_EXPR, fop_le); + set (GT_EXPR, fop_gt); + set (GE_EXPR, fop_ge); + set (UNLE_EXPR, fop_relop_unknown); + set (UNLT_EXPR, fop_relop_unknown); + set (UNGE_EXPR, fop_relop_unknown); + set (UNGT_EXPR, fop_relop_unknown); + set (UNEQ_EXPR, fop_relop_unknown); + set (ORDERED_EXPR, fop_ordered); + set (UNORDERED_EXPR, fop_unordered); } // Return a pointer to the range_operator_float instance, if there is diff --git a/gcc/simplify-rtx.cc b/gcc/simplify-rtx.cc index fa20665..7d09bf7 100644 --- a/gcc/simplify-rtx.cc +++ b/gcc/simplify-rtx.cc @@ -1366,11 +1366,35 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, break; /* If operand is something known to be positive, ignore the ABS. */ - if (GET_CODE (op) == FFS || GET_CODE (op) == ABS - || val_signbit_known_clear_p (GET_MODE (op), - nonzero_bits (op, GET_MODE (op)))) + if (val_signbit_known_clear_p (GET_MODE (op), + nonzero_bits (op, GET_MODE (op)))) return op; + /* Using nonzero_bits doesn't (currently) work for modes wider than + HOST_WIDE_INT, so the following transformations help simplify + ABS for TImode and wider. */ + switch (GET_CODE (op)) + { + case ABS: + case CLRSB: + case FFS: + case PARITY: + case POPCOUNT: + case SS_ABS: + return op; + + case LSHIFTRT: + if (CONST_INT_P (XEXP (op, 1)) + && INTVAL (XEXP (op, 1)) > 0 + && is_a <scalar_int_mode> (mode, &int_mode) + && INTVAL (XEXP (op, 1)) < GET_MODE_PRECISION (int_mode)) + return op; + break; + + default: + break; + } + /* If operand is known to be only -1 or 0, convert ABS to NEG. */ if (is_a <scalar_int_mode> (mode, &int_mode) && (num_sign_bit_copies (op, int_mode) @@ -1615,6 +1639,24 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, } } + /* We can canonicalize SIGN_EXTEND (op) as ZERO_EXTEND (op) when + we know the sign bit of OP must be clear. */ + if (val_signbit_known_clear_p (GET_MODE (op), + nonzero_bits (op, GET_MODE (op)))) + return simplify_gen_unary (ZERO_EXTEND, mode, op, GET_MODE (op)); + + /* (sign_extend:DI (subreg:SI (ctz:DI ...))) is (ctz:DI ...). */ + if (GET_CODE (op) == SUBREG + && subreg_lowpart_p (op) + && GET_MODE (SUBREG_REG (op)) == mode + && is_a <scalar_int_mode> (mode, &int_mode) + && is_a <scalar_int_mode> (GET_MODE (op), &op_mode) + && GET_MODE_PRECISION (int_mode) <= HOST_BITS_PER_WIDE_INT + && GET_MODE_PRECISION (op_mode) < GET_MODE_PRECISION (int_mode) + && (nonzero_bits (SUBREG_REG (op), mode) + & ~(GET_MODE_MASK (op_mode) >> 1)) == 0) + return SUBREG_REG (op); + #if defined(POINTERS_EXTEND_UNSIGNED) /* As we do not know which address space the pointer is referring to, we can do this only if the target does not support different pointer @@ -1765,6 +1807,18 @@ simplify_context::simplify_unary_operation_1 (rtx_code code, machine_mode mode, op0_mode); } + /* (zero_extend:DI (subreg:SI (ctz:DI ...))) is (ctz:DI ...). */ + if (GET_CODE (op) == SUBREG + && subreg_lowpart_p (op) + && GET_MODE (SUBREG_REG (op)) == mode + && is_a <scalar_int_mode> (mode, &int_mode) + && is_a <scalar_int_mode> (GET_MODE (op), &op_mode) + && GET_MODE_PRECISION (int_mode) <= HOST_BITS_PER_WIDE_INT + && GET_MODE_PRECISION (op_mode) < GET_MODE_PRECISION (int_mode) + && (nonzero_bits (SUBREG_REG (op), mode) + & ~GET_MODE_MASK (op_mode)) == 0) + return SUBREG_REG (op); + #if defined(POINTERS_EXTEND_UNSIGNED) /* As we do not know which address space the pointer is referring to, we can do this only if the target does not support different pointer diff --git a/gcc/symtab.cc b/gcc/symtab.cc index 8670337..f2d96c0 100644 --- a/gcc/symtab.cc +++ b/gcc/symtab.cc @@ -894,7 +894,8 @@ symtab_node::dump_base (FILE *f) }; fprintf (f, "%s (%s)", dump_asm_name (), name ()); - dump_addr (f, " @", (void *)this); + if (dump_flags & TDF_ADDRESS) + dump_addr (f, " @", (void *)this); fprintf (f, "\n Type: %s", symtab_type_names[type]); if (definition) diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 781bba7..fee24d2 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,161 @@ +2022-08-07 Roger Sayle <roger@nextmovesoftware.com> + + * gcc.target/i386/cmpti2.c: Add -mno-stv to dg-options. + +2022-08-07 Jakub Jelinek <jakub@redhat.com> + + PR c++/88174 + * g++.dg/cpp1y/constexpr-complex1.C: New test. + +2022-08-07 Roger Sayle <roger@nextmovesoftware.com> + + * gcc.target/i386/cmpti1.c: New test case. + * gcc.target/i386/cmpti2.c: Likewise. + * gcc.target/i386/cmpti3.c: Likewise. + +2022-08-05 David Malcolm <dmalcolm@redhat.com> + + PR analyzer/105947 + * gcc.dg/analyzer/function-ptr-5.c: New test. + +2022-08-05 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106533 + * gcc.dg/tree-ssa/ldist-39.c: New testcase. + +2022-08-05 Haochen Gui <guihaoc@gcc.gnu.org> + + * lib/target-supports.exp (check_p9modulo_hw_available): Correct return + value. + +2022-08-04 Tamar Christina <tamar.christina@arm.com> + + * gcc.dg/subnot.c: New test. + +2022-08-04 Tamar Christina <tamar.christina@arm.com> + + PR middle-end/106519 + * gcc.dg/pr106519.c: New test. + +2022-08-04 Sam Feifer <sfeifer@redhat.com> + + PR tree-optimization/106243 + * gcc.dg/pr106243-1.c: New test. + * gcc.dg/pr106243.c: New test. + +2022-08-04 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106521 + * gcc.dg/torture/pr106521.c: New testcase. + +2022-08-03 Jose E. Marchesi <jose.marchesi@oracle.com> + + PR testsuite/106515 + * gcc.dg/debug/btf/btf-int-1.c: Fix regexps in + scan-assembler-times. + +2022-08-03 Tamar Christina <tamar.christina@arm.com> + + * gcc.dg/tree-ssa/split-path-1.c: Disable phi-opts so we don't optimize + code away. + * gcc.dg/tree-ssa/minmax-10.c: New test. + * gcc.dg/tree-ssa/minmax-11.c: New test. + * gcc.dg/tree-ssa/minmax-12.c: New test. + * gcc.dg/tree-ssa/minmax-13.c: New test. + * gcc.dg/tree-ssa/minmax-14.c: New test. + * gcc.dg/tree-ssa/minmax-15.c: New test. + * gcc.dg/tree-ssa/minmax-16.c: New test. + * gcc.dg/tree-ssa/minmax-3.c: New test. + * gcc.dg/tree-ssa/minmax-4.c: New test. + * gcc.dg/tree-ssa/minmax-5.c: New test. + * gcc.dg/tree-ssa/minmax-6.c: New test. + * gcc.dg/tree-ssa/minmax-7.c: New test. + * gcc.dg/tree-ssa/minmax-8.c: New test. + * gcc.dg/tree-ssa/minmax-9.c: New test. + +2022-08-03 Roger Sayle <roger@nextmovesoftware.com> + Uroš Bizjak <ubizjak@gmail.com> + + PR target/47949 + * gcc.target/i386/pr47949.c: New test case. + +2022-08-03 Roger Sayle <roger@nextmovesoftware.com> + + * gcc.target/i386/sse4_1-stv-7.c: New test case. + +2022-08-02 Andrew MacLeod <amacleod@redhat.com> + + PR tree-optimization/106510 + * gcc.dg/pr106510.c: New. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * gcc.dg/tree-ssa/vrp-float-1.c: Adjust test so it passes. + +2022-08-02 Andrew MacLeod <amacleod@redhat.com> + + PR tree-optimization/106474 + * g++.dg/pr106474.C: New. + +2022-08-02 Jose E. Marchesi <jose.marchesi@oracle.com> + + * gcc.dg/debug/btf/btf-int-1.c: Do not check for char bits in + bti_encoding and check for bool bits. + +2022-08-02 Immad Mir <mirimmad@outlook.com> + + PR analyzer/106298 + * gcc.dg/analyzer/fd-1.c: Add tests for 'creat'. + * gcc.dg/analyzer/fd-2.c: Likewise. + * gcc.dg/analyzer/fd-4.c: Likewise. + * gcc.dg/analyzer/fd-dup-1.c: New tests. + +2022-08-02 Aldy Hernandez <aldyh@redhat.com> + + * g++.dg/opt/pr94589-2.C: XFAIL. + * gcc.dg/tree-ssa/vrp-float-1.c: New test. + * gcc.dg/tree-ssa/vrp-float-11.c: New test. + * gcc.dg/tree-ssa/vrp-float-3.c: New test. + * gcc.dg/tree-ssa/vrp-float-4.c: New test. + * gcc.dg/tree-ssa/vrp-float-6.c: New test. + * gcc.dg/tree-ssa/vrp-float-7.c: New test. + * gcc.dg/tree-ssa/vrp-float-8.c: New test. + +2022-08-02 Richard Biener <rguenther@suse.de> + + PR tree-optimization/106497 + * gcc.dg/torture/pr106497.c: New testcase. + +2022-08-01 David Malcolm <dmalcolm@redhat.com> + + * gcc.target/i386/addr-space-typeck-1.c: New test. + * gcc.target/i386/addr-space-typeck-2.c: New test. + +2022-08-01 Roger Sayle <roger@nextmovesoftware.com> + Uroš Bizjak <ubizjak@gmail.com> + + PR target/106481 + * gcc.target/i386/pr106481.c: New test case. + +2022-08-01 H.J. Lu <hjl.tools@gmail.com> + + PR target/83782 + * gcc.target/i386/pr83782-1.c: Require non-ia32. + * gcc.target/i386/pr83782-2.c: Likewise. + * gcc.target/i386/pr83782-3.c: New test. + +2022-08-01 Jose E. Marchesi <jose.marchesi@oracle.com> + + PR debug/106263 + * gcc.dg/debug/btf/btf-function-4.c: New test. + * gcc.dg/debug/btf/btf-function-5.c: Likewise. + +2022-08-01 Sam Feifer <sfeifer@redhat.com> + + PR tree-optimization/104992 + * g++.dg/pr104992-1.C: New test. + * gcc.dg/pr104992.c: New test. + 2022-07-31 Roger Sayle <roger@nextmovesoftware.com> H.J. Lu <hjl.tools@gmail.com> diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C new file mode 100644 index 0000000..8bb24cb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-complex1.C @@ -0,0 +1,24 @@ +// PR c++/88174 +// { dg-do compile { target c++14 } } + +constexpr bool +foo (double x, double y, double z, double w) +{ + __complex__ double a = 0; + __real__ a = x; + __imag__ a = y; +#if __cpp_constexpr >= 201907L + __complex__ double b; + __real__ b = z; +#else + __complex__ double b = z; +#endif + __imag__ b = w; + a += b; + a -= b; + a *= b; + a /= b; + return __real__ a == x && __imag__ a == y; +} + +static_assert (foo (1.0, 2.0, 3.0, 4.0), ""); diff --git a/gcc/testsuite/g++.dg/opt/pr94589-2.C b/gcc/testsuite/g++.dg/opt/pr94589-2.C index e9ef84b..370deea 100644 --- a/gcc/testsuite/g++.dg/opt/pr94589-2.C +++ b/gcc/testsuite/g++.dg/opt/pr94589-2.C @@ -1,7 +1,7 @@ // PR tree-optimization/94589 // { dg-do compile { target c++20 } } // { dg-options "-O2 -g0 -ffast-math -fdump-tree-optimized" } -// { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" } } +// { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" { xfail *-*-* } } } // { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) 5\\.0" 12 "optimized" } } #include <compare> diff --git a/gcc/testsuite/g++.dg/pr104992-1.C b/gcc/testsuite/g++.dg/pr104992-1.C new file mode 100644 index 0000000..f5696b2 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr104992-1.C @@ -0,0 +1,30 @@ +/* PR tree-optimization/104992 */ +/* { dg-do run } */ +/* { dg-options "-O2"} */ + +#include "../gcc.dg/pr104992.c" + +int main () { + + /* Should be true. */ + if (!foo(6, 3) + || !bar(12, 2) + || !baz(34, 17) + || !qux(50, 10) + || !fred(16, 8) + || !baz(-9, 3) + || !baz(9, -3) + || !baz(-9, -3) + ) { + __builtin_abort(); + } + + /* Should be false. */ + if (foo(5, 30) + || bar(72, 27) + || baz(42, 15)) { + __builtin_abort(); + } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/pr106474.C b/gcc/testsuite/g++.dg/pr106474.C new file mode 100644 index 0000000..6cd37a2 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr106474.C @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-evrp " } */ + +void foo(); +static void __attribute__ ((noinline)) DCEMarker0_() {foo ();} + +void f(bool s, bool c) { + if ((!c == !s) && !c) { + if (s) { + DCEMarker0_(); + } + } +} + +// With equivalences, vrp should be able to remove all IFs. +/* { dg-final { scan-tree-dump-not "goto" "evrp" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr93776.c b/gcc/testsuite/gcc.c-torture/compile/pr93776.c index c407a62..3852736 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr93776.c +++ b/gcc/testsuite/gcc.c-torture/compile/pr93776.c @@ -1,5 +1,5 @@ -/* { dg-do compile } */ -/* { dg-options "-O1" } */ +/* This used to ICE in SRA as SRA got + confused by the zero signed assigment. */ struct empty {}; struct s { int i; }; diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-1.c b/gcc/testsuite/gcc.dg/analyzer/fd-1.c index 8a72e63..5b85a33 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-1.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-1.c @@ -3,6 +3,13 @@ int open(const char *, int mode); #define O_WRONLY 1 #define O_RDWR 2 +typedef enum { + S_IRWXU + // etc +} mode_t; + +int creat (const char *, mode_t mode); + void test_1 (const char *path) { @@ -37,3 +44,17 @@ void test_4 (const char *path) /* { dg-message "\\(1\\) leaks here" "" { target *-*-* } .-1 } */ } +void +test_5 (const char *path, mode_t mode) +{ + creat (path, mode); /* { dg-warning "leak of file descriptor \\\[CWE-775\\\]" } */ +} + +void +test_6 (const char *path, mode_t mode) +{ + int fd = creat (path, mode); + return; /* { dg-warning "leak of file descriptor 'fd' \\\[CWE-775\\\]" } */ +} + + diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-2.c b/gcc/testsuite/gcc.dg/analyzer/fd-2.c index d794b46..10c9ecd 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-2.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-2.c @@ -5,6 +5,13 @@ void close(int fd); #define O_RDWR 2 #define STDIN 0 +typedef enum { + S_IRWXU + // etc +} mode_t; + +int creat (const char *, mode_t mode); + void test_1 (const char *path) { @@ -46,4 +53,12 @@ test_4 () int fd = -1; close(fd); close(fd); +} + +void +test_5 (const char *path, mode_t mode) +{ + int fd = creat (path, mode); + close(fd); + close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ }
\ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-4.c b/gcc/testsuite/gcc.dg/analyzer/fd-4.c index ecd787c..6b8fca5 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-4.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-4.c @@ -9,6 +9,12 @@ int read (int fd, void *buf, int nbytes); #define O_WRONLY 1 #define O_RDWR 2 +typedef enum { + S_IRWXU + // etc +} mode_t; + +int creat (const char *, mode_t mode); void test_1 (const char *path, void *buf) @@ -69,4 +75,27 @@ test_5 (const char *path) int fd = open (path, O_RDWR); close(fd); printf("%d", fd); /* { dg-bogus "'printf' on a closed file descriptor 'fd'" } */ -}
\ No newline at end of file +} + + +void +test_6 (const char *path, mode_t mode, void *buf) +{ + int fd = creat (path, mode); + if (fd != -1) + { + read (fd, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'fd'" } */ + close(fd); + } +} + +void +test_7 (const char *path, mode_t mode, void *buf) +{ + int fd = creat (path, mode); + if (fd != -1) + { + write (fd, buf, 1); + close(fd); + } +} diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-dup-1.c b/gcc/testsuite/gcc.dg/analyzer/fd-dup-1.c new file mode 100644 index 0000000..eba2570 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/fd-dup-1.c @@ -0,0 +1,223 @@ +int open(const char *, int mode); +void close(int fd); +int dup (int old_fd); +int dup2 (int old_fd, int new_fd); +int dup3 (int old_fd, int new_fd, int flags); +int write (int fd, void *buf, int nbytes); +int read (int fd, void *buf, int nbytes); +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 + +void test_1 (const char *path) +{ + int old_fd = open (path, O_RDWR); + int new_fd = dup (old_fd); /* { dg-warning "'dup' on possibly invalid file descriptor 'old_fd'" } */ + close(old_fd); + close(new_fd); +} + +void test_2 (const char *path) +{ + int old_fd = open (path, O_RDWR); + if (old_fd != -1) + { + int new_fd = dup (old_fd); + close(old_fd); + return; /* { dg-warning "leak of file descriptor 'new_fd' \\\[CWE-775\\\]" } */ + } +} + +void test_3 (const char *path, void *buf) +{ + int old_fd = open (path, O_RDWR); + if (old_fd != -1) + { + int new_fd = dup (old_fd); + write (new_fd, buf, 1); /* { dg-warning "'write' on possibly invalid file descriptor 'new_fd'" } */ + close (new_fd); + close(old_fd); + } +} + + +void test_5 (const char *path, void *buf) +{ + int old_fd = open (path, O_RDWR); + if (old_fd != -1) + { + int new_fd = dup (old_fd); + if (new_fd != -1) + { + write (new_fd, buf, 1); + close (new_fd); + + } + close(old_fd); + } +} + + +void test_7 (const char *path) +{ + int old_fd = open (path, O_RDWR); + dup2 (old_fd, 4); /* { dg-warning "'dup2' on possibly invalid file descriptor 'old_fd'" } */ + close(old_fd); +} + +void test_8 (const char *path) +{ + int old_fd = open (path, O_RDWR); + int new_fd = open (path, O_RDWR); + if (old_fd != -1) + { + dup2 (old_fd, new_fd); /* { dg-warning "'dup2' on possibly invalid file descriptor 'new_fd'" } */ + close (old_fd); + } + close (new_fd); +} + +void test_9 (const char *path, void *buf) +{ + int old_fd = open (path, O_RDWR); + + if (old_fd != -1) + { + int new_fd = open (path, O_RDWR); + if (new_fd != -1) + { + int lhs = dup2 (old_fd, new_fd); + write (lhs, buf, 1); /* { dg-warning "'write' on possibly invalid file descriptor 'lhs'" } */ + close(new_fd); + close(lhs); + } + close(old_fd); + } +} + +void test_10 (const char *path, int flags) +{ + int old_fd = open (path, O_RDWR); + int new_fd = open (path, O_RDWR); + if (old_fd != -1) + { + dup3 (old_fd, new_fd, flags); /* { dg-warning "'dup3' on possibly invalid file descriptor 'new_fd'" } */ + close(old_fd); + + } + close(new_fd); +} + +void test_11 (const char *path, int flags) +{ + int old_fd = open (path, O_RDWR); + int new_fd = open (path, O_RDWR); + if (new_fd != -1) + { + dup3 (old_fd, new_fd, flags); /* { dg-warning "'dup3' on possibly invalid file descriptor 'old_fd'" } */ + close(new_fd); + + } + close(old_fd); +} + +void test_12 (const char *path, void *buf) +{ + int old_fd = open (path, O_RDONLY); + if (old_fd != -1) + { + int new_fd = dup (old_fd); + if (new_fd != -1) + { + write (new_fd, buf, 1); /* { dg-warning "'write' on read-only file descriptor 'new_fd'" } */ + close(new_fd); + } + close(old_fd); + } +} + +void test_13 (const char *path, void *buf) +{ + int old_fd = open (path, O_WRONLY); + if (old_fd != -1) + { + int new_fd = dup (old_fd); + if (new_fd != -1) + { + read (new_fd, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'new_fd'" } */ + close(new_fd); + } + close(old_fd); + } +} + +void test_14 (const char *path, void *buf) +{ + int old_fd = open (path, O_RDWR); + if (old_fd != -1) + { + int new_fd = dup (old_fd); + if (new_fd != -1) + { + write (new_fd, buf, 1); + read (new_fd, buf, 1); + close(new_fd); + } + close(old_fd); + } +} + +void test_15 (void *buf) +{ + int fd = dup(0); + read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */ + close(fd); +} + +void test_16 (void *buf) +{ + int fd = dup(1); + if (fd != -1) + { + write (fd, buf, 1); + close (fd); + } +} + +void test_17 (const char *path) +{ + int fd = open (path, O_RDWR); + close(fd); + dup (fd); /* { dg-warning "'dup' on closed file descriptor 'fd'" } */ + dup2 (fd, 4); /* { dg-warning "'dup2' on closed file descriptor 'fd'" } */ +} + +void +test_18 (const char *path, void *buf) +{ + int fd = open (path, O_RDWR); + if (fd != -1) + { + int fd2 = dup2 (fd, 3); + read (fd2, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd2'" } */ + close(fd); + close(fd2); + } +} + +void +test_19 (const char *path, void *buf) +{ + int fd = open (path, O_WRONLY); + if (fd != -1) + { + int fd2 = dup2 (fd, 4); + if (fd2 != -1) + { + read (fd2, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'fd2'" } */ + close(fd2); + } + close (fd); + } + +}
\ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c b/gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c new file mode 100644 index 0000000..3c46f28 --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/function-ptr-5.c @@ -0,0 +1,42 @@ +#define NULL ((void *)0) + +void calling_null_fn_ptr_1 (void) +{ + void (*fn_ptr) (void) = NULL; + fn_ptr (); /* { dg-warning "jump through null pointer" } */ +} + +int calling_null_fn_ptr_2 (void) +{ + int (*fn_ptr) (void) = NULL; + return fn_ptr (); /* { dg-warning "jump through null pointer" } */ +} + +typedef void (*void_void_fn_ptr) (void); + +void calling_const_fn_ptr (void) +{ + void_void_fn_ptr fn_ptr = (void_void_fn_ptr)0xffd2; + return fn_ptr (); +} + +void skipping_init (int flag) +{ + void_void_fn_ptr fn_ptr = NULL; + if (flag) /* { dg-message "branch" } */ + fn_ptr = (void_void_fn_ptr)0xffd2; + fn_ptr (); /* { dg-warning "jump through null pointer" } */ +} + +struct callbacks +{ + void_void_fn_ptr on_redraw; + void_void_fn_ptr on_cleanup; +}; + +void test_callbacks (void) +{ + struct callbacks cb; + __builtin_memset (&cb, 0, sizeof (cb)); + cb.on_cleanup (); /* { dg-warning "jump through null pointer" } */ +} diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-function-4.c b/gcc/testsuite/gcc.dg/debug/btf/btf-function-4.c new file mode 100644 index 0000000..fd31244 --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-function-4.c @@ -0,0 +1,14 @@ +/* Test BTF linkage for functions. + + We expect to see one BTF_KIND_FUNC type with static linkage encoded in the + BTF type's vlen field. */ + +/* { dg-do compile } */ +/* { dg-options "-O0 -gbtf -dA" } */ + +/* { dg-final { scan-assembler-times "btt_info: kind=12, kflag=0, linkage=0" 1 } } */ + +static int funfoo (void) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-function-5.c b/gcc/testsuite/gcc.dg/debug/btf/btf-function-5.c new file mode 100644 index 0000000..12ee97f --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-function-5.c @@ -0,0 +1,14 @@ +/* Test BTF linkage for functions. + + We expect to see one BTF_KIND_FUNC type with global linkage encoded in the + BTF type's vlen field. */ + +/* { dg-do compile } */ +/* { dg-options "-O0 -gbtf -dA" } */ + +/* { dg-final { scan-assembler-times "btt_info: kind=12, kflag=0, linkage=1" 1 } } */ + +int funfoo (void) +{ + return 0; +} diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-int-1.c b/gcc/testsuite/gcc.dg/debug/btf/btf-int-1.c index 2381dec..e1ed198 100644 --- a/gcc/testsuite/gcc.dg/debug/btf/btf-int-1.c +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-int-1.c @@ -4,7 +4,8 @@ | 0 | encoding | offset | 00 | bits | encoding: signed 1 << 24 - char 2 << 24 + char 2 << 24 (not used) + bool 4 << 24 All offsets in this test should be 0. This test does _not_ check number of bits, as it may vary between targets. @@ -13,13 +14,14 @@ /* { dg-do compile } */ /* { dg-options "-O0 -gbtf -dA" } */ -/* Check for 8 BTF_KIND_INT types. */ -/* { dg-final { scan-assembler-times "\[\t \]0x1000000\[\t \]+\[^\n\]*btt_info" 8 } } */ +/* Check for 9 BTF_KIND_INT types. */ +/* { dg-final { scan-assembler-times "\[\t \]0x1000000\[\t \]+\[^\n\]*btt_info" 9 } } */ -/* Check the signed/char flags, but not bit size. */ -/* { dg-final { scan-assembler-times "\[\t \]0x10000..\[\t \]+\[^\n\]*bti_encoding" 3 } } */ -/* { dg-final { scan-assembler-times "\[\t \]0x20000..\[\t \]+\[^\n\]*bti_encoding" 1 } } */ -/* { dg-final { scan-assembler-times "\[\t \]0x30000..\[\t \]+\[^\n\]*bti_encoding" 1 } } */ +/* Check the signed flags, but not bit size. */ +/* { dg-final { scan-assembler-times "\[\t \]0x10000\[0-9a-zA-Z\]{2}\[\t \]+\[^\n\]*bti_encoding" 4 } } */ +/* { dg-final { scan-assembler-times "\[\t \]0x\[0-9a-zA-Z\]{2}\[\t \]+\[^\n\]*bti_encoding" 3 } } */ +/* { dg-final { scan-assembler-times "\[\t \]0x\[0-9a-zA-Z\]\[\t \]+\[^\n\]*bti_encoding" 1 } } */ +/* { dg-final { scan-assembler-times "\[\t \]0x40000\[0-9a-zA-Z\]{2}\[\t \]+\[^\n\]*bti_encoding" 1 } } */ /* Check that there is a string entry for each type name. */ /* { dg-final { scan-assembler-times "ascii \"unsigned char.0\"\[\t \]+\[^\n\]*btf_string" 1 } } */ @@ -42,3 +44,5 @@ signed int f = -66; unsigned long int g = 77; signed long int h = 88; + +_Bool x = 1; diff --git a/gcc/testsuite/gcc.dg/pr104992.c b/gcc/testsuite/gcc.dg/pr104992.c new file mode 100644 index 0000000..b9d91a1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr104992.c @@ -0,0 +1,57 @@ +/* PR tree-optimization/104992 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +#define vector __attribute__((vector_size(4*sizeof(int)))) + +/* Form from PR. */ +__attribute__((noipa)) unsigned foo(unsigned x, unsigned y) +{ + return x / y * y == x; +} + +__attribute__((noipa)) unsigned bar(unsigned x, unsigned y) { + return x == x / y * y; +} + +/* Signed test case. */ +__attribute__((noipa)) unsigned baz (int x, int y) { + return x / y * y == x; +} + +/* Changed order. */ +__attribute__((noipa)) unsigned qux (unsigned x, unsigned y) { + return y * (x / y) == x; +} + +/* Test for forward propogation. */ +__attribute__((noipa)) unsigned corge(unsigned x, unsigned y) { + int z = x / y; + int q = z * y; + return q == x; +} + +/* Test vector case. */ +__attribute__((noipa)) vector int thud(vector int x, vector int y) { + return x / y * y == x; +} + +/* Complex type should not simplify because mod is different. */ +__attribute__((noipa)) int goo(_Complex int x, _Complex int y) +{ + _Complex int z = x / y; + _Complex int q = z * y; + return q == x; +} + +/* Wrong order. */ +__attribute__((noipa)) unsigned fred (unsigned x, unsigned y) { + return y * x / y == x; +} + +/* Wrong pattern. */ +__attribute__((noipa)) unsigned waldo (unsigned x, unsigned y, unsigned z) { + return x / y * z == x; +} + +/* { dg-final {scan-tree-dump-times " % " 9 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/pr106243-1.c b/gcc/testsuite/gcc.dg/pr106243-1.c new file mode 100644 index 0000000..b1dbe5c --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr106243-1.c @@ -0,0 +1,18 @@ +/* PR tree-optimization/106243 */ +/* { dg-do run } */ +/* { dg-options "-O2" } */ + +#include "pr106243.c" + +int main () { + + if (foo(3) != 1 + || bar(-6) != 0 + || baz(17) != 1 + || qux(-128) != 0 + || foo(127) != 1) { + __builtin_abort(); + } + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/pr106243.c b/gcc/testsuite/gcc.dg/pr106243.c new file mode 100644 index 0000000..ee2706f --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr106243.c @@ -0,0 +1,43 @@ +/* PR tree-optimization/106243 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +#define vector __attribute__((vector_size(4*sizeof(int)))) + +/* Test from PR. */ +__attribute__((noipa)) int foo (int x) { + return -x & 1; +} + +/* Other test from PR. */ +__attribute__((noipa)) int bar (int x) { + return (0 - x) & 1; +} + +/* Forward propogation. */ +__attribute__((noipa)) int baz (int x) { + x = -x; + return x & 1; +} + +/* Commutative property. */ +__attribute__((noipa)) int qux (int x) { + return 1 & -x; +} + +/* Vector test case. */ +__attribute__((noipa)) vector int waldo (vector int x) { + return -x & 1; +} + +/* Should not simplify. */ +__attribute__((noipa)) int thud (int x) { + return -x & 2; +} + +/* Should not simplify. */ +__attribute__((noipa)) int corge (int x) { + return -x & -1; +} + +/* { dg-final {scan-tree-dump-times "-" 2 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/pr106510.c b/gcc/testsuite/gcc.dg/pr106510.c new file mode 100644 index 0000000..24e9112 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr106510.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +void foo (); +void ine_ok() { + float y, x; + if (x < y || x > y || y) + foo (); +} + diff --git a/gcc/testsuite/gcc.dg/pr106519.c b/gcc/testsuite/gcc.dg/pr106519.c new file mode 100644 index 0000000..3d4662d --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr106519.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ + +int bytestart, bytemem_0_0, modlookup_l_p; + +void +modlookup_l() { + long j; + while (modlookup_l_p) + while (bytestart && j && bytemem_0_0) + j = j + 1; +} diff --git a/gcc/testsuite/gcc.dg/subnot.c b/gcc/testsuite/gcc.dg/subnot.c new file mode 100644 index 0000000..d621bac --- /dev/null +++ b/gcc/testsuite/gcc.dg/subnot.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +float g(float a, float b) +{ + return ~(int)a - ~(int)b; +} + +/* { dg-final { scan-tree-dump-not "~" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/pr106497.c b/gcc/testsuite/gcc.dg/torture/pr106497.c new file mode 100644 index 0000000..601200d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr106497.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fno-tree-dce" } */ + +int n; + +__attribute__ ((pure,returns_twice)) int +bar (void); + +int +foo (int x) +{ + n = 0; + + bar (); + + if (x && n) + return 0; + + foo (x); +} diff --git a/gcc/testsuite/gcc.dg/torture/pr106521.c b/gcc/testsuite/gcc.dg/torture/pr106521.c new file mode 100644 index 0000000..05c8ce5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr106521.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-floop-unroll-and-jam --param unroll-jam-min-percent=0" } */ + +short a, b, e; +volatile long c; +long d; +int main() { + for (; d; d++) { + long g = a = 1; + for (; a; a++) { + g++; + c; + } + g && (b = e); + } + return 0; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ldist-39.c b/gcc/testsuite/gcc.dg/tree-ssa/ldist-39.c new file mode 100644 index 0000000..3ef7286 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/ldist-39.c @@ -0,0 +1,16 @@ +/* PR/106533 */ +/* { dg-options "-O2 -fdump-tree-ldist-optimized" } */ + +void bar (int *a, int * __restrict b) +{ + for (int k = 0; k < 10; k++) + { + for (int j = 0; j < 100000; ++j) + a[j] = b[j]; + __builtin_printf ("Foo!"); + } +} + +/* The stmt with side-effects in the outer loop should not prevent + distribution of the inner loop of the loop nest. */ +/* { dg-final { scan-tree-dump "optimized: Loop . distributed: split to 0 loops and 1 library calls" "ldist" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-10.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-10.c new file mode 100644 index 0000000..5899536 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-10.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +#include <stdint.h> + +uint8_t three_max (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + xc=~xc; + xm=~xm; + xy=~xy; + if (xc > xm) { + xk = (uint8_t) (xc > xy ? xc : xy); + } else { + xk = (uint8_t) (xm > xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 2 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "= ~" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-11.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-11.c new file mode 100644 index 0000000..1c2ef01 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-11.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +#include <stdint.h> + +uint8_t three_minmax1 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + xc=~xc; + xm=~xm; + xy=~xy; + if (xc > xm) { + xk = (uint8_t) (xc < xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "= ~" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-12.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-12.c new file mode 100644 index 0000000..3d0c07d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-12.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_minmax3 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + xc=~xc; + xm=~xm; + xy=~xy; + if (xc > xm) { + xk = (uint8_t) (xy < xc ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-13.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-13.c new file mode 100644 index 0000000..c0d0f27 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-13.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_minmax2 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + xc=~xc; + xm=~xm; + xy=~xy; + if (xc > xm) { + xk = (uint8_t) (xc > xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-14.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-14.c new file mode 100644 index 0000000..9c0cadb --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-14.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +#include <stdint.h> + +uint8_t three_minmax11 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + xc=~xc; + xm=~xm; + xy=~xy; + if (xc < xm) { + xk = (uint8_t) (xc > xy ? xc : xy); + } else { + xk = (uint8_t) (xm > xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "= ~" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-15.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-15.c new file mode 100644 index 0000000..1d97a16 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-15.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> +#include <stdbool.h> + +uint8_t three_min (uint8_t xc, uint8_t xm, uint8_t xy, bool m) { + uint8_t xk; + if (xc) + { + if (xc < xm) { + xk = (uint8_t) (xc < xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + } + + return xk; +} +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 3 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 0 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-16.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-16.c new file mode 100644 index 0000000..89377a2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-16.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt -g" } */ + +#include <stdint.h> + +uint8_t three_min (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc < xm) { + xk = (uint8_t) (xc < xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 3 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 0 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-3.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-3.c new file mode 100644 index 0000000..de3b2e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-3.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_min (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc < xm) { + xk = (uint8_t) (xc < xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 3 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 0 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-4.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-4.c new file mode 100644 index 0000000..0b6d667 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-4.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_max (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc > xm) { + xk = (uint8_t) (xc > xy ? xc : xy); + } else { + xk = (uint8_t) (xm > xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 0 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 3 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-5.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-5.c new file mode 100644 index 0000000..650601a --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-5.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_minmax1 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc > xm) { + xk = (uint8_t) (xc < xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 2 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-6.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-6.c new file mode 100644 index 0000000..a628f6d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-6.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_minmax3 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc > xm) { + xk = (uint8_t) (xy < xc ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-7.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-7.c new file mode 100644 index 0000000..cb42412 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-7.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_minmax2 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc > xm) { + xk = (uint8_t) (xc > xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-8.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-8.c new file mode 100644 index 0000000..9cd050e --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-8.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-phiopt" } */ + +#include <stdint.h> + +uint8_t three_minmax11 (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + if (xc < xm) { + xk = (uint8_t) (xc > xy ? xc : xy); + } else { + xk = (uint8_t) (xm > xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 2 "phiopt1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/minmax-9.c b/gcc/testsuite/gcc.dg/tree-ssa/minmax-9.c new file mode 100644 index 0000000..24f5802 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/minmax-9.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +#include <stdint.h> + +uint8_t three_min (uint8_t xc, uint8_t xm, uint8_t xy) { + uint8_t xk; + xc=~xc; + xm=~xm; + xy=~xy; + if (xc < xm) { + xk = (uint8_t) (xc < xy ? xc : xy); + } else { + xk = (uint8_t) (xm < xy ? xm : xy); + } + return xk; +} + +/* { dg-final { scan-tree-dump-times "= ~" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "MAX_EXPR" 2 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/split-path-1.c b/gcc/testsuite/gcc.dg/tree-ssa/split-path-1.c index 8b23ef4..902dde4 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/split-path-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/split-path-1.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details --param max-jump-thread-duplication-stmts=20" } */ +/* { dg-options "-O2 -fsplit-paths -fdump-tree-split-paths-details --param max-jump-thread-duplication-stmts=20 -fno-ssa-phiopt" } */ #include <stdio.h> #include <stdlib.h> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c new file mode 100644 index 0000000..5be5426 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c @@ -0,0 +1,19 @@ +// { dg-do compile } +// { dg-options "-O2 -fdisable-tree-ethread -fdisable-tree-fre1 -fdump-tree-evrp-details" } + +void bar (); +void george (); + +float +foo (float x, float y) +{ + if (x == x) + { + if (x > y) + bar(); + if (x == x) + george(); + } +} + +// { dg-final { scan-tree-dump-times "Folded into: if \\(1 != 0\\)" 1 "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c new file mode 100644 index 0000000..2f4dc87 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" } + +extern void link_error (); + +void fast_sqrt (float); + +float test (float x) +{ + float y = x*x; + if (y >= 0.f) + { + if (__builtin_isnan (y)) + link_error (); + else + fast_sqrt (y); + + if (!__builtin_isnan (y)) + fast_sqrt (y); + else + link_error (); + } +} + +// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } } +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c new file mode 100644 index 0000000..c659abb --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c @@ -0,0 +1,18 @@ +// { dg-do compile } +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } + +void link_error (); + +void +foo (double x, double y) +{ + if (x == y) + { + if (__builtin_isnan (x)) + link_error (); + if (__builtin_isnan (y)) + link_error (); + } +} + +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c new file mode 100644 index 0000000..8643674 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c @@ -0,0 +1,16 @@ +// { dg-do compile } +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } + +void link_error (); + +void +foo (double x, double y) +{ + if (x > y) + { + if (__builtin_isnan (x) || __builtin_isnan (y)) + link_error (); + } +} + +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c new file mode 100644 index 0000000..145d186 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c @@ -0,0 +1,20 @@ +// { dg-do compile } +// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" } + +void bar (); + +void +foo (double x, double y) +{ + if (x > y) + ; + else if (!__builtin_isnan (x) && !__builtin_isnan (y)) + { + // If x and y are not NAN, the x <= y relationship holds, and the + // following conditional can be folded away. + if (x <= y) + bar (); + } +} + +// { dg-final { scan-tree-dump-times "Folding predicate x_.* <= y_.* to 1" 1 "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c new file mode 100644 index 0000000..92af870 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c @@ -0,0 +1,14 @@ +// { dg-do compile } +// { dg-options "-O2 -fno-tree-forwprop -fno-tree-ccp -fno-tree-fre -fdump-tree-evrp" } + +extern void link_error (); + +void +foo () +{ + float z = 0.0; + if (__builtin_isnan (z)) + link_error (); +} + +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c new file mode 100644 index 0000000..9170150 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" } + +extern void link_error (); + +void fast_sqrt (float); + +float test (float x) +{ + float y = x*x; + if (y >= 0.f) + { + if (__builtin_isnan (y)) + link_error (); + else + fast_sqrt (y); + + if (!__builtin_isnan (y)) + fast_sqrt (y); + else + link_error (); + } +} + +// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } } +// { dg-final { scan-tree-dump-not "link_error" "evrp" } } diff --git a/gcc/testsuite/gcc.target/i386/addr-space-typeck-1.c b/gcc/testsuite/gcc.target/i386/addr-space-typeck-1.c new file mode 100644 index 0000000..84d27b0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/addr-space-typeck-1.c @@ -0,0 +1,22 @@ +/* { dg-options "-std=gnu90" } */ + +void * +test_gs_to_generic (void __seg_gs *p) +{ + return p; /* { dg-error "return from pointer to non-enclosed address space" "error" } */ + /* { dg-message "expected 'void \\*' but pointer is of type '__seg_gs void \\*'" "note" { target *-*-* } .-1 } */ +} + +void __seg_gs * +test_generic_to_gs (void *q) +{ + return q; /* { dg-error "return from pointer to non-enclosed address space" "error" } */ + /* { dg-message "expected '__seg_gs void \\*' but pointer is of type 'void \\*'" "note" { target *-*-* } .-1 } */ +} + +extern void use_double_deref (char __seg_gs **buffer); + +void test_double_deref (char __seg_gs *buf) +{ + use_double_deref (&buf); +} diff --git a/gcc/testsuite/gcc.target/i386/addr-space-typeck-2.c b/gcc/testsuite/gcc.target/i386/addr-space-typeck-2.c new file mode 100644 index 0000000..d9fb9a7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/addr-space-typeck-2.c @@ -0,0 +1,25 @@ +/* Tests of C frontend's address space type-checking. */ +/* { dg-options "-std=gnu90 -fdiagnostics-show-caret" } */ + +/* Verify that we emit helpful diagnostics at a mismatching address space + at a function call, and that the underlined locations are correct. */ + +extern void expects_seg_gs (int i, void __seg_gs *param, int j); /* { dg-line "decl_line" } */ + +void +test_bad_call (void *ptr) +{ + expects_seg_gs (0, ptr, 1); /* { dg-line "err_line" } */ +} + +/* { dg-error "passing argument 2 of 'expects_seg_gs' from pointer to non-enclosed address space" "" { target *-*-* } err_line } */ +/* { dg-begin-multiline-output "" } + expects_seg_gs (0, ptr, 1); + ^~~ + { dg-end-multiline-output "" } */ + +/* { dg-message "expected '__seg_gs void \\*' but argument is of type 'void \\*'" "" { target *-*-* } decl_line } */ +/* { dg-begin-multiline-output "" } + extern void expects_seg_gs (int i, void __seg_gs *param, int j); + ~~~~~~~~~~~~~~~^~~~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.target/i386/cmpti1.c b/gcc/testsuite/gcc.target/i386/cmpti1.c new file mode 100644 index 0000000..1c5f121 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/cmpti1.c @@ -0,0 +1,8 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2" } */ +int eq(__int128 x, __int128 y) { return x == y; } +int ne(__int128 x, __int128 y) { return x != y; } +/* { dg-final { scan-assembler-times "xorq" 4 } } */ +/* { dg-final { scan-assembler-times "setne" 1 } } */ +/* { dg-final { scan-assembler-times "sete" 1 } } */ + diff --git a/gcc/testsuite/gcc.target/i386/cmpti2.c b/gcc/testsuite/gcc.target/i386/cmpti2.c new file mode 100644 index 0000000..ba7dd72 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/cmpti2.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2 -mno-stv" } */ + +__int128 x; +__int128 y; + +int eq() { return x == y; } +int ne() { return x != y; } + +/* { dg-final { scan-assembler-times "xorq" 4 } } */ +/* { dg-final { scan-assembler-times "setne" 1 } } */ +/* { dg-final { scan-assembler-times "sete" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/cmpti3.c b/gcc/testsuite/gcc.target/i386/cmpti3.c new file mode 100644 index 0000000..302efd2 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/cmpti3.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2" } */ + +__int128 x; +int foo() +{ + __int128 t = 0x1234567890abcdefLL; + return x == t; +} + +/* { dg-final { scan-assembler-times "movabsq" 1 } } */ +/* { dg-final { scan-assembler-times "xorq" 1 } } */ +/* { dg-final { scan-assembler-not "xorl" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr106481.c b/gcc/testsuite/gcc.target/i386/pr106481.c new file mode 100644 index 0000000..8cc7048 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr106481.c @@ -0,0 +1,17 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2 -fno-dce -fno-forward-propagate -fno-rerun-cse-after-loop -Wno-psabi" } */ + +typedef int V __attribute__((vector_size (64))); +typedef __int128 W __attribute__((vector_size (64))); + +W w; +V bar (void); + +void +foo (V v, W) +{ + foo ((V){4, ~0}, (W) v); + foo (v, w); + bar (); +} + diff --git a/gcc/testsuite/gcc.target/i386/pr47949.c b/gcc/testsuite/gcc.target/i386/pr47949.c new file mode 100644 index 0000000..a0524b1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr47949.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-Oz" } */ +/* { dg-additional-options "-mregparm=2" { target ia32 } } */ + +int foo(int x, int y) +{ + return y; +} + +long bar(long x, long y) +{ + return y; +} + +/* { dg-final { scan-assembler-times "xchg" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr83782-1.c b/gcc/testsuite/gcc.target/i386/pr83782-1.c index ce97b12..8567434 100644 --- a/gcc/testsuite/gcc.target/i386/pr83782-1.c +++ b/gcc/testsuite/gcc.target/i386/pr83782-1.c @@ -1,4 +1,4 @@ -/* { dg-do compile } */ +/* { dg-do compile { target { ! ia32 } } } */ /* { dg-require-ifunc "" } */ /* { dg-options "-O2 -fpic" } */ @@ -20,7 +20,5 @@ bar(void) return foo; } -/* { dg-final { scan-assembler {leal[ \t]foo@GOTOFF\(%[^,]*\),[ \t]%eax} { target ia32 } } } */ -/* { dg-final { scan-assembler {lea(?:l|q)[ \t]foo\(%rip\),[ \t]%(?:e|r)ax} { target { ! ia32 } } } } */ -/* { dg-final { scan-assembler-not "foo@GOT\\\(" { target ia32 } } } */ -/* { dg-final { scan-assembler-not "foo@GOTPCREL\\\(" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler {lea(?:l|q)[ \t]foo\(%rip\),[ \t]%(?:e|r)ax} } } */ +/* { dg-final { scan-assembler-not "foo@GOTPCREL\\\(" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr83782-2.c b/gcc/testsuite/gcc.target/i386/pr83782-2.c index e25d258..a654ded 100644 --- a/gcc/testsuite/gcc.target/i386/pr83782-2.c +++ b/gcc/testsuite/gcc.target/i386/pr83782-2.c @@ -1,4 +1,4 @@ -/* { dg-do compile } */ +/* { dg-do compile { target { ! ia32 } } } */ /* { dg-require-ifunc "" } */ /* { dg-options "-O2 -fpic" } */ @@ -20,7 +20,5 @@ bar(void) return foo; } -/* { dg-final { scan-assembler {leal[ \t]foo@GOTOFF\(%[^,]*\),[ \t]%eax} { target ia32 } } } */ /* { dg-final { scan-assembler {lea(?:l|q)[ \t]foo\(%rip\),[ \t]%(?:e|r)ax} { target { ! ia32 } } } } */ -/* { dg-final { scan-assembler-not "foo@GOT\\\(" { target ia32 } } } */ /* { dg-final { scan-assembler-not "foo@GOTPCREL\\\(" { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr83782-3.c b/gcc/testsuite/gcc.target/i386/pr83782-3.c new file mode 100644 index 0000000..1536481 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr83782-3.c @@ -0,0 +1,32 @@ +/* { dg-do run } */ +/* { dg-require-ifunc "" } */ +/* { dg-require-effective-target pie } */ +/* { dg-options "-fpie -pie" } */ + +#include <stdio.h> + +static int __attribute__((noinline)) +implementation (void) +{ + printf ("'ere I am JH\n"); + return 0; +} + +static __typeof__ (implementation) *resolver (void) +{ + return (void *)implementation; +} + +extern int magic (void) __attribute__ ((ifunc ("resolver"))); + +__attribute__ ((weak)) +int +call_magic (int (*ptr) (void)) +{ + return ptr (); +} + +int main () +{ + return call_magic (magic); +} diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-stv-7.c b/gcc/testsuite/gcc.target/i386/sse4_1-stv-7.c new file mode 100644 index 0000000..b0d5fce --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_1-stv-7.c @@ -0,0 +1,18 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2 -msse4.1 -mstv -mno-stackrealign" } */ + +unsigned __int128 a; +unsigned __int128 b; + +void foo() +{ + a = b << 16; +} + +void bar() +{ + a = b >> 16; +} + +/* { dg-final { scan-assembler "pslldq" } } */ +/* { dg-final { scan-assembler "psrldq" } } */ diff --git a/gcc/testsuite/gdc.test/compilable/backendfloatoptim.d b/gcc/testsuite/gdc.test/compilable/backendfloatoptim.d new file mode 100644 index 0000000..7ec9f61 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/backendfloatoptim.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -O -inline + +//https://issues.dlang.org/show_bug.cgi?id=20143 +real fun(int x) { return 0.0; } + +double bug() +{ + // value passed to fun is irrelevant + return 0.0 / fun(420); +} diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle3.d b/gcc/testsuite/gdc.test/compilable/cppmangle3.d index 93e49c7..82c68f7 100644 --- a/gcc/testsuite/gdc.test/compilable/cppmangle3.d +++ b/gcc/testsuite/gdc.test/compilable/cppmangle3.d @@ -45,16 +45,12 @@ alias Alias(T) = T; static assert(is(Alias!(__traits(parent, Foo.bar)) == Foo)); extern(C++, "std"): -debug = 456; debug = def; -version = 456; version = def; extern(C++, "std") { - debug = 456; debug = def; - version = 456; version = def; } diff --git a/gcc/testsuite/gdc.test/compilable/must_use_initialize.d b/gcc/testsuite/gdc.test/compilable/must_use_initialize.d new file mode 100644 index 0000000..8caec43 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/must_use_initialize.d @@ -0,0 +1,16 @@ +// https://issues.dlang.org/show_bug.cgi?id=23236 +// can't initialize a @mustuse member in constructor + +import core.attribute; + +@mustuse struct MyError { } + +struct S +{ + MyError lastError; + + this(int x) + { + this.lastError = MyError(); + } +} diff --git a/gcc/testsuite/gdc.test/compilable/noreturn1.d b/gcc/testsuite/gdc.test/compilable/noreturn1.d index 5bba9ba..e648a56 100644 --- a/gcc/testsuite/gdc.test/compilable/noreturn1.d +++ b/gcc/testsuite/gdc.test/compilable/noreturn1.d @@ -122,3 +122,31 @@ noreturn testdg(noreturn delegate() dg) { dg(); } + +noreturn func() +{ + while(1) + { + } +} +alias AliasSeq(T...) = T; +alias Types = AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, + long, ulong, char, wchar, dchar, float, double, + real); +void noreturnImplicit() +{ + /* + Testing both ways because, although the underlying table + is symmetrical the code that calls into it may be buggy. + */ + { + int x = 2 + func(); + int y = func() + 2; + } + foreach(T; Types) + { + T value; + auto x = value + throw new Exception("Hello"); + auto y = (throw new Exception("wow")) + value; + } +} diff --git a/gcc/testsuite/gdc.test/compilable/test20832.d b/gcc/testsuite/gdc.test/compilable/test20832.d new file mode 100644 index 0000000..25617a9 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test20832.d @@ -0,0 +1,12 @@ +// PERMUTE_ARGS: -preview=dip1000 +// https://issues.dlang.org/show_bug.cgi?id=20823 + +void boo(T)( scope void delegate(T[] data) fun) {} +void goo(T)(/+scope+/ void delegate(T[] data) fun) {} + +void main() +{ + void Execute(int[] data) {} + goo(&Execute); + boo(&Execute); +} diff --git a/gcc/testsuite/gdc.test/compilable/test21177.d b/gcc/testsuite/gdc.test/compilable/test21177.d index b3b613b..b485304 100644 --- a/gcc/testsuite/gdc.test/compilable/test21177.d +++ b/gcc/testsuite/gdc.test/compilable/test21177.d @@ -4,15 +4,19 @@ DISABLED: win TEST_OUTPUT: --- compilable/test21177.d(103): Deprecation: more format specifiers than 0 arguments +compilable/test21177.d(111): Deprecation: more format specifiers than 0 arguments compilable/test21177.d(150): Deprecation: more format specifiers than 0 arguments compilable/test21177.d(151): Deprecation: more format specifiers than 0 arguments compilable/test21177.d(152): Deprecation: more format specifiers than 0 arguments compilable/test21177.d(153): Deprecation: more format specifiers than 0 arguments -compilable/test21177.d(200): Deprecation: more format specifiers than 0 arguments -compilable/test21177.d(203): Deprecation: format specifier `"%m"` is invalid -compilable/test21177.d(204): Deprecation: format specifier `"%m"` is invalid -compilable/test21177.d(205): Deprecation: argument `c` for format specification `"%a"` must be `float*`, not `char*` -compilable/test21177.d(206): Deprecation: argument `c` for format specification `"%a"` must be `float*`, not `char*` +compilable/test21177.d(154): Deprecation: more format specifiers than 0 arguments +compilable/test21177.d(155): Deprecation: more format specifiers than 0 arguments +compilable/test21177.d(202): Deprecation: format specifier `"%m"` is invalid +compilable/test21177.d(203): Deprecation: argument `d` for format specification `"%mc"` must be `char**`, not `int` +compilable/test21177.d(204): Deprecation: argument `c` for format specification `"%ms"` must be `char**`, not `char*` +compilable/test21177.d(205): Deprecation: format specifier `"%ml"` is invalid +compilable/test21177.d(206): Deprecation: argument `d` for format specification `"%mlc"` must be `wchar_t**`, not `int` +compilable/test21177.d(207): Deprecation: argument `c` for format specification `"%mls"` must be `wchar_t**`, not `char*` --- */ @@ -27,50 +31,45 @@ void main() #line 100 printf("%m this is a string in errno"); printf("%s %m", "str".ptr, 2); - printf("%a", 2.); + printf("%m %a", 2.); printf("%m %m %s"); + printf("%m"); printf("%*m"); - + pragma(msg, "compilable/test21177.d(111): Deprecation: more format specifiers than 0 arguments"); + } + else + { + pragma(msg, "compilable/test21177.d(103): Deprecation: more format specifiers than 0 arguments"); + printf("%m"); + } + { char* a, b; - sscanf("salut poilu", "%a %m", a, b); + sscanf("salut poilu", "%ms %m[^\n]", &a, &b); assert(!strcmp(a, b)); free(a); free(b); - char* t, p; - sscanf("Tomate Patate", "%ms %as", t, p); + char* t; wchar_t* p; + sscanf("Tomate Patate", "%mc %mlc", &t, &p); free(t); free(p); #line 150 sscanf("150", "%m"); sscanf("151", "%ms"); - sscanf("152", "%a"); - sscanf("153", "%as"); - - pragma(msg, "compilable/test21177.d(200): Deprecation: more format specifiers than 0 arguments"); - pragma(msg, "compilable/test21177.d(203): Deprecation: format specifier `\"%m\"` is invalid"); - pragma(msg, "compilable/test21177.d(204): Deprecation: format specifier `\"%m\"` is invalid"); - pragma(msg, "compilable/test21177.d(205): Deprecation: argument `c` for format specification `\"%a\"` must be `float*`, not `char*`"); - pragma(msg, "compilable/test21177.d(206): Deprecation: argument `c` for format specification `\"%a\"` must be `float*`, not `char*`"); - } - else - { - // fake it - pragma(msg, "compilable/test21177.d(103): Deprecation: more format specifiers than 0 arguments"); - pragma(msg, "compilable/test21177.d(150): Deprecation: more format specifiers than 0 arguments"); - pragma(msg, "compilable/test21177.d(151): Deprecation: more format specifiers than 0 arguments"); - pragma(msg, "compilable/test21177.d(152): Deprecation: more format specifiers than 0 arguments"); - pragma(msg, "compilable/test21177.d(153): Deprecation: more format specifiers than 0 arguments"); + sscanf("152", "%mc"); + sscanf("153", "%ml"); + sscanf("154", "%mls"); + sscanf("155", "%mlc"); #line 200 - printf("%m"); - char* c; + int d; sscanf("204", "%m", c); - sscanf("205", "%ms", c); - sscanf("206", "%a", c); - sscanf("207", "%as", c); - + sscanf("205", "%mc", d); + sscanf("206", "%ms", c); + sscanf("207", "%ml", d); + sscanf("208", "%mlc", d); + sscanf("209", "%mls", c); } } diff --git a/gcc/testsuite/gdc.test/compilable/test21432.d b/gcc/testsuite/gdc.test/compilable/test21432.d new file mode 100644 index 0000000..2c83e24 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test21432.d @@ -0,0 +1,25 @@ +// https://issues.dlang.org/show_bug.cgi?id=21432 +auto issue21432() +{ + enum int[] a = []; + return a; +} + +enum test21432a = issue21432; + +/////////////////////// + +double issue21432b(double r) +{ + enum double[4] poly = [ + 0x1.ffffffffffdbdp-2, + 0x1.555555555543cp-3, + 0x1.55555cf172b91p-5, + 0x1.1111167a4d017p-7, + ]; + + immutable r2 = r * r; + return r + r2 * (poly[0] + r * poly[1]) + r2 * r2 * (poly[2] + r * poly[3]); +} + +enum test21432b = issue21432b(-0x1p-1); diff --git a/gcc/testsuite/gdc.test/compilable/test22390.d b/gcc/testsuite/gdc.test/compilable/test22390.d new file mode 100644 index 0000000..f045416c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test22390.d @@ -0,0 +1,8 @@ +// https://issues.dlang.org/show_bug.cgi?id=22390 + +int main() +{ + noreturn[] empty; + assert(empty == empty); + return 0; +} diff --git a/gcc/testsuite/gdc.test/compilable/test23082.d b/gcc/testsuite/gdc.test/compilable/test23082.d new file mode 100644 index 0000000..9df4e4e --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23082.d @@ -0,0 +1,17 @@ +// https://issues.dlang.org/show_bug.cgi?id=23082 + +/* +TEST_OUTPUT: +--- +bar +--- +*/ + +void foo()() {} +alias bar = foo; +void bar() { } + +void main() +{ + pragma(msg, __traits(parent, main).bar.stringof); +} diff --git a/gcc/testsuite/gdc.test/compilable/test23166.d b/gcc/testsuite/gdc.test/compilable/test23166.d new file mode 100644 index 0000000..66da4cd --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23166.d @@ -0,0 +1,22 @@ +// REQUIRED_ARGS: -inline + +// https://issues.dlang.org/show_bug.cgi?id=23166 + +// seg fault with -inline + +bool __equals(scope const char[] lhs, scope const char[] rhs) +{ + if (lhs.length != rhs.length) + return false; + + { + import core.stdc.string : memcmp; + return lhs.length == 0; + } + return true; +} + +int test(string type) +{ + return __equals(type, "as-is"); +} diff --git a/gcc/testsuite/gdc.test/compilable/test23172.d b/gcc/testsuite/gdc.test/compilable/test23172.d new file mode 100644 index 0000000..18b6d4c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23172.d @@ -0,0 +1,33 @@ +// https://issues.dlang.org/show_bug.cgi?id=23172 + +enum E : ubyte { // `ubyte` is needed to trigger the bug + A, + B, +} + +struct S { + E e; +} + +void compiles(bool b, S s) { + E e = b ? E.A : s.e; +} + +void errors(bool b, const ref S s) { + E e = b ? E.A : s.e; +} + +// from https://issues.dlang.org/show_bug.cgi?id=23188 + +enum Status : byte +{ + A, B, C +} + +Status foo() +{ + Status t = Status.A; + const Status s = t; + + return (s == Status.A) ? Status.B : s; // <-- here +} diff --git a/gcc/testsuite/gdc.test/compilable/test23235.d b/gcc/testsuite/gdc.test/compilable/test23235.d new file mode 100644 index 0000000..99772ad --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23235.d @@ -0,0 +1,20 @@ +/* https://issues.dlang.org/show_bug.cgi?id=23235 + */ + +@safe: + +void awkk(string[] ppp...) +{ +} + +void bark(string[] foo...) { + awkk(foo); +} + +void cack(string[] bar...) { + bark(bar); +} + +void test() { + cack("abc", "def"); +} diff --git a/gcc/testsuite/gdc.test/compilable/test23256.d b/gcc/testsuite/gdc.test/compilable/test23256.d new file mode 100644 index 0000000..1e57201 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23256.d @@ -0,0 +1,6 @@ +/* REQUIRED_ARGS: -os=windows + */ + +// https://issues.dlang.org/show_bug.cgi?id=23256 + +void test23256() { } diff --git a/gcc/testsuite/gdc.test/compilable/test23262.d b/gcc/testsuite/gdc.test/compilable/test23262.d new file mode 100644 index 0000000..96da272 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test23262.d @@ -0,0 +1,17 @@ +/* https://issues.dlang.org/show_bug.cgi?id=23262 + */ + +struct T() +{ + string[] tags; + + this(string[] tags...) + { + this.tags = tags; // don't infer `return` attribute for `tags` + } +} + +void test() +{ + T!() t; +} diff --git a/gcc/testsuite/gdc.test/compilable/testgotoskips.d b/gcc/testsuite/gdc.test/compilable/testgotoskips.d new file mode 100644 index 0000000..659b5f7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testgotoskips.d @@ -0,0 +1,17 @@ +/* + Tests to defend against false positives from the goto skips over decl errors +*/ +// https://issues.dlang.org/show_bug.cgi?id=23271 +class A { + private static A[] active; + private void test() { + foreach(a; active) { + if(a is this) + goto label; + } + // used to say Error: `goto` skips declaration of variable `s.A.test.__appendtmp4` at s.d(...) + active ~= this; + label: + return; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic.d b/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic.d index 1fdf5a5..8360e1a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic.d +++ b/gcc/testsuite/gdc.test/fail_compilation/attributediagnostic.d @@ -1,12 +1,20 @@ /* TEST_OUTPUT: --- -fail_compilation/attributediagnostic.d(16): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1` -fail_compilation/attributediagnostic.d(18): which calls `attributediagnostic.layer0` -fail_compilation/attributediagnostic.d(20): which calls `attributediagnostic.system` -fail_compilation/attributediagnostic.d(22): which was inferred `@system` because of: -fail_compilation/attributediagnostic.d(22): `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not -fail_compilation/attributediagnostic.d(17): `attributediagnostic.layer1` is declared here +fail_compilation/attributediagnostic.d(24): Error: `@safe` function `attributediagnostic.layer2` cannot call `@system` function `attributediagnostic.layer1` +fail_compilation/attributediagnostic.d(26): which calls `attributediagnostic.layer0` +fail_compilation/attributediagnostic.d(28): which calls `attributediagnostic.system` +fail_compilation/attributediagnostic.d(30): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(30): `asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not +fail_compilation/attributediagnostic.d(25): `attributediagnostic.layer1` is declared here +fail_compilation/attributediagnostic.d(46): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system1` +fail_compilation/attributediagnostic.d(35): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(35): cast from `uint` to `int*` not allowed in safe code +fail_compilation/attributediagnostic.d(33): `attributediagnostic.system1` is declared here +fail_compilation/attributediagnostic.d(47): Error: `@safe` function `D main` cannot call `@system` function `attributediagnostic.system2` +fail_compilation/attributediagnostic.d(41): which was inferred `@system` because of: +fail_compilation/attributediagnostic.d(41): `@safe` function `system2` cannot call `@system` `fsys` +fail_compilation/attributediagnostic.d(39): `attributediagnostic.system2` is declared here --- */ @@ -19,5 +27,22 @@ auto layer0() { system(); } auto system() { - asm {} + asm {} +} + +auto system1() +{ + int* x = cast(int*) 0xDEADBEEF; +} + +auto fsys = function void() @system {}; +auto system2() +{ + fsys(); +} + +void main() @safe +{ + system1(); + system2(); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/chkformat.d b/gcc/testsuite/gdc.test/fail_compilation/chkformat.d index e9ed241..fa8915e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/chkformat.d +++ b/gcc/testsuite/gdc.test/fail_compilation/chkformat.d @@ -169,3 +169,21 @@ void test409() { char* p; printf("%llu", p); } void test410() { char* p; printf("%lld", p); } void test411() { char* p; printf("%ju", p); } void test412() { char* p; printf("%jd", p); } + +/* https://issues.dlang.org/show_bug.cgi?id=23247 +TEST_OUTPUT: +--- +fail_compilation/chkformat.d(501): Deprecation: argument `p` for format specification `"%a"` must be `double`, not `char*` +fail_compilation/chkformat.d(502): Deprecation: argument `p` for format specification `"%La"` must be `real`, not `char*` +fail_compilation/chkformat.d(503): Deprecation: argument `p` for format specification `"%a"` must be `float*`, not `char*` +fail_compilation/chkformat.d(504): Deprecation: argument `p` for format specification `"%la"` must be `double*`, not `char*` +fail_compilation/chkformat.d(505): Deprecation: argument `p` for format specification `"%La"` must be `real*`, not `char*` +--- +*/ +#line 500 + +void test501() { char* p; printf("%a", p); } +void test502() { char* p; printf("%La", p); } +void test503() { char* p; scanf("%a", p); } +void test504() { char* p; scanf("%la", p); } +void test505() { char* p; scanf("%La", p); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10319.d b/gcc/testsuite/gdc.test/fail_compilation/diag10319.d index 416d563..7b2eca7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag10319.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10319.d @@ -1,15 +1,17 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10319.d(27): Error: `pure` function `D main` cannot call impure function `diag10319.foo` -fail_compilation/diag10319.d(27): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` -fail_compilation/diag10319.d(16): `diag10319.foo` is declared here -fail_compilation/diag10319.d(28): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(28): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` -fail_compilation/diag10319.d(18): `diag10319.bar!int.bar` is declared here -fail_compilation/diag10319.d(27): Error: function `diag10319.foo` is not `nothrow` -fail_compilation/diag10319.d(28): Error: function `diag10319.bar!int.bar` is not `nothrow` -fail_compilation/diag10319.d(25): Error: function `D main` may throw but is marked as `nothrow` +fail_compilation/diag10319.d(29): Error: `pure` function `D main` cannot call impure function `diag10319.foo` +fail_compilation/diag10319.d(29): Error: `@safe` function `D main` cannot call `@system` function `diag10319.foo` +fail_compilation/diag10319.d(18): `diag10319.foo` is declared here +fail_compilation/diag10319.d(30): Error: `pure` function `D main` cannot call impure function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(30): Error: `@safe` function `D main` cannot call `@system` function `diag10319.bar!int.bar` +fail_compilation/diag10319.d(23): which was inferred `@system` because of: +fail_compilation/diag10319.d(23): cannot take address of local `x` in `@safe` function `bar` +fail_compilation/diag10319.d(20): `diag10319.bar!int.bar` is declared here +fail_compilation/diag10319.d(29): Error: function `diag10319.foo` is not `nothrow` +fail_compilation/diag10319.d(30): Error: function `diag10319.bar!int.bar` is not `nothrow` +fail_compilation/diag10319.d(27): Error: function `D main` may throw but is marked as `nothrow` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11198.d b/gcc/testsuite/gdc.test/fail_compilation/diag11198.d index 279d62a..1be0f1e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag11198.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag11198.d @@ -1,12 +1,14 @@ /* TEST_OUTPUT: --- -fail_compilation/diag11198.d(15): Error: version `blah` declaration must be at module level -fail_compilation/diag11198.d(16): Error: debug `blah` declaration must be at module level -fail_compilation/diag11198.d(17): Error: version `1` level declaration must be at module level -fail_compilation/diag11198.d(18): Error: debug `2` level declaration must be at module level -fail_compilation/diag11198.d(19): Error: identifier or integer expected, not `""` -fail_compilation/diag11198.d(20): Error: identifier or integer expected, not `""` +fail_compilation/diag11198.d(17): Error: version `blah` declaration must be at module level +fail_compilation/diag11198.d(18): Error: debug `blah` declaration must be at module level +fail_compilation/diag11198.d(19): Deprecation: `version = <integer>` is deprecated, use version identifiers instead +fail_compilation/diag11198.d(19): Error: version `1` level declaration must be at module level +fail_compilation/diag11198.d(20): Deprecation: `debug = <integer>` is deprecated, use debug identifiers instead +fail_compilation/diag11198.d(20): Error: debug `2` level declaration must be at module level +fail_compilation/diag11198.d(21): Error: identifier or integer expected, not `""` +fail_compilation/diag11198.d(22): Error: identifier or integer expected, not `""` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12829.d b/gcc/testsuite/gdc.test/fail_compilation/diag12829.d index f6e764b..1d37a1e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag12829.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12829.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/diag12829.d(12): Error: function `diag12829.test1` is `@nogc` yet allocates closures with the GC -fail_compilation/diag12829.d(15): diag12829.test1.__lambda2 closes over variable x at fail_compilation/diag12829.d(14) -fail_compilation/diag12829.d(19): diag12829.test1.bar closes over variable x at fail_compilation/diag12829.d(14) -fail_compilation/diag12829.d(26): Error: function `diag12829.test2` is `@nogc` yet allocates closures with the GC -fail_compilation/diag12829.d(31): diag12829.test2.S.foo closes over variable x at fail_compilation/diag12829.d(28) +fail_compilation/diag12829.d(12): Error: function `diag12829.test1` is `@nogc` yet allocates closure for `test1()` with the GC +fail_compilation/diag12829.d(15): `diag12829.test1.__lambda2` closes over variable `x` at fail_compilation/diag12829.d(14) +fail_compilation/diag12829.d(19): `diag12829.test1.bar` closes over variable `x` at fail_compilation/diag12829.d(14) +fail_compilation/diag12829.d(26): Error: function `diag12829.test2` is `@nogc` yet allocates closure for `test2()` with the GC +fail_compilation/diag12829.d(31): `diag12829.test2.S.foo` closes over variable `x` at fail_compilation/diag12829.d(28) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail118.d b/gcc/testsuite/gdc.test/fail_compilation/fail118.d index 3df797d..a526b90 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail118.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail118.d @@ -1,15 +1,17 @@ /* TEST_OUTPUT: --- -fail_compilation/fail118.d(43): Error: invalid `foreach` aggregate `Iter` of type `Iter` -fail_compilation/fail118.d(43): maybe define `opApply()`, range primitives, or use `.tupleof` -fail_compilation/fail118.d(44): Error: invalid `foreach` aggregate `Iter` of type `Iter` -fail_compilation/fail118.d(44): maybe define `opApply()`, range primitives, or use `.tupleof` -fail_compilation/fail118.d(47): Error: invalid `foreach` aggregate `s` of type `S*` -fail_compilation/fail118.d(49): Error: undefined identifier `unknown` -fail_compilation/fail118.d(37): Error: undefined identifier `doesNotExist` -fail_compilation/fail118.d(51): Error: template instance `fail118.error!()` error instantiating -fail_compilation/fail118.d(51): Error: invalid `foreach` aggregate `error()` of type `void` +fail_compilation/fail118.d(45): Error: invalid `foreach` aggregate `Iter` of type `Iter` +fail_compilation/fail118.d(45): `foreach` works with input ranges (implementing `front` and `popFront`), aggregates implementing `opApply`, or the result of an aggregate's `.tupleof` property +fail_compilation/fail118.d(45): https://dlang.org/phobos/std_range_primitives.html#isInputRange +fail_compilation/fail118.d(46): Error: invalid `foreach` aggregate `Iter` of type `Iter` +fail_compilation/fail118.d(46): `foreach` works with input ranges (implementing `front` and `popFront`), aggregates implementing `opApply`, or the result of an aggregate's `.tupleof` property +fail_compilation/fail118.d(46): https://dlang.org/phobos/std_range_primitives.html#isInputRange +fail_compilation/fail118.d(49): Error: invalid `foreach` aggregate `s` of type `S*` +fail_compilation/fail118.d(51): Error: undefined identifier `unknown` +fail_compilation/fail118.d(39): Error: undefined identifier `doesNotExist` +fail_compilation/fail118.d(53): Error: template instance `fail118.error!()` error instantiating +fail_compilation/fail118.d(53): Error: invalid `foreach` aggregate `error()` of type `void` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail20547.d b/gcc/testsuite/gdc.test/fail_compilation/fail20547.d deleted file mode 100644 index c14977d..0000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail20547.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail20547.d(12): Error: cannot create a `string[string]` with `new` -fail_compilation/fail20547.d(14): Error: cannot create a `string[string]` with `new` ---- -*/ - -void main() -{ - //https://issues.dlang.org/show_bug.cgi?id=11790 - string[string] crash = new string[string]; - //https://issues.dlang.org/show_bug.cgi?id=20547 - int[string] c = new typeof(crash); -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail22134.d b/gcc/testsuite/gdc.test/fail_compilation/fail22134.d new file mode 100644 index 0000000..5a4933e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail22134.d @@ -0,0 +1,17 @@ +// https://issues.dlang.org/show_bug.cgi?id=22134 +/* REQUIRED_ARGS: -de +TEST_OUTPUT: +--- +fail_compilation/fail22134.d(12): Deprecation: `this.arr[i]` has no effect +--- +*/ +struct StackBuffer +{ + auto opIndex(size_t i) + { + return arr[i]; + } + +private: + void[] arr; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail23181.d b/gcc/testsuite/gdc.test/fail_compilation/fail23181.d new file mode 100644 index 0000000..519244c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail23181.d @@ -0,0 +1,16 @@ +/* https://issues.dlang.org/show_bug.cgi?id=23181 +TEST_OUTPUT: +--- +$p:druntime/import/core/lifetime.d$($n$): Error: struct `fail23181.fail23181.NoPostblit` is not copyable because it has a disabled postblit +$p:druntime/import/core/internal/array/construction.d$($n$): Error: template instance `core.lifetime.copyEmplace!(NoPostblit, NoPostblit)` error instantiating +fail_compilation/fail23181.d(15): instantiated from here: `_d_arraysetctor!(NoPostblit[], NoPostblit)` +--- +*/ +void fail23181() +{ + struct NoPostblit + { + @disable this(this); + } + NoPostblit[4] noblit23181 = NoPostblit(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail261.d b/gcc/testsuite/gdc.test/fail_compilation/fail261.d index 85da957..d807dc8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail261.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail261.d @@ -1,8 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail261.d(19): Error: invalid `foreach` aggregate `range` of type `MyRange` -fail_compilation/fail261.d(19): maybe define `opApply()`, range primitives, or use `.tupleof` +fail_compilation/fail261.d(20): Error: invalid `foreach` aggregate `range` of type `MyRange` +fail_compilation/fail261.d(20): `foreach` works with input ranges (implementing `front` and `popFront`), aggregates implementing `opApply`, or the result of an aggregate's `.tupleof` property +fail_compilation/fail261.d(20): https://dlang.org/phobos/std_range_primitives.html#isInputRange --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail58.d b/gcc/testsuite/gdc.test/fail_compilation/fail58.d index 1557055..89b2351 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail58.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail58.d @@ -7,7 +7,7 @@ fail_compilation/fail58.d(30): Error: function `fail58.SomeFunc(dchar[] pText, o fail_compilation/fail58.d(30): cannot pass argument `""` of type `string` to parameter `dchar[] pText` --- */ -debug(1) import std.stdio; +debug import std.stdio; const int anything = -1000; // Line #2 dchar[] SomeFunc( dchar[] pText, out int pStopPosn) { @@ -15,7 +15,7 @@ dchar[] SomeFunc( dchar[] pText, out int pStopPosn) pStopPosn = 0; else pStopPosn = -1; - debug(1) writefln("DEBUG: using '%s' we get %d", pText, pStopPosn); + debug writefln("DEBUG: using '%s' we get %d", pText, pStopPosn); return pText.dup; } @@ -24,12 +24,12 @@ int main(char[][] pArgs) int sp; SomeFunc("123", sp); - debug(1) writefln("DEBUG: got %d", sp); + debug writefln("DEBUG: got %d", sp); assert(sp == -1); SomeFunc("", sp); // if (sp != 0){} // Line #22 - debug(1) writefln("DEBUG: got %d", sp); + debug writefln("DEBUG: got %d", sp); assert(sp == -1); return 0; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6889.d b/gcc/testsuite/gdc.test/fail_compilation/fail6889.d index aa18977..ee84a84 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6889.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6889.d @@ -55,7 +55,7 @@ L1: scope(failure) { L2: goto L1; } // OK goto L2; // NG - scope(failure) { return; } // OK + foreach (i; 0..1) { diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail7848.d b/gcc/testsuite/gdc.test/fail_compilation/fail7848.d index e8371c4..001c7d7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail7848.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail7848.d @@ -9,12 +9,12 @@ fail_compilation/fail7848.d(21): `fail7848.func` is declared here fail_compilation/fail7848.d(27): Error: `@nogc` function `fail7848.C.__unittest_L25_C30` cannot call non-@nogc function `fail7848.func` fail_compilation/fail7848.d(27): Error: function `fail7848.func` is not `nothrow` fail_compilation/fail7848.d(25): Error: function `fail7848.C.__unittest_L25_C30` may throw but is marked as `nothrow` -fail_compilation/fail7848.d(32): Error: `pure` function `fail7848.C.__invariant1` cannot call impure function `fail7848.func` -fail_compilation/fail7848.d(32): Error: `@safe` function `fail7848.C.__invariant1` cannot call `@system` function `fail7848.func` +fail_compilation/fail7848.d(32): Error: `pure` function `fail7848.C.__invariant0` cannot call impure function `fail7848.func` +fail_compilation/fail7848.d(32): Error: `@safe` function `fail7848.C.__invariant0` cannot call `@system` function `fail7848.func` fail_compilation/fail7848.d(21): `fail7848.func` is declared here -fail_compilation/fail7848.d(32): Error: `@nogc` function `fail7848.C.__invariant1` cannot call non-@nogc function `fail7848.func` +fail_compilation/fail7848.d(32): Error: `@nogc` function `fail7848.C.__invariant0` cannot call non-@nogc function `fail7848.func` fail_compilation/fail7848.d(32): Error: function `fail7848.func` is not `nothrow` -fail_compilation/fail7848.d(30): Error: function `fail7848.C.__invariant1` may throw but is marked as `nothrow` +fail_compilation/fail7848.d(30): Error: function `fail7848.C.__invariant0` may throw but is marked as `nothrow` --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d b/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d index d2a1d1d..24e39da 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11856_1.d @@ -1,8 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11856_1.d(13): Error: none of the overloads of template `ice11856_1.g` are callable using argument types `!()(A)` -fail_compilation/ice11856_1.d(11): Candidate is: `g(T)(T x)` +fail_compilation/ice11856_1.d(16): Error: none of the overloads of template `ice11856_1.g` are callable using argument types `!()(A)` +fail_compilation/ice11856_1.d(14): Candidate is: `g(T)(T x)` + with `T = A` + must satisfy the following constraint: +` is(typeof(x.f()))` --- */ struct A {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d index 22cf392..b50a616 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d +++ b/gcc/testsuite/gdc.test/fail_compilation/misc_parser_err_cov1.d @@ -10,7 +10,6 @@ fail_compilation/misc_parser_err_cov1.d(31): Error: expression expected, not `)` fail_compilation/misc_parser_err_cov1.d(32): Error: `type identifier : specialization` expected following `is` fail_compilation/misc_parser_err_cov1.d(33): Error: semicolon expected following auto declaration, not `auto` fail_compilation/misc_parser_err_cov1.d(33): Error: found `+` when expecting `(` following `mixin` -fail_compilation/misc_parser_err_cov1.d(34): Error: cannot create a `char[float]` with `new` fail_compilation/misc_parser_err_cov1.d(35): Error: `key:value` expected for associative array literal fail_compilation/misc_parser_err_cov1.d(36): Error: basic type expected, not `;` fail_compilation/misc_parser_err_cov1.d(36): Error: `{ members }` expected for anonymous class @@ -44,7 +43,7 @@ void main() auto tt = __traits(<o<); auto b = is ; auto mx1 = mixin +); - auto aa1 = new char[float]; + aa += [key:value, key]; auto anon1 = new class; auto anon2 = new class {}; diff --git a/gcc/testsuite/gdc.test/fail_compilation/newaa.d b/gcc/testsuite/gdc.test/fail_compilation/newaa.d new file mode 100644 index 0000000..ebc23fb --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/newaa.d @@ -0,0 +1,19 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/newaa.d(14): Error: cannot implicitly convert expression `new string[string]` of type `string[string]` to `int[string]` +fail_compilation/newaa.d(15): Error: function expected before `()`, not `new int[int]` of type `int[int]` +fail_compilation/newaa.d(17): Error: `new` cannot take arguments for an associative array +--- +*/ +#line 9 +void main() +{ + //https://issues.dlang.org/show_bug.cgi?id=11790 + string[string] crash = new string[string]; + //https://issues.dlang.org/show_bug.cgi?id=20547 + int[string] c = new typeof(crash); + auto d = new int[int](5); + alias AA = char[string]; + auto e = new AA(5); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc3.d b/gcc/testsuite/gdc.test/fail_compilation/nogc3.d index fdc3cde..3bd7167 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/nogc3.d +++ b/gcc/testsuite/gdc.test/fail_compilation/nogc3.d @@ -43,10 +43,10 @@ fail_compilation/nogc3.d(35): Error: `@nogc` function `nogc3.testCall` cannot ca /* TEST_OUTPUT: --- -fail_compilation/nogc3.d(52): Error: function `nogc3.testClosure1` is `@nogc` yet allocates closures with the GC -fail_compilation/nogc3.d(55): nogc3.testClosure1.bar closes over variable x at fail_compilation/nogc3.d(54) -fail_compilation/nogc3.d(64): Error: function `nogc3.testClosure3` is `@nogc` yet allocates closures with the GC -fail_compilation/nogc3.d(67): nogc3.testClosure3.bar closes over variable x at fail_compilation/nogc3.d(66) +fail_compilation/nogc3.d(52): Error: function `nogc3.testClosure1` is `@nogc` yet allocates closure for `testClosure1()` with the GC +fail_compilation/nogc3.d(55): `nogc3.testClosure1.bar` closes over variable `x` at fail_compilation/nogc3.d(54) +fail_compilation/nogc3.d(64): Error: function `nogc3.testClosure3` is `@nogc` yet allocates closure for `testClosure3()` with the GC +fail_compilation/nogc3.d(67): `nogc3.testClosure3.bar` closes over variable `x` at fail_compilation/nogc3.d(66) --- */ @nogc auto testClosure1() diff --git a/gcc/testsuite/gdc.test/fail_compilation/retscope.d b/gcc/testsuite/gdc.test/fail_compilation/retscope.d index 9394404..2a69fe0 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/retscope.d +++ b/gcc/testsuite/gdc.test/fail_compilation/retscope.d @@ -54,8 +54,8 @@ void test2(scope int* p, int[] a ...) @safe /* TEST_OUTPUT: --- -fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closures with the GC -fail_compilation/retscope.d(77): retscope.HTTP.Impl.onReceive.__lambda1 closes over variable this at fail_compilation/retscope.d(75) +fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closure for `onReceive()` with the GC +fail_compilation/retscope.d(77): `retscope.HTTP.Impl.onReceive.__lambda1` closes over variable `this` at fail_compilation/retscope.d(75) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13786.d b/gcc/testsuite/gdc.test/fail_compilation/test13786.d index 3524875..73ec588 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test13786.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test13786.d @@ -1,11 +1,13 @@ /* TEST_OUTPUT: --- -fail_compilation/test13786.d(14): Error: debug `123` level declaration must be at module level -fail_compilation/test13786.d(15): Error: debug `abc` declaration must be at module level -fail_compilation/test13786.d(16): Error: version `123` level declaration must be at module level -fail_compilation/test13786.d(17): Error: version `abc` declaration must be at module level -fail_compilation/test13786.d(20): Error: template instance `test13786.T!()` error instantiating +fail_compilation/test13786.d(16): Deprecation: `debug = <integer>` is deprecated, use debug identifiers instead +fail_compilation/test13786.d(18): Deprecation: `version = <integer>` is deprecated, use version identifiers instead +fail_compilation/test13786.d(16): Error: debug `123` level declaration must be at module level +fail_compilation/test13786.d(17): Error: debug `abc` declaration must be at module level +fail_compilation/test13786.d(18): Error: version `123` level declaration must be at module level +fail_compilation/test13786.d(19): Error: version `abc` declaration must be at module level +fail_compilation/test13786.d(22): Error: template instance `test13786.T!()` error instantiating --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test16193.d b/gcc/testsuite/gdc.test/fail_compilation/test16193.d index 053f583..6c80471 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test16193.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test16193.d @@ -2,8 +2,8 @@ REQUIRED_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test16193.d(38): Error: function `test16193.abc` is `@nogc` yet allocates closures with the GC -fail_compilation/test16193.d(40): test16193.abc.__foreachbody2 closes over variable x at fail_compilation/test16193.d(39) +fail_compilation/test16193.d(38): Error: function `test16193.abc` is `@nogc` yet allocates closure for `abc()` with the GC +fail_compilation/test16193.d(40): `test16193.abc.__foreachbody2` closes over variable `x` at fail_compilation/test16193.d(39) --- */ //fail_compilation/test16193.d(22): To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope` diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21443.d b/gcc/testsuite/gdc.test/fail_compilation/test21443.d new file mode 100644 index 0000000..2d99524 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test21443.d @@ -0,0 +1,21 @@ +// https://issues.dlang.org/show_bug.cgi?id=21443 +// REQUIRED_ARGS: -de + +/* +TEST_OUTPUT: +--- +fail_compilation/test21443.d(14): Deprecation: `return` statements cannot be in `scope(failure)` bodies. +fail_compilation/test21443.d(14): Use try-catch blocks for this purpose +--- +*/ + +ulong get () @safe nothrow +{ + scope (failure) return 10; + throw new Error(""); +} + +void main () @safe +{ + assert(get() == 10); // passes +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21912.d b/gcc/testsuite/gdc.test/fail_compilation/test21912.d index 925210b..9b07eba 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test21912.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test21912.d @@ -2,14 +2,14 @@ PERMUTE_ARGS: -preview=dip1000 TEST_OUTPUT: --- -fail_compilation/test21912.d(24): Error: function `test21912.escapeParam` is `@nogc` yet allocates closures with the GC -fail_compilation/test21912.d(26): test21912.escapeParam.__lambda2 closes over variable i at fail_compilation/test21912.d(24) -fail_compilation/test21912.d(29): Error: function `test21912.escapeAssign` is `@nogc` yet allocates closures with the GC -fail_compilation/test21912.d(31): test21912.escapeAssign.__lambda3 closes over variable i at fail_compilation/test21912.d(29) -fail_compilation/test21912.d(40): Error: function `test21912.escapeAssignRef` is `@nogc` yet allocates closures with the GC -fail_compilation/test21912.d(42): test21912.escapeAssignRef.__lambda3 closes over variable i at fail_compilation/test21912.d(40) -fail_compilation/test21912.d(51): Error: function `test21912.escapeParamInferred` is `@nogc` yet allocates closures with the GC -fail_compilation/test21912.d(53): test21912.escapeParamInferred.__lambda2 closes over variable i at fail_compilation/test21912.d(51) +fail_compilation/test21912.d(24): Error: function `test21912.escapeParam` is `@nogc` yet allocates closure for `escapeParam()` with the GC +fail_compilation/test21912.d(26): `test21912.escapeParam.__lambda2` closes over variable `i` at fail_compilation/test21912.d(24) +fail_compilation/test21912.d(29): Error: function `test21912.escapeAssign` is `@nogc` yet allocates closure for `escapeAssign()` with the GC +fail_compilation/test21912.d(31): `test21912.escapeAssign.__lambda3` closes over variable `i` at fail_compilation/test21912.d(29) +fail_compilation/test21912.d(40): Error: function `test21912.escapeAssignRef` is `@nogc` yet allocates closure for `escapeAssignRef()` with the GC +fail_compilation/test21912.d(42): `test21912.escapeAssignRef.__lambda3` closes over variable `i` at fail_compilation/test21912.d(40) +fail_compilation/test21912.d(51): Error: function `test21912.escapeParamInferred` is `@nogc` yet allocates closure for `escapeParamInferred()` with the GC +fail_compilation/test21912.d(53): `test21912.escapeParamInferred.__lambda2` closes over variable `i` at fail_compilation/test21912.d(51) --- */ @nogc: diff --git a/gcc/testsuite/gdc.test/fail_compilation/test21939.d b/gcc/testsuite/gdc.test/fail_compilation/test21939.d index 8f30bac..e513dc2 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test21939.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test21939.d @@ -2,8 +2,9 @@ /* TEST_OUTPUT: --- -fail_compilation/test21939.d(10): Error: invalid `foreach` aggregate `Object` of type `Object` -fail_compilation/test21939.d(10): maybe define `opApply()`, range primitives, or use `.tupleof` +fail_compilation/test21939.d(11): Error: invalid `foreach` aggregate `Object` of type `Object` +fail_compilation/test21939.d(11): `foreach` works with input ranges (implementing `front` and `popFront`), aggregates implementing `opApply`, or the result of an aggregate's `.tupleof` property +fail_compilation/test21939.d(11): https://dlang.org/phobos/std_range_primitives.html#isInputRange --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23022.d b/gcc/testsuite/gdc.test/fail_compilation/test23022.d new file mode 100644 index 0000000..8c4eca9 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test23022.d @@ -0,0 +1,15 @@ +/* +REQUIRED_ARGS: -preview=dip1000 +TEST_OUTPUT: +--- +fail_compilation/test23022.d(14): Error: scope parameter `p` may not be returned +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23022 +// Typesafe variadic parameter should not infer return + +auto ir(string[] p...) +{ + return p; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23112.d b/gcc/testsuite/gdc.test/fail_compilation/test23112.d new file mode 100644 index 0000000..325d89b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test23112.d @@ -0,0 +1,30 @@ +/* REQUIRED_ARGS: -betterC +TEST_OUTPUT: +--- +fail_compilation/test23112.d(106): Error: function `test23112.bar` is `@nogc` yet allocates closure for `bar()` with the GC +fail_compilation/test23112.d(108): `test23112.bar.f` closes over variable `a` at fail_compilation/test23112.d(106) +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23112 + +#line 100 + +struct Forward(alias F) +{ + auto call()() { return F(); } +} + +auto bar(int a) nothrow @safe +{ + auto f() + { + return a; + } + return Forward!f(); +} + +extern(C) void main() +{ + assert(bar(3).call() == 3); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23170.d b/gcc/testsuite/gdc.test/fail_compilation/test23170.d new file mode 100644 index 0000000..eb79cd8 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test23170.d @@ -0,0 +1,12 @@ +/*
+TEST_OUTPUT:
+---
+fail_compilation/test23170.d(10): Error: array literal in `@nogc` delegate `test23170.__lambda5` may cause a GC allocation
+---
+*/
+// https://issues.dlang.org/show_bug.cgi?id=23170
+
+@nogc:
+enum lambda = () => badAlias([1, 2, 3]);
+alias badAlias = (int[] array) => id(array);
+int[] id(int[] array) { return array; }
diff --git a/gcc/testsuite/gdc.test/fail_compilation/test23216.d b/gcc/testsuite/gdc.test/fail_compilation/test23216.d new file mode 100644 index 0000000..d7c12ed --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test23216.d @@ -0,0 +1,24 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test23216.d(23): Error: invalid `foreach_reverse` aggregate `r` of type `Range` +fail_compilation/test23216.d(23): `foreach_reverse` works with bidirectional ranges (implementing `back` and `popBack`), aggregates implementing `opApplyReverse`, or the result of an aggregate's `.tupleof` property +fail_compilation/test23216.d(23): https://dlang.org/phobos/std_range_primitives.html#isBidirectionalRange +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=23216 +// Better Error Message For foreach_reverse Without Bidirectional Range + +struct Range +{ + bool empty = true; + int front = 0; + void popFront() { } +} + +void main() +{ + Range r; + foreach_reverse (word; r) { } +} diff --git a/gcc/testsuite/gdc.test/runnable/closure.d b/gcc/testsuite/gdc.test/runnable/closure.d index af304c1..1382f2d 100644 --- a/gcc/testsuite/gdc.test/runnable/closure.d +++ b/gcc/testsuite/gdc.test/runnable/closure.d @@ -922,7 +922,10 @@ void test14730() // This is questionable case. Currently it works without any errors, // but not sure it's really intentional - +// It showed up again in https://issues.dlang.org/show_bug.cgi?id=23112 +// where it's an @safe issue so it's a bug. +static if (0) +{ struct S14730x(alias f) { auto foo()() { return f(0); } @@ -947,6 +950,7 @@ void test14730x() // *after* the semantic3 completion of makeS() function. assert(s.foo() == 10); } +} /************************************/ @@ -981,7 +985,7 @@ int main() test9685b(); test12406(); test14730(); - test14730x(); + //test14730x(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/evalorder.d b/gcc/testsuite/gdc.test/runnable/evalorder.d index 6805ee2..f93aef5 100644 --- a/gcc/testsuite/gdc.test/runnable/evalorder.d +++ b/gcc/testsuite/gdc.test/runnable/evalorder.d @@ -46,6 +46,12 @@ int mul11ret3(T)(ref T s) return 3; } +auto cat11ret3(T)(ref T s) +{ + s ~= 11; + return [3]; +} + void add() { static int test1(int val) { val += add8ret3(val); return val; } @@ -147,6 +153,25 @@ void shr() static assert(test(0x80) == 0x40); } +void cat() +{ + static auto test1(int[] val) { val ~= cat11ret3(val); return val; } + assert(test1([1]) == [1, 11, 3]); + static assert(test1([1]) == [1, 11, 3]); + + static auto test2(int[] val) { val = val ~ cat11ret3(val); return val; } + // FIXME: assert(test2([1]) == [1, 3]); + static assert(test2([1]) == [1, 3]); + + static auto test3(int[] val) { (val ~= 7) ~= cat11ret3(val); return val; } + assert(test3([2]) == [2, 7, 11, 3]); + static assert(test3([2]) == [2, 7, 11, 3]); + + static auto test4(int[] val) { (val ~= cat11ret3(val)) ~= 7; return val; } + assert(test4([2]) == [2, 11, 3, 7]); + static assert(test4([2]) == [2, 11, 3, 7]); +} + void ldc_github_1617() { add(); @@ -156,6 +181,7 @@ void ldc_github_1617() addptr(); lhsCast(); shr(); + cat(); } /******************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/lexer.d b/gcc/testsuite/gdc.test/runnable/lexer.d index ee2fef8..6e31c07 100644 --- a/gcc/testsuite/gdc.test/runnable/lexer.d +++ b/gcc/testsuite/gdc.test/runnable/lexer.d @@ -1,5 +1,11 @@ // REQUIRED_ARGS: - +/* +TEST_OUTPUT: +--- +runnable/lexer.d(81): Deprecation: `version( <integer> )` is deprecated, use version identifiers instead +runnable/lexer.d(82): Deprecation: `debug( <integer> )` is deprecated, use debug identifiers instead +--- +*/ /*********************************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/noreturn1.d b/gcc/testsuite/gdc.test/runnable/noreturn1.d index 7d15b54..5ed46c1 100644 --- a/gcc/testsuite/gdc.test/runnable/noreturn1.d +++ b/gcc/testsuite/gdc.test/runnable/noreturn1.d @@ -261,6 +261,37 @@ void testThrowDtor() /*****************************************/ +noreturn func() +{ + throw new Exception("B"); +} + +// https://issues.dlang.org/show_bug.cgi?id=23120 +void test23120() +{ + string a; + try + { + noreturn q = throw new Exception ("A"); + } + catch(Exception e) + { + a ~= e.msg; + } + + try + { + noreturn z = func(); + } + catch(Exception e) + { + a ~= e.msg; + } + + assert(a == "AB"); +} + +/*****************************************/ int main() { test1(); @@ -269,5 +300,6 @@ int main() testThrowExpression(); testThrowSideEffect(); testThrowDtor(); + test23120(); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/test11.d b/gcc/testsuite/gdc.test/runnable/test11.d index 6735a81..333b600 100644 --- a/gcc/testsuite/gdc.test/runnable/test11.d +++ b/gcc/testsuite/gdc.test/runnable/test11.d @@ -1193,41 +1193,6 @@ void test63() printf("%.*s\n", cast(int)s.length, s.ptr); } - -/**************************************/ - -debug = 3; - -void test64() -{ - debug(5) - { - assert(0); - } - debug(3) - { - int x = 3; - } - assert(x == 3); -} - -/**************************************/ - -version = 3; - -void test65() -{ - version(5) - { - assert(0); - } - version(3) - { - int x = 3; - } - assert(x == 3); -} - /**************************************/ // https://issues.dlang.org/show_bug.cgi?id=8809 @@ -1381,8 +1346,6 @@ int main(string[] argv) test61(); test62(); test63(); - test64(); - test65(); test8809(); test9734(); diff --git a/gcc/testsuite/gdc.test/runnable/test18973.d b/gcc/testsuite/gdc.test/runnable/test18973.d new file mode 100644 index 0000000..29fcfa7 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test18973.d @@ -0,0 +1,25 @@ +// This is a runnable test as we are testing a linker error + +// https://issues.dlang.org/show_bug.cgi?id=18973 +struct X { + @disable size_t toHash() const; + @disable string toString() const; + @disable bool opEquals(const ref X) const; + @disable int opCmp(const ref X) const; +} + +// https://issues.dlang.org/show_bug.cgi?id=9161 +public struct dummy +{ + static auto opCall(C)(in C[] name) + { + return name; + } + + @disable ~this(); //comment this out to avoid error +} + +void main() +{ + assert(dummy("ABCDE") == "ABCDE"); +} diff --git a/gcc/testsuite/gdc.test/runnable/test19.d b/gcc/testsuite/gdc.test/runnable/test19.d index 372d32f..eda6172 100644 --- a/gcc/testsuite/gdc.test/runnable/test19.d +++ b/gcc/testsuite/gdc.test/runnable/test19.d @@ -59,22 +59,7 @@ void test2() void test3() { debug printf("debug\n"); - debug(1) printf("debug(1)\n"); - debug(2) printf("debug(2)\n"); - debug(3) printf("debug(3)\n"); debug(bar) printf("debug(bar)\n"); - debug(10) assert(0); - - debug(1) - { - int d1 = 3; - - printf("debug(1) { }\n"); - } - debug(2) - { - printf("debug(2): d1 = %d\n", d1); - } } /* ================================ */ diff --git a/gcc/testsuite/gdc.test/runnable/test20734.d b/gcc/testsuite/gdc.test/runnable/test20734.d index 264602b..b3c5916 100644 --- a/gcc/testsuite/gdc.test/runnable/test20734.d +++ b/gcc/testsuite/gdc.test/runnable/test20734.d @@ -16,6 +16,7 @@ extern(C) int main() nothrow @nogc @safe { takeScopeSlice([ S(1), S(2) ]); // @nogc => no GC allocation (() @trusted { assert(numDtor == 2); })(); // stack-allocated array literal properly destructed + assert23100([]); return 0; } @@ -26,3 +27,9 @@ void test23098() @safe { f23098([10, 20]); } + +// https://issues.dlang.org/show_bug.cgi?id=23100 +void assert23100(scope int[] d) @safe nothrow @nogc +{ + assert(!d); +} diff --git a/gcc/testsuite/gdc.test/runnable/test23181.d b/gcc/testsuite/gdc.test/runnable/test23181.d new file mode 100644 index 0000000..b961690 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test23181.d @@ -0,0 +1,27 @@ +// https://issues.dlang.org/show_bug.cgi?id=23181 +void main() +{ + int count; + struct HasDtor + { + ~this() { ++count; } + } + + // array[] = elem() + // -> creates temporary to construct array and calls destructor. + { + count = 0; + HasDtor[4] dtor1 = HasDtor(); + assert(count == 1); + } + assert(count == 5); + + // array[] = array[elem()] + // -> constructs array using direct emplacement. + { + count = 0; + HasDtor[2] dtor2 = [HasDtor(), HasDtor()]; + assert(count == 0); + } + assert(count == 2); +} diff --git a/gcc/testsuite/gdc.test/runnable/test8.d b/gcc/testsuite/gdc.test/runnable/test8.d index 7d66eb6..d65ba0e 100644 --- a/gcc/testsuite/gdc.test/runnable/test8.d +++ b/gcc/testsuite/gdc.test/runnable/test8.d @@ -592,6 +592,44 @@ void test34() } /***********************************/ +// https://issues.dlang.org/show_bug.cgi?id=19178 + +float[3][4] arr2f = 10; +Int3_4[1] arr3i = 20; +short[3][4][1][1] arr4s = 30; + +enum Int3 : int[3] { + a = [0, 1, 2], +} + +enum Int3_4 : Int3[4] { + b = Int3[4].init, +} + +struct S35 +{ + int[3][3] arr = [2, 1]; +} + +void test35() +{ + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 3; j++) + { + // printf("[%d %d]: %f %d %d\n", i, j, arr2f[i][j], arr3i[0][i][j], arr4s[0][0][i][j]); + assert(arr2f[i][j] == 10); + assert(arr3i[0][i][j] == 20); + assert(arr4s[0][0][i][j] == 30); + } + } + + S35 t = S35.init; + assert(t.arr[0] == [2, 2, 2]); + assert(t.arr[1] == [1, 1, 1]); + assert(t.arr[2] == [0, 0, 0]); +} +/***********************************/ string itoa(int i) { @@ -868,6 +906,7 @@ int main() test32(); test33(); test34(); + test35(); test36(); test37(); test38(); diff --git a/gcc/testsuite/gdc.test/runnable/version.d b/gcc/testsuite/gdc.test/runnable/version.d index 1186e4c..e225d5e 100644 --- a/gcc/testsuite/gdc.test/runnable/version.d +++ b/gcc/testsuite/gdc.test/runnable/version.d @@ -1,10 +1,9 @@ /* PERMUTE_ARGS: -REQUIRED_ARGS: -version=3 -version=foo +REQUIRED_ARGS: -version=foo RUN_OUTPUT: --- i = 2 -i = 2 --- */ @@ -15,20 +14,6 @@ extern(C) int printf(const char*, ...); void test1() { int i = 3; - - version(2) - { - i = 2; - } - else - { - i = 0; - } - printf("i = %d\n", i); - assert(i == 2); - - i = 3; - version(foo) { i = 2; @@ -47,10 +32,6 @@ version(foo) { version = bar; } -else -{ - version = 4; -} void test2() { @@ -59,8 +40,6 @@ void test2() } else assert(0); - - version(4) assert(0); } /*******************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/warning1.d b/gcc/testsuite/gdc.test/runnable/warning1.d index 537088e..01ac20c0 100644 --- a/gcc/testsuite/gdc.test/runnable/warning1.d +++ b/gcc/testsuite/gdc.test/runnable/warning1.d @@ -133,15 +133,6 @@ void test6518() } } -/******************************************/ -// https://issues.dlang.org/show_bug.cgi?id=7232 - -bool test7232() -{ - scope(failure) return false; - return true; -} - /***************************************************/ struct S9332 diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 4ed7b25..04a2a8e 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -2288,7 +2288,7 @@ proc check_p9modulo_hw_available { } { { int i = 5, j = 3, r = -1; asm ("modsw %0,%1,%2" : "+r" (r) : "r" (i), "r" (j)); - return (r == 2); + return (r != 2); } } $options } diff --git a/gcc/tree-core.h b/gcc/tree-core.h index ea9f281..86a07c2 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -1589,17 +1589,17 @@ struct GTY(()) tree_ssa_name { /* Value range information. */ union ssa_name_info_type { + /* Ranges for integers. */ + struct GTY ((tag ("0"))) irange_storage_slot *irange_info; + /* Ranges for floating point numbers. */ + struct GTY ((tag ("1"))) frange_storage_slot *frange_info; /* Pointer attributes used for alias analysis. */ - struct GTY ((tag ("0"))) ptr_info_def *ptr_info; + struct GTY ((tag ("2"))) ptr_info_def *ptr_info; /* This holds any range info supported by ranger (except ptr_info above) and is managed by vrange_storage. */ void * GTY ((skip)) range_info; - /* GTY tag when the range in the range_info slot above satisfies - irange::supports_type_p. */ - struct GTY ((tag ("1"))) irange_storage_slot *irange_info; } GTY ((desc ("%1.typed.type ?" \ - "!POINTER_TYPE_P (TREE_TYPE ((tree)&%1)) : 2"))) info; - + "(POINTER_TYPE_P (TREE_TYPE ((tree)&%1)) ? 2 : SCALAR_FLOAT_TYPE_P (TREE_TYPE ((tree)&%1))) : 3"))) info; /* Immediate uses list for this SSA_NAME. */ struct ssa_use_operand_t imm_uses; }; diff --git a/gcc/tree-loop-distribution.cc b/gcc/tree-loop-distribution.cc index 0ee441c..e1948fb 100644 --- a/gcc/tree-loop-distribution.cc +++ b/gcc/tree-loop-distribution.cc @@ -3829,7 +3829,7 @@ loop_distribution::execute (function *fun) { auto_vec<gimple *> work_list; if (!find_seed_stmts_for_distribution (loop, &work_list)) - break; + continue; const char *str = loop->inner ? " nest" : ""; dump_user_location_t loc = find_loop_location (loop); diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc index 2f4bebe..0191b9c 100644 --- a/gcc/tree-ssa-phiopt.cc +++ b/gcc/tree-ssa-phiopt.cc @@ -63,8 +63,8 @@ static gphi *factor_out_conditional_conversion (edge, edge, gphi *, tree, tree, gimple *); static int value_replacement (basic_block, basic_block, edge, edge, gphi *, tree, tree); -static bool minmax_replacement (basic_block, basic_block, - edge, edge, gphi *, tree, tree); +static bool minmax_replacement (basic_block, basic_block, basic_block, + edge, edge, gphi *, tree, tree, bool); static bool spaceship_replacement (basic_block, basic_block, edge, edge, gphi *, tree, tree); static bool cond_removal_in_builtin_zero_pattern (basic_block, basic_block, @@ -200,6 +200,7 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) basic_block bb1, bb2; edge e1, e2; tree arg0, arg1; + bool diamond_p = false; bb = bb_order[i]; @@ -266,6 +267,12 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) hoist_adjacent_loads (bb, bb1, bb2, bb3); continue; } + else if (EDGE_SUCC (bb1, 0)->dest == EDGE_SUCC (bb2, 0)->dest + && !empty_block_p (bb1)) + { + diamond_p = true; + e2 = EDGE_SUCC (bb2, 0); + } else continue; @@ -276,7 +283,7 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) || (e1->flags & EDGE_FALLTHRU) == 0) continue; - if (do_store_elim) + if (do_store_elim && !diamond_p) { /* Also make sure that bb1 only have one predecessor and that it is bb. */ @@ -294,13 +301,16 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) } else { - gimple_seq phis = phi_nodes (bb2); gimple_stmt_iterator gsi; bool candorest = true; + /* Check that we're looking for nested phis. */ + basic_block merge = diamond_p ? EDGE_SUCC (bb2, 0)->dest : bb2; + gimple_seq phis = phi_nodes (merge); + /* Value replacement can work with more than one PHI so try that first. */ - if (!early_p) + if (!early_p && !diamond_p) for (gsi = gsi_start (phis); !gsi_end_p (gsi); gsi_next (&gsi)) { phi = as_a <gphi *> (gsi_stmt (gsi)); @@ -330,6 +340,7 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) gphi *newphi; if (single_pred_p (bb1) + && !diamond_p && (newphi = factor_out_conditional_conversion (e1, e2, phi, arg0, arg1, cond_stmt))) @@ -344,20 +355,25 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) } /* Do the replacement of conditional if it can be done. */ - if (!early_p && two_value_replacement (bb, bb1, e2, phi, arg0, arg1)) + if (!early_p + && !diamond_p + && two_value_replacement (bb, bb1, e2, phi, arg0, arg1)) cfgchanged = true; - else if (match_simplify_replacement (bb, bb1, e1, e2, phi, - arg0, arg1, - early_p)) + else if (!diamond_p + && match_simplify_replacement (bb, bb1, e1, e2, phi, + arg0, arg1, early_p)) cfgchanged = true; else if (!early_p + && !diamond_p && single_pred_p (bb1) && cond_removal_in_builtin_zero_pattern (bb, bb1, e1, e2, phi, arg0, arg1)) cfgchanged = true; - else if (minmax_replacement (bb, bb1, e1, e2, phi, arg0, arg1)) + else if (minmax_replacement (bb, bb1, bb2, e1, e2, phi, arg0, arg1, + diamond_p)) cfgchanged = true; else if (single_pred_p (bb1) + && !diamond_p && spaceship_replacement (bb, bb1, e1, e2, phi, arg0, arg1)) cfgchanged = true; } @@ -422,12 +438,23 @@ replace_phi_edge_with_variable (basic_block cond_block, SET_USE (PHI_ARG_DEF_PTR (phi, e->dest_idx), new_tree); /* Remove the empty basic block. */ - edge edge_to_remove; + edge edge_to_remove = NULL, keep_edge = NULL; if (EDGE_SUCC (cond_block, 0)->dest == bb) - edge_to_remove = EDGE_SUCC (cond_block, 1); + { + edge_to_remove = EDGE_SUCC (cond_block, 1); + keep_edge = EDGE_SUCC (cond_block, 0); + } + else if (EDGE_SUCC (cond_block, 1)->dest == bb) + { + edge_to_remove = EDGE_SUCC (cond_block, 0); + keep_edge = EDGE_SUCC (cond_block, 1); + } + else if ((keep_edge = find_edge (cond_block, e->src))) + ; else - edge_to_remove = EDGE_SUCC (cond_block, 0); - if (EDGE_COUNT (edge_to_remove->dest->preds) == 1) + gcc_unreachable (); + + if (edge_to_remove && EDGE_COUNT (edge_to_remove->dest->preds) == 1) { e->flags |= EDGE_FALLTHRU; e->flags &= ~(EDGE_TRUE_VALUE | EDGE_FALSE_VALUE); @@ -444,9 +471,9 @@ replace_phi_edge_with_variable (basic_block cond_block, CFG cleanup deal with the edge removal to avoid updating dominators here in a non-trivial way. */ gcond *cond = as_a <gcond *> (last_stmt (cond_block)); - if (edge_to_remove->flags & EDGE_TRUE_VALUE) + if (keep_edge->flags & EDGE_FALSE_VALUE) gimple_cond_make_false (cond); - else + else if (keep_edge->flags & EDGE_TRUE_VALUE) gimple_cond_make_true (cond); } @@ -1733,15 +1760,52 @@ value_replacement (basic_block cond_bb, basic_block middle_bb, return 0; } +/* If VAR is an SSA_NAME that points to a BIT_NOT_EXPR then return the TREE for + the value being inverted. */ + +static tree +strip_bit_not (tree var) +{ + if (TREE_CODE (var) != SSA_NAME) + return NULL_TREE; + + gimple *assign = SSA_NAME_DEF_STMT (var); + if (gimple_code (assign) != GIMPLE_ASSIGN) + return NULL_TREE; + + if (gimple_assign_rhs_code (assign) != BIT_NOT_EXPR) + return NULL_TREE; + + return gimple_assign_rhs1 (assign); +} + +/* Invert a MIN to a MAX or a MAX to a MIN expression CODE. */ + +enum tree_code +invert_minmax_code (enum tree_code code) +{ + switch (code) { + case MIN_EXPR: + return MAX_EXPR; + case MAX_EXPR: + return MIN_EXPR; + default: + gcc_unreachable (); + } +} + /* The function minmax_replacement does the main work of doing the minmax replacement. Return true if the replacement is done. Otherwise return false. BB is the basic block where the replacement is going to be done on. ARG0 - is argument 0 from the PHI. Likewise for ARG1. */ + is argument 0 from the PHI. Likewise for ARG1. + + If THREEWAY_P then expect the BB to be laid out in diamond shape with each + BB containing only a MIN or MAX expression. */ static bool -minmax_replacement (basic_block cond_bb, basic_block middle_bb, - edge e0, edge e1, gphi *phi, tree arg0, tree arg1) +minmax_replacement (basic_block cond_bb, basic_block middle_bb, basic_block alt_middle_bb, + edge e0, edge e1, gphi *phi, tree arg0, tree arg1, bool threeway_p) { tree result; edge true_edge, false_edge; @@ -1896,16 +1960,20 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb, if (false_edge->dest == middle_bb) false_edge = EDGE_SUCC (false_edge->dest, 0); + /* When THREEWAY_P then e1 will point to the edge of the final transition + from middle-bb to end. */ if (true_edge == e0) { - gcc_assert (false_edge == e1); + if (!threeway_p) + gcc_assert (false_edge == e1); arg_true = arg0; arg_false = arg1; } else { gcc_assert (false_edge == e0); - gcc_assert (true_edge == e1); + if (!threeway_p) + gcc_assert (true_edge == e1); arg_true = arg1; arg_false = arg0; } @@ -1937,6 +2005,165 @@ minmax_replacement (basic_block cond_bb, basic_block middle_bb, else return false; } + else if (middle_bb != alt_middle_bb && threeway_p) + { + /* Recognize the following case: + + if (smaller < larger) + a = MIN (smaller, c); + else + b = MIN (larger, c); + x = PHI <a, b> + + This is equivalent to + + a = MIN (smaller, c); + x = MIN (larger, a); */ + + gimple *assign = last_and_only_stmt (middle_bb); + tree lhs, op0, op1, bound; + tree alt_lhs, alt_op0, alt_op1; + bool invert = false; + + if (!single_pred_p (middle_bb) + || !single_pred_p (alt_middle_bb) + || !single_succ_p (middle_bb) + || !single_succ_p (alt_middle_bb)) + return false; + + /* When THREEWAY_P then e1 will point to the edge of the final transition + from middle-bb to end. */ + if (true_edge == e0) + gcc_assert (false_edge == EDGE_PRED (e1->src, 0)); + else + gcc_assert (true_edge == EDGE_PRED (e1->src, 0)); + + bool valid_minmax_p = false; + gimple_stmt_iterator it1 + = gsi_start_nondebug_after_labels_bb (middle_bb); + gimple_stmt_iterator it2 + = gsi_start_nondebug_after_labels_bb (alt_middle_bb); + if (gsi_one_nondebug_before_end_p (it1) + && gsi_one_nondebug_before_end_p (it2)) + { + gimple *stmt1 = gsi_stmt (it1); + gimple *stmt2 = gsi_stmt (it2); + if (is_gimple_assign (stmt1) && is_gimple_assign (stmt2)) + { + enum tree_code code1 = gimple_assign_rhs_code (stmt1); + enum tree_code code2 = gimple_assign_rhs_code (stmt2); + valid_minmax_p = (code1 == MIN_EXPR || code1 == MAX_EXPR) + && (code2 == MIN_EXPR || code2 == MAX_EXPR); + } + } + + if (!valid_minmax_p) + return false; + + if (!assign + || gimple_code (assign) != GIMPLE_ASSIGN) + return false; + + lhs = gimple_assign_lhs (assign); + ass_code = gimple_assign_rhs_code (assign); + if (ass_code != MAX_EXPR && ass_code != MIN_EXPR) + return false; + + op0 = gimple_assign_rhs1 (assign); + op1 = gimple_assign_rhs2 (assign); + + assign = last_and_only_stmt (alt_middle_bb); + if (!assign + || gimple_code (assign) != GIMPLE_ASSIGN) + return false; + + alt_lhs = gimple_assign_lhs (assign); + if (ass_code != gimple_assign_rhs_code (assign)) + return false; + + if (!operand_equal_for_phi_arg_p (lhs, arg_true) + || !operand_equal_for_phi_arg_p (alt_lhs, arg_false)) + return false; + + alt_op0 = gimple_assign_rhs1 (assign); + alt_op1 = gimple_assign_rhs2 (assign); + + if ((operand_equal_for_phi_arg_p (op0, smaller) + || (alt_smaller + && operand_equal_for_phi_arg_p (op0, alt_smaller))) + && (operand_equal_for_phi_arg_p (alt_op0, larger) + || (alt_larger + && operand_equal_for_phi_arg_p (alt_op0, alt_larger)))) + { + /* We got here if the condition is true, i.e., SMALLER < LARGER. */ + if (!operand_equal_for_phi_arg_p (op1, alt_op1)) + return false; + + if ((arg0 = strip_bit_not (op0)) != NULL + && (arg1 = strip_bit_not (alt_op0)) != NULL + && (bound = strip_bit_not (op1)) != NULL) + { + minmax = MAX_EXPR; + ass_code = invert_minmax_code (ass_code); + invert = true; + } + else + { + bound = op1; + minmax = MIN_EXPR; + arg0 = op0; + arg1 = alt_op0; + } + } + else if ((operand_equal_for_phi_arg_p (op0, larger) + || (alt_larger + && operand_equal_for_phi_arg_p (op0, alt_larger))) + && (operand_equal_for_phi_arg_p (alt_op0, smaller) + || (alt_smaller + && operand_equal_for_phi_arg_p (alt_op0, alt_smaller)))) + { + /* We got here if the condition is true, i.e., SMALLER > LARGER. */ + if (!operand_equal_for_phi_arg_p (op1, alt_op1)) + return false; + + if ((arg0 = strip_bit_not (op0)) != NULL + && (arg1 = strip_bit_not (alt_op0)) != NULL + && (bound = strip_bit_not (op1)) != NULL) + { + minmax = MIN_EXPR; + ass_code = invert_minmax_code (ass_code); + invert = true; + } + else + { + bound = op1; + minmax = MAX_EXPR; + arg0 = op0; + arg1 = alt_op0; + } + } + else + return false; + + /* Emit the statement to compute min/max. */ + location_t locus = gimple_location (last_stmt (cond_bb)); + gimple_seq stmts = NULL; + tree phi_result = PHI_RESULT (phi); + result = gimple_build (&stmts, locus, minmax, TREE_TYPE (phi_result), + arg0, bound); + result = gimple_build (&stmts, locus, ass_code, TREE_TYPE (phi_result), + result, arg1); + if (invert) + result = gimple_build (&stmts, locus, BIT_NOT_EXPR, TREE_TYPE (phi_result), + result); + + gsi = gsi_last_bb (cond_bb); + gsi_insert_seq_before (&gsi, stmts, GSI_NEW_STMT); + + replace_phi_edge_with_variable (cond_bb, e1, phi, result); + + return true; + } else { /* Recognize the following case, assuming d <= u: diff --git a/gcc/tree-ssa-threadbackward.cc b/gcc/tree-ssa-threadbackward.cc index 90f5331..332a1d2 100644 --- a/gcc/tree-ssa-threadbackward.cc +++ b/gcc/tree-ssa-threadbackward.cc @@ -91,7 +91,6 @@ private: edge maybe_register_path (); void maybe_register_path_dump (edge taken_edge); void find_paths_to_names (basic_block bb, bitmap imports); - void resolve_phi (gphi *phi, bitmap imports); edge find_taken_edge (const vec<basic_block> &path); edge find_taken_edge_cond (const vec<basic_block> &path, gcond *); edge find_taken_edge_switch (const vec<basic_block> &path, gswitch *); @@ -244,10 +243,9 @@ back_threader::maybe_register_path () bool irreducible = false; if (m_profit.profitable_path_p (m_path, m_name, taken_edge, &irreducible) - && debug_counter ()) + && debug_counter () + && m_registry.register_path (m_path, taken_edge)) { - m_registry.register_path (m_path, taken_edge); - if (irreducible) vect_free_loop_info_assumptions (m_path[0]->loop_father); } @@ -335,71 +333,6 @@ back_threader::find_taken_edge_cond (const vec<basic_block> &path, return NULL; } -// Populate a vector of trees from a bitmap. - -static inline void -populate_worklist (vec<tree> &worklist, bitmap bits) -{ - bitmap_iterator bi; - unsigned i; - - EXECUTE_IF_SET_IN_BITMAP (bits, 0, i, bi) - { - tree name = ssa_name (i); - worklist.quick_push (name); - } -} - -// Find jump threading paths that go through a PHI. - -void -back_threader::resolve_phi (gphi *phi, bitmap interesting) -{ - if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_phi_result (phi))) - return; - - for (size_t i = 0; i < gimple_phi_num_args (phi); ++i) - { - edge e = gimple_phi_arg_edge (phi, i); - - // This is like path_crosses_loops in profitable_path_p but more - // restrictive to avoid peeling off loop iterations (see - // tree-ssa/pr14341.c for an example). - bool profitable_p = m_path[0]->loop_father == e->src->loop_father; - if (!profitable_p) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, - " FAIL: path through PHI in bb%d (incoming bb:%d) crosses loop\n", - e->dest->index, e->src->index); - fprintf (dump_file, "path: %d->", e->src->index); - dump_path (dump_file, m_path); - fprintf (dump_file, "->xx REJECTED\n"); - } - continue; - } - - tree arg = gimple_phi_arg_def (phi, i); - unsigned v = 0; - - if (TREE_CODE (arg) == SSA_NAME - && !bitmap_bit_p (interesting, SSA_NAME_VERSION (arg))) - { - // Record that ARG is interesting when searching down this path. - v = SSA_NAME_VERSION (arg); - gcc_checking_assert (v != 0); - bitmap_set_bit (interesting, v); - bitmap_set_bit (m_imports, v); - } - - find_paths_to_names (e->src, interesting); - - if (v) - bitmap_clear_bit (interesting, v); - } -} - // Find jump threading paths to any of the SSA names in the // INTERESTING bitmap, and register any such paths. // @@ -417,51 +350,75 @@ back_threader::find_paths_to_names (basic_block bb, bitmap interesting) if (m_path.length () > 1 && (!m_profit.profitable_path_p (m_path, m_name, NULL) || maybe_register_path ())) - { - m_path.pop (); - m_visited_bbs.remove (bb); - return; - } + ; - auto_bitmap processed; - bool done = false; - // We use a worklist instead of iterating through the bitmap, - // because we may add new items in-flight. - auto_vec<tree> worklist (bitmap_count_bits (interesting)); - populate_worklist (worklist, interesting); - while (!worklist.is_empty ()) + // Continue looking for ways to extend the path + else { - tree name = worklist.pop (); - unsigned i = SSA_NAME_VERSION (name); - gimple *def_stmt = SSA_NAME_DEF_STMT (name); - basic_block def_bb = gimple_bb (def_stmt); - - // Process any PHIs defined in this block. - if (def_bb == bb - && bitmap_set_bit (processed, i) - && gimple_code (def_stmt) == GIMPLE_PHI) + // For further greedy searching we want to remove interesting + // names defined in BB but add ones on the PHI edges for the + // respective edges. We do this by starting with all names + // not defined in BB as interesting, collecting a list of + // interesting PHIs in BB on the fly. Then we iterate over + // predecessor edges, adding interesting PHI edge defs to + // the set of interesting names to consider when processing it. + auto_bitmap new_interesting; + auto_vec<gphi *, 4> interesting_phis; + bitmap_iterator bi; + unsigned i; + EXECUTE_IF_SET_IN_BITMAP (interesting, 0, i, bi) { - resolve_phi (as_a<gphi *> (def_stmt), interesting); - done = true; - break; + tree name = ssa_name (i); + gimple *def_stmt = SSA_NAME_DEF_STMT (name); + if (gimple_bb (def_stmt) != bb) + bitmap_set_bit (new_interesting, i); + else if (gphi *phi = dyn_cast<gphi *> (def_stmt)) + { + tree res = gimple_phi_result (phi); + if (!SSA_NAME_OCCURS_IN_ABNORMAL_PHI (res)) + interesting_phis.safe_push (phi); + } } - } - // If there are interesting names not yet processed, keep looking. - if (!done) - { - bitmap_and_compl_into (interesting, processed); - if (!bitmap_empty_p (interesting)) + if (!bitmap_empty_p (new_interesting) + || !interesting_phis.is_empty ()) { + auto_vec<tree, 4> unwind (interesting_phis.length ()); edge_iterator iter; edge e; FOR_EACH_EDGE (e, iter, bb->preds) - if ((e->flags & EDGE_ABNORMAL) == 0) - find_paths_to_names (e->src, interesting); + { + if (e->flags & EDGE_ABNORMAL + // This is like path_crosses_loops in profitable_path_p but + // more restrictive to avoid peeling off loop iterations (see + // tree-ssa/pr14341.c for an example). + // ??? Note this restriction only applied when visiting an + // interesting PHI with the former resolve_phi. + || (!interesting_phis.is_empty () + && m_path[0]->loop_father != e->src->loop_father)) + continue; + for (gphi *phi : interesting_phis) + { + tree def = PHI_ARG_DEF_FROM_EDGE (phi, e); + if (TREE_CODE (def) == SSA_NAME) + if (bitmap_set_bit (new_interesting, + SSA_NAME_VERSION (def))) + { + bitmap_set_bit (m_imports, SSA_NAME_VERSION (def)); + unwind.quick_push (def); + } + } + find_paths_to_names (e->src, new_interesting); + // Restore new_interesting. We leave m_imports alone since + // we do not prune defs in BB from it and separately keeping + // track of which bits to unwind isn't worth the trouble. + for (tree def : unwind) + bitmap_clear_bit (new_interesting, SSA_NAME_VERSION (def)); + unwind.truncate (0); + } } } // Reset things to their original state. - bitmap_ior_into (interesting, processed); m_path.pop (); m_visited_bbs.remove (bb); } @@ -777,6 +734,13 @@ back_threader_profitability::profitable_path_p (const vec<basic_block> &m_path, "exceeds PARAM_MAX_FSM_THREAD_PATH_INSNS.\n"); return false; } + if (taken_edge && probably_never_executed_edge_p (cfun, taken_edge)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " FAIL: Jump-thread path not considered: " + "path leads to probably never executed edge.\n"); + return false; + } edge entry = find_edge (m_path[m_path.length () - 1], m_path[m_path.length () - 2]); if (probably_never_executed_edge_p (cfun, entry)) @@ -787,7 +751,7 @@ back_threader_profitability::profitable_path_p (const vec<basic_block> &m_path, return false; } } - else if (!m_speed_p && n_insns > 1) + else if (n_insns > 1) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, " FAIL: Jump-thread path not considered: " @@ -893,8 +857,7 @@ back_threader_registry::register_path (const vec<basic_block> &m_path, } push_edge (jump_thread_path, taken_edge, EDGE_NO_COPY_SRC_BLOCK); - register_jump_thread (jump_thread_path); - return true; + return register_jump_thread (jump_thread_path); } // Thread all suitable paths in the current function. diff --git a/gcc/tree-ssa-threadupdate.cc b/gcc/tree-ssa-threadupdate.cc index 0f2b319..59c268a 100644 --- a/gcc/tree-ssa-threadupdate.cc +++ b/gcc/tree-ssa-threadupdate.cc @@ -2679,7 +2679,8 @@ fwd_jt_path_registry::update_cfg (bool may_peel_loop_headers) { edge e = (*path)[j]->e; if (m_removed_edges->find_slot (e, NO_INSERT) - || ((*path)[j]->type == EDGE_COPY_SRC_BLOCK + || (((*path)[j]->type == EDGE_COPY_SRC_BLOCK + || (*path)[j]->type == EDGE_COPY_SRC_JOINER_BLOCK) && !can_duplicate_block_p (e->src))) break; } diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc index 8b5ab54..ea3b83c 100644 --- a/gcc/value-range-storage.cc +++ b/gcc/value-range-storage.cc @@ -40,7 +40,8 @@ vrange_storage::alloc_slot (const vrange &r) if (is_a <irange> (r)) return irange_storage_slot::alloc_slot (*m_alloc, as_a <irange> (r)); - + if (is_a <frange> (r)) + return frange_storage_slot::alloc_slot (*m_alloc, as_a <frange> (r)); return NULL; } @@ -55,6 +56,12 @@ vrange_storage::set_vrange (void *slot, const vrange &r) gcc_checking_assert (s->fits_p (as_a <irange> (r))); s->set_irange (as_a <irange> (r)); } + else if (is_a <frange> (r)) + { + frange_storage_slot *s = static_cast <frange_storage_slot *> (slot); + gcc_checking_assert (s->fits_p (as_a <frange> (r))); + s->set_frange (as_a <frange> (r)); + } else gcc_unreachable (); } @@ -70,6 +77,12 @@ vrange_storage::get_vrange (const void *slot, vrange &r, tree type) = static_cast <const irange_storage_slot *> (slot); s->get_irange (as_a <irange> (r), type); } + else if (is_a <frange> (r)) + { + const frange_storage_slot *s + = static_cast <const frange_storage_slot *> (slot); + s->get_frange (as_a <frange> (r), type); + } else gcc_unreachable (); } @@ -85,6 +98,12 @@ vrange_storage::fits_p (const void *slot, const vrange &r) = static_cast <const irange_storage_slot *> (slot); return s->fits_p (as_a <irange> (r)); } + if (is_a <frange> (r)) + { + const frange_storage_slot *s + = static_cast <const frange_storage_slot *> (slot); + return s->fits_p (as_a <frange> (r)); + } gcc_unreachable (); return false; } @@ -215,3 +234,43 @@ debug (const irange_storage_slot &storage) storage.dump (); fprintf (stderr, "\n"); } + +// Implementation of frange_storage_slot. + +frange_storage_slot * +frange_storage_slot::alloc_slot (vrange_allocator &allocator, const frange &r) +{ + size_t size = sizeof (frange_storage_slot); + frange_storage_slot *p + = static_cast <frange_storage_slot *> (allocator.alloc (size)); + new (p) frange_storage_slot (r); + return p; +} + +void +frange_storage_slot::set_frange (const frange &r) +{ + gcc_checking_assert (fits_p (r)); + gcc_checking_assert (!r.undefined_p ()); + + m_props = r.m_props; +} + +void +frange_storage_slot::get_frange (frange &r, tree type) const +{ + gcc_checking_assert (r.supports_type_p (type)); + + r.set_varying (type); + r.m_props = m_props; + r.normalize_kind (); + + if (flag_checking) + r.verify_range (); +} + +bool +frange_storage_slot::fits_p (const frange &) const +{ + return true; +} diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h index 5a3336b..3fac5ea 100644 --- a/gcc/value-range-storage.h +++ b/gcc/value-range-storage.h @@ -100,6 +100,25 @@ private: trailing_wide_ints<MAX_INTS> m_ints; }; +// A chunk of memory to store an frange to long term memory. + +class GTY (()) frange_storage_slot +{ + public: + static frange_storage_slot *alloc_slot (vrange_allocator &, const frange &r); + void set_frange (const frange &r); + void get_frange (frange &r, tree type) const; + bool fits_p (const frange &) const; + private: + frange_storage_slot (const frange &r) { set_frange (r); } + DISABLE_COPY_AND_ASSIGN (frange_storage_slot); + + // We can get away with just storing the properties because the type + // can be gotten from the SSA, and UNDEFINED is unsupported, so it + // can only be a range. + frange_props m_props; +}; + class obstack_vrange_allocator : public vrange_allocator { public: diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 2923f4f..a2273f5 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -105,7 +105,7 @@ vrange::type () const } bool -vrange::supports_type_p (tree) const +vrange::supports_type_p (const_tree) const { return false; } @@ -229,7 +229,7 @@ vrange::dump (FILE *file) const } bool -irange::supports_type_p (tree type) const +irange::supports_type_p (const_tree type) const { return supports_p (type); } @@ -248,78 +248,108 @@ irange::set_nonnegative (tree type) set (build_int_cst (type, 0), TYPE_MAX_VALUE (type)); } -unsupported_range::unsupported_range () -{ - m_discriminator = VR_UNKNOWN; - set_undefined (); -} - void frange::accept (const vrange_visitor &v) const { v.visit (*this); } -// Setter for franges. Currently only singletons are supported. +// Helper function to compare floats. Returns TRUE if op1 .CODE. op2 +// is nonzero. + +static inline bool +tree_compare (tree_code code, tree op1, tree op2) +{ + return !integer_zerop (fold_build2 (code, integer_type_node, op1, op2)); +} + +// Setter for franges. void frange::set (tree min, tree max, value_range_kind kind) { - gcc_checking_assert (kind == VR_RANGE); - gcc_checking_assert (operand_equal_p (min, max)); gcc_checking_assert (TREE_CODE (min) == REAL_CST); + gcc_checking_assert (TREE_CODE (max) == REAL_CST); + + if (kind == VR_UNDEFINED) + { + set_undefined (); + return; + } + + // Treat VR_ANTI_RANGE and VR_VARYING as varying. + if (kind != VR_RANGE) + { + set_varying (TREE_TYPE (min)); + return; + } m_kind = kind; m_type = TREE_TYPE (min); + m_props.set_varying (); - REAL_VALUE_TYPE *const cst = TREE_REAL_CST_PTR (min); - if (real_isnan (cst)) - m_props.nan_set_yes (); - else - m_props.nan_set_no (); + bool is_min = vrp_val_is_min (min); + bool is_max = vrp_val_is_max (max); + bool is_nan = (real_isnan (TREE_REAL_CST_PTR (min)) + || real_isnan (TREE_REAL_CST_PTR (max))); + + // Ranges with a NAN and a non-NAN endpoint are nonsensical. + gcc_checking_assert (!is_nan || operand_equal_p (min, max)); - if (real_isinf (cst)) + // The properties for singletons can be all set ahead of time. + if (operand_equal_p (min, max)) { - if (real_isneg (cst)) - { - m_props.inf_set_no (); - m_props.ninf_set_yes (); - } + // Set INF properties. + if (is_min) + m_props.ninf_set_yes (); else - { - m_props.inf_set_yes (); - m_props.ninf_set_no (); - } + m_props.ninf_set_no (); + if (is_max) + m_props.inf_set_yes (); + else + m_props.inf_set_no (); + // Set NAN property. + if (is_nan) + m_props.nan_set_yes (); + else + m_props.nan_set_no (); } else { - m_props.inf_set_no (); - m_props.ninf_set_no (); + // Mark when the endpoints can't be +-INF. + if (!is_min) + m_props.ninf_set_no (); + if (!is_max) + m_props.inf_set_no (); } + // Check for swapped ranges. + gcc_checking_assert (is_nan || tree_compare (LE_EXPR, min, max)); + + normalize_kind (); + if (flag_checking) verify_range (); } -// Normalize range to VARYING or UNDEFINED, or vice versa. +// Normalize range to VARYING or UNDEFINED, or vice versa. Return +// TRUE if anything changed. // // A range with no known properties can be dropped to VARYING. // Similarly, a VARYING with any properties should be dropped to a // VR_RANGE. Normalizing ranges upon changing them ensures there is // only one representation for a given range. -void +bool frange::normalize_kind () { if (m_kind == VR_RANGE) { // No FP properties set means varying. - if (m_props.nan_varying_p () - && m_props.inf_varying_p () - && m_props.ninf_varying_p ()) + if (m_props.varying_p ()) { set_varying (m_type); - return; + return true; } // Undefined is viral. if (m_props.nan_undefined_p () @@ -327,17 +357,19 @@ frange::normalize_kind () || m_props.ninf_undefined_p ()) { set_undefined (); - return; + return true; } } else if (m_kind == VR_VARYING) { // If a VARYING has any FP properties, it's no longer VARYING. - if (!m_props.nan_varying_p () - || !m_props.inf_varying_p () - || !m_props.ninf_varying_p ()) - m_kind = VR_RANGE; + if (!m_props.varying_p ()) + { + m_kind = VR_RANGE; + return true; + } } + return false; } bool @@ -354,7 +386,7 @@ frange::union_ (const vrange &v) } bool ret = m_props.union_ (r.m_props); - normalize_kind (); + ret |= normalize_kind (); if (flag_checking) verify_range (); @@ -380,7 +412,7 @@ frange::intersect (const vrange &v) } bool ret = m_props.intersect (r.m_props); - normalize_kind (); + ret |= normalize_kind (); if (flag_checking) verify_range (); @@ -416,7 +448,7 @@ frange::operator== (const frange &src) const } bool -frange::supports_type_p (tree type) const +frange::supports_type_p (const_tree type) const { return supports_p (type); } @@ -429,12 +461,11 @@ frange::verify_range () gcc_checking_assert (m_props.undefined_p ()); return; } - else if (varying_p ()) + if (varying_p ()) { gcc_checking_assert (m_props.varying_p ()); return; } - gcc_checking_assert (m_kind == VR_RANGE); gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ()); } diff --git a/gcc/value-range.h b/gcc/value-range.h index e43fbe3..856947d 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -78,7 +78,7 @@ public: virtual void accept (const class vrange_visitor &v) const = 0; virtual void set (tree, tree, value_range_kind = VR_RANGE); virtual tree type () const; - virtual bool supports_type_p (tree type) const; + virtual bool supports_type_p (const_tree type) const; virtual void set_varying (tree type); virtual void set_undefined (); virtual bool union_ (const vrange &); @@ -122,8 +122,8 @@ public: virtual void set_undefined () override; // Range types. - static bool supports_p (tree type); - virtual bool supports_type_p (tree type) const override; + static bool supports_p (const_tree type); + virtual bool supports_type_p (const_tree type) const override; virtual tree type () const override; // Iteration over sub-ranges. @@ -250,7 +250,15 @@ private: class unsupported_range : public vrange { public: - unsupported_range (); + unsupported_range () + { + m_discriminator = VR_UNKNOWN; + set_undefined (); + } + virtual void set_undefined () final override + { + m_kind = VR_UNDEFINED; + } virtual void accept (const vrange_visitor &v) const override; }; @@ -336,10 +344,9 @@ class frange : public vrange public: frange (); frange (const frange &); - static bool supports_p (tree type) + static bool supports_p (const_tree type) { - // Disabled until floating point range-ops come live. - return 0 && SCALAR_FLOAT_TYPE_P (type); + return SCALAR_FLOAT_TYPE_P (type); } virtual tree type () const override; virtual void set (tree, tree, value_range_kind = VR_RANGE) override; @@ -347,7 +354,7 @@ public: virtual void set_undefined () override; virtual bool union_ (const vrange &) override; virtual bool intersect (const vrange &) override; - virtual bool supports_type_p (tree type) const override; + virtual bool supports_type_p (const_tree type) const override; virtual void accept (const vrange_visitor &v) const override; frange& operator= (const frange &); bool operator== (const frange &) const; @@ -359,7 +366,7 @@ public: FRANGE_PROP_ACCESSOR(ninf) private: void verify_range (); - void normalize_kind (); + bool normalize_kind (); frange_props m_props; tree m_type; @@ -457,7 +464,7 @@ public: operator vrange &(); operator const vrange &() const; void dump (FILE *) const; - static bool supports_type_p (tree type); + static bool supports_type_p (const_tree type); // Convenience methods for vrange compatability. void set (tree min, tree max, value_range_kind kind = VR_RANGE) @@ -588,7 +595,7 @@ Value_Range::operator const vrange &() const // Return TRUE if TYPE is supported by the vrange infrastructure. inline bool -Value_Range::supports_type_p (tree type) +Value_Range::supports_type_p (const_tree type) { return irange::supports_p (type) || frange::supports_p (type); } @@ -730,7 +737,7 @@ irange::nonzero_p () const } inline bool -irange::supports_p (tree type) +irange::supports_p (const_tree type) { return INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type); } @@ -1010,6 +1017,45 @@ irange::normalize_kind () } } +// Return the maximum value for TYPE. + +inline tree +vrp_val_max (const_tree type) +{ + if (INTEGRAL_TYPE_P (type)) + return TYPE_MAX_VALUE (type); + if (POINTER_TYPE_P (type)) + { + wide_int max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + return wide_int_to_tree (const_cast<tree> (type), max); + } + if (frange::supports_p (type)) + { + REAL_VALUE_TYPE real; + real_inf (&real); + return build_real (const_cast <tree> (type), real); + } + return NULL_TREE; +} + +// Return the minimum value for TYPE. + +inline tree +vrp_val_min (const_tree type) +{ + if (INTEGRAL_TYPE_P (type)) + return TYPE_MIN_VALUE (type); + if (POINTER_TYPE_P (type)) + return build_zero_cst (const_cast<tree> (type)); + if (frange::supports_p (type)) + { + REAL_VALUE_TYPE real, real_ninf; + real_inf (&real); + real_ninf = real_value_negate (&real); + return build_real (const_cast <tree> (type), real_ninf); + } + return NULL_TREE; +} // Supporting methods for frange. @@ -1039,7 +1085,6 @@ inline frange::frange () { m_discriminator = VR_FRANGE; - m_type = nullptr; set_undefined (); } @@ -1072,32 +1117,4 @@ frange::set_undefined () m_props.set_undefined (); } - -// Return the maximum value for TYPE. - -inline tree -vrp_val_max (const_tree type) -{ - if (INTEGRAL_TYPE_P (type)) - return TYPE_MAX_VALUE (type); - if (POINTER_TYPE_P (type)) - { - wide_int max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); - return wide_int_to_tree (const_cast<tree> (type), max); - } - return NULL_TREE; -} - -// Return the minimum value for TYPE. - -inline tree -vrp_val_min (const_tree type) -{ - if (INTEGRAL_TYPE_P (type)) - return TYPE_MIN_VALUE (type); - if (POINTER_TYPE_P (type)) - return build_zero_cst (const_cast<tree> (type)); - return NULL_TREE; -} - #endif // GCC_VALUE_RANGE_H diff --git a/gcc/value-relation.cc b/gcc/value-relation.cc index a447021..3f0957c 100644 --- a/gcc/value-relation.cc +++ b/gcc/value-relation.cc @@ -1400,16 +1400,7 @@ path_oracle::killing_def (tree ssa) unsigned v = SSA_NAME_VERSION (ssa); bitmap_set_bit (m_killed_defs, v); - - // Walk the equivalency list and remove SSA from any equivalencies. - if (bitmap_bit_p (m_equiv.m_names, v)) - { - for (equiv_chain *ptr = m_equiv.m_next; ptr; ptr = ptr->m_next) - if (bitmap_bit_p (ptr->m_names, v)) - bitmap_clear_bit (ptr->m_names, v); - } - else - bitmap_set_bit (m_equiv.m_names, v); + bitmap_set_bit (m_equiv.m_names, v); // Now add an equivalency with itself so we don't look to the root oracle. bitmap b = BITMAP_ALLOC (&m_bitmaps); diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index d1b2b2c..9d7f83d 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,10 @@ +2022-08-01 Jakub Jelinek <jakub@redhat.com> + + PR libfortran/106079 + * io/transfer.c (formatted_transfer_scalar_read, + formatted_transfer_scalar_write): For type BT_REAL with kind 17 + change kind to 16 before calling read_radix or write_{b,o,z}. + 2022-06-29 Jakub Jelinek <jakub@redhat.com> * mk-kinds-h.sh: Change __float128 to _Float128 in a comment. diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c index f543dfd..2760929 100644 --- a/libgfortran/io/transfer.c +++ b/libgfortran/io/transfer.c @@ -1614,6 +1614,10 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind if (!(compile_options.allow_std & GFC_STD_F2008) && require_type (dtp, BT_INTEGER, type, f)) return; +#ifdef HAVE_GFC_REAL_17 + if (type == BT_REAL && kind == 17) + kind = 16; +#endif read_radix (dtp, f, p, kind, 2); break; @@ -1626,6 +1630,10 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind if (!(compile_options.allow_std & GFC_STD_F2008) && require_type (dtp, BT_INTEGER, type, f)) return; +#ifdef HAVE_GFC_REAL_17 + if (type == BT_REAL && kind == 17) + kind = 16; +#endif read_radix (dtp, f, p, kind, 8); break; @@ -1638,6 +1646,10 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind if (!(compile_options.allow_std & GFC_STD_F2008) && require_type (dtp, BT_INTEGER, type, f)) return; +#ifdef HAVE_GFC_REAL_17 + if (type == BT_REAL && kind == 17) + kind = 16; +#endif read_radix (dtp, f, p, kind, 16); break; @@ -2085,6 +2097,10 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin if (!(compile_options.allow_std & GFC_STD_F2008) && require_type (dtp, BT_INTEGER, type, f)) return; +#ifdef HAVE_GFC_REAL_17 + if (type == BT_REAL && kind == 17) + kind = 16; +#endif write_b (dtp, f, p, kind); break; @@ -2097,6 +2113,10 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin if (!(compile_options.allow_std & GFC_STD_F2008) && require_type (dtp, BT_INTEGER, type, f)) return; +#ifdef HAVE_GFC_REAL_17 + if (type == BT_REAL && kind == 17) + kind = 16; +#endif write_o (dtp, f, p, kind); break; @@ -2109,6 +2129,10 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin if (!(compile_options.allow_std & GFC_STD_F2008) && require_type (dtp, BT_INTEGER, type, f)) return; +#ifdef HAVE_GFC_REAL_17 + if (type == BT_REAL && kind == 17) + kind = 16; +#endif write_z (dtp, f, p, kind); break; diff --git a/libphobos/ChangeLog b/libphobos/ChangeLog index 2d4abbb..bf1c632 100644 --- a/libphobos/ChangeLog +++ b/libphobos/ChangeLog @@ -1,3 +1,14 @@ +2022-08-03 Iain Buclaw <ibuclaw@gdcproject.org> + + * configure: Regenerate. + * configure.ac (libtool_VERSION): Update to 4:0:0. + * libdruntime/MERGE: Merge upstream druntime d7772a2369. + * libdruntime/Makefile.am (DRUNTIME_DSOURCES): Add + core/internal/array/duplication.d. + * libdruntime/Makefile.in: Regenerate. + * src/MERGE: Merge upstream phobos 5748ca43f. + * testsuite/libphobos.gc/nocollect.d: + 2022-07-06 Iain Buclaw <ibuclaw@gdcproject.org> * libdruntime/MERGE: Merge upstream druntime 651389b5. diff --git a/libphobos/configure b/libphobos/configure index 9da06f0..69d2d44 100755 --- a/libphobos/configure +++ b/libphobos/configure @@ -15554,7 +15554,7 @@ SPEC_PHOBOS_DEPS="$LIBS" # Libdruntime / phobos soname version -libtool_VERSION=3:0:0 +libtool_VERSION=4:0:0 # Set default flags (after DRUNTIME_WERROR!) diff --git a/libphobos/configure.ac b/libphobos/configure.ac index 31209ba..8bdf733 100644 --- a/libphobos/configure.ac +++ b/libphobos/configure.ac @@ -253,7 +253,7 @@ SPEC_PHOBOS_DEPS="$LIBS" AC_SUBST(SPEC_PHOBOS_DEPS) # Libdruntime / phobos soname version -libtool_VERSION=3:0:0 +libtool_VERSION=4:0:0 AC_SUBST(libtool_VERSION) # Set default flags (after DRUNTIME_WERROR!) diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 6e25a9d..c358b69 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -651389b52243dcadb338dd0c14dd27e7850cda8d +d7772a236983ec37b92d21b28bad3cd2de57b945 The first line of this file holds the git revision number of the last -merge done from the dlang/druntime repository. +merge done from the dlang/dmd repository. diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index 56b332d..2e1e91d 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -174,14 +174,14 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/internal/array/appending.d core/internal/array/capacity.d \ core/internal/array/casting.d core/internal/array/comparison.d \ core/internal/array/concatenation.d core/internal/array/construction.d \ - core/internal/array/equality.d core/internal/array/operations.d \ - core/internal/array/utils.d core/internal/atomic.d \ - core/internal/attributes.d core/internal/container/array.d \ - core/internal/container/common.d core/internal/container/hashtab.d \ - core/internal/container/treap.d core/internal/convert.d \ - core/internal/dassert.d core/internal/destruction.d \ - core/internal/entrypoint.d core/internal/gc/bits.d \ - core/internal/gc/impl/conservative/gc.d \ + core/internal/array/duplication.d core/internal/array/equality.d \ + core/internal/array/operations.d core/internal/array/utils.d \ + core/internal/atomic.d core/internal/attributes.d \ + core/internal/container/array.d core/internal/container/common.d \ + core/internal/container/hashtab.d core/internal/container/treap.d \ + core/internal/convert.d core/internal/dassert.d \ + core/internal/destruction.d core/internal/entrypoint.d \ + core/internal/gc/bits.d core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 24865fb..de6656c 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -196,6 +196,7 @@ am__objects_1 = core/atomic.lo core/attribute.lo core/bitop.lo \ core/internal/array/comparison.lo \ core/internal/array/concatenation.lo \ core/internal/array/construction.lo \ + core/internal/array/duplication.lo \ core/internal/array/equality.lo \ core/internal/array/operations.lo core/internal/array/utils.lo \ core/internal/atomic.lo core/internal/attributes.lo \ @@ -841,14 +842,14 @@ DRUNTIME_DSOURCES = core/atomic.d core/attribute.d core/bitop.d \ core/internal/array/appending.d core/internal/array/capacity.d \ core/internal/array/casting.d core/internal/array/comparison.d \ core/internal/array/concatenation.d core/internal/array/construction.d \ - core/internal/array/equality.d core/internal/array/operations.d \ - core/internal/array/utils.d core/internal/atomic.d \ - core/internal/attributes.d core/internal/container/array.d \ - core/internal/container/common.d core/internal/container/hashtab.d \ - core/internal/container/treap.d core/internal/convert.d \ - core/internal/dassert.d core/internal/destruction.d \ - core/internal/entrypoint.d core/internal/gc/bits.d \ - core/internal/gc/impl/conservative/gc.d \ + core/internal/array/duplication.d core/internal/array/equality.d \ + core/internal/array/operations.d core/internal/array/utils.d \ + core/internal/atomic.d core/internal/attributes.d \ + core/internal/container/array.d core/internal/container/common.d \ + core/internal/container/hashtab.d core/internal/container/treap.d \ + core/internal/convert.d core/internal/dassert.d \ + core/internal/destruction.d core/internal/entrypoint.d \ + core/internal/gc/bits.d core/internal/gc/impl/conservative/gc.d \ core/internal/gc/impl/manual/gc.d core/internal/gc/impl/proto/gc.d \ core/internal/gc/os.d core/internal/gc/pooltable.d \ core/internal/gc/proxy.d core/internal/hash.d core/internal/lifetime.d \ @@ -1208,6 +1209,8 @@ core/internal/array/concatenation.lo: \ core/internal/array/$(am__dirstamp) core/internal/array/construction.lo: \ core/internal/array/$(am__dirstamp) +core/internal/array/duplication.lo: \ + core/internal/array/$(am__dirstamp) core/internal/array/equality.lo: core/internal/array/$(am__dirstamp) core/internal/array/operations.lo: \ core/internal/array/$(am__dirstamp) diff --git a/libphobos/libdruntime/core/cpuid.d b/libphobos/libdruntime/core/cpuid.d index e31f776..1c2ac06 100644 --- a/libphobos/libdruntime/core/cpuid.d +++ b/libphobos/libdruntime/core/cpuid.d @@ -170,6 +170,8 @@ public: bool hle() {return _hle;} /// Is RTM (restricted transactional memory) supported bool rtm() {return _rtm;} + /// Is AVX512F supported + bool avx512f() {return _avx512f;} /// Is rdseed supported bool hasRdseed() {return _hasRdseed;} /// Is SHA supported @@ -279,6 +281,7 @@ private immutable bool _avx2; bool _hle; bool _rtm; + bool _avx512f; bool _hasRdseed; bool _hasSha; bool _amd3dnow; @@ -389,6 +392,7 @@ CpuFeatures* getCpuFeatures() @nogc nothrow enum : uint { FSGSBASE_BIT = 1 << 0, + SGX_BIT = 1 << 2, BMI1_BIT = 1 << 3, HLE_BIT = 1 << 4, AVX2_BIT = 1 << 5, @@ -397,8 +401,19 @@ CpuFeatures* getCpuFeatures() @nogc nothrow ERMS_BIT = 1 << 9, INVPCID_BIT = 1 << 10, RTM_BIT = 1 << 11, + AVX512F_BIT = 1 << 16, + AVX512DQ_BIT = 1 << 17, RDSEED_BIT = 1 << 18, + ADX_BIT = 1 << 19, + AVX512IFMA_BIT = 1 << 21, + CLFLUSHOPT_BIT = 1 << 23, + CLWB_BIT = 1 << 24, + AVX512PF_BIT = 1 << 26, + AVX512ER_BIT = 1 << 27, + AVX512CD_BIT = 1 << 28, SHA_BIT = 1 << 29, + AVX512BW_BIT = 1 << 30, + AVX512VL_BIT = 1 << 31, } // feature flags XFEATURES_ENABLED_MASK enum : ulong @@ -1122,6 +1137,7 @@ shared static this() _avx2 = avx && (cf.extfeatures & AVX2_BIT) != 0; _hle = (cf.extfeatures & HLE_BIT) != 0; _rtm = (cf.extfeatures & RTM_BIT) != 0; + _avx512f = (cf.extfeatures & AVX512F_BIT) != 0; _hasRdseed = (cf.extfeatures&RDSEED_BIT)!=0; _hasSha = (cf.extfeatures&SHA_BIT)!=0; _amd3dnow = (cf.amdfeatures&AMD_3DNOW_BIT)!=0; diff --git a/libphobos/libdruntime/core/int128.d b/libphobos/libdruntime/core/int128.d index e4326fd..46eb9b2 100644 --- a/libphobos/libdruntime/core/int128.d +++ b/libphobos/libdruntime/core/int128.d @@ -943,5 +943,3 @@ unittest assert(rol(C7_9, 1) == rol1(C7_9)); assert(ror(C7_9, 1) == ror1(C7_9)); } - - diff --git a/libphobos/libdruntime/core/internal/array/appending.d b/libphobos/libdruntime/core/internal/array/appending.d index d416efe..616d27c 100644 --- a/libphobos/libdruntime/core/internal/array/appending.d +++ b/libphobos/libdruntime/core/internal/array/appending.d @@ -30,26 +30,14 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) * Returns: * The new value of `px` * Bugs: - * This function template was ported from a much older runtime hook that bypassed safety, - * purity, and throwabilty checks. To prevent breaking existing code, this function template - * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. + * This function template was ported from a much older runtime hook that bypassed safety, + * purity, and throwabilty checks. To prevent breaking existing code, this function template + * is temporarily declared `@trusted pure` until the implementation can be brought up to modern D expectations. */ - static if (isCopyingNothrow!T) // `nothrow` deduction doesn't work, so this is needed - ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow - { - pragma(inline, false); - - mixin(_d_arrayappendcTXBody); - } - else - ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow - { - pragma(inline, false); - - mixin(_d_arrayappendcTXBody); - } - - private enum _d_arrayappendcTXBody = q{ + ref Tarr _d_arrayappendcTX(return ref scope Tarr px, size_t n) @trusted pure nothrow + { + // needed for CTFE: https://github.com/dlang/druntime/pull/3870#issuecomment-1178800718 + pragma(inline, false); version (D_TypeInfo) { auto ti = typeid(Tarr); @@ -64,7 +52,7 @@ template _d_arrayappendcTXImpl(Tarr : T[], T) } else assert(0, "Cannot append arrays if compiling without support for runtime type information!"); - }; + } /** * TraceGC wrapper around $(REF _d_arrayappendcTX, rt,array,appending,_d_arrayappendcTXImpl). diff --git a/libphobos/libdruntime/core/internal/array/duplication.d b/libphobos/libdruntime/core/internal/array/duplication.d new file mode 100644 index 0000000..41dfab6 --- /dev/null +++ b/libphobos/libdruntime/core/internal/array/duplication.d @@ -0,0 +1,346 @@ +/** +The `.dup` and `.idup` properties for Associative Arrays and Dynamic Arrays + +Copyright: Copyright Digital Mars 2000 - 2022. +License: Distributed under the $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + (See accompanying file LICENSE) +Source: $(DRUNTIMESRC core/internal/_array/_duplication.d) +*/ +module core.internal.array.duplication; + +private extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow; + +U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T)) +{ + if (__ctfe) + return _dupCtfe!(T, U)(a); + + import core.stdc.string : memcpy; + auto arr = _d_newarrayU(typeid(T[]), a.length); + memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length); + return *cast(U[]*) &arr; +} + +U[] _dupCtfe(T, U)(scope T[] a) +{ + static if (is(T : void)) + assert(0, "Cannot dup a void[] array at compile time."); + else + { + U[] res; + foreach (ref e; a) + res ~= e; + return res; + } +} + +U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T)) +{ + // note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch + // has the extra duty to infer _dup `@system` when the copy-constructor is `@system`. + if (__ctfe) + return _dupCtfe!(T, U)(a); + + import core.lifetime: copyEmplace; + U[] res = () @trusted { + auto arr = cast(U*) _d_newarrayU(typeid(T[]), a.length); + size_t i; + scope (failure) + { + import core.internal.lifetime: emplaceInitializer; + // Initialize all remaining elements to not destruct garbage + foreach (j; i .. a.length) + emplaceInitializer(cast() arr[j]); + } + for (; i < a.length; i++) + { + copyEmplace(a.ptr[i], arr[i]); + } + return cast(U[])(arr[0..a.length]); + } (); + + return res; +} + +// https://issues.dlang.org/show_bug.cgi?id=22107 +@safe unittest +{ + static int i; + @safe struct S + { + this(this) { i++; } + } + + void fun(scope S[] values...) @safe + { + values.dup; + } +} + +@safe unittest +{ + static struct S1 { int* p; } + static struct S2 { @disable this(); } + static struct S3 { @disable this(this); } + + int dg1() pure nothrow @safe + { + { + char[] m; + string i; + m = m.dup; + i = i.idup; + m = i.dup; + i = m.idup; + } + { + S1[] m; + immutable(S1)[] i; + m = m.dup; + i = i.idup; + static assert(!is(typeof(m.idup))); + static assert(!is(typeof(i.dup))); + } + { + S3[] m; + immutable(S3)[] i; + static assert(!is(typeof(m.dup))); + static assert(!is(typeof(i.idup))); + } + { + shared(S1)[] m; + m = m.dup; + static assert(!is(typeof(m.idup))); + } + { + int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0); + } + return 1; + } + + int dg2() pure nothrow @safe + { + { + S2[] m = [S2.init, S2.init]; + immutable(S2)[] i = [S2.init, S2.init]; + m = m.dup; + m = i.dup; + i = m.idup; + i = i.idup; + } + return 2; + } + + enum a = dg1(); + enum b = dg2(); + assert(dg1() == a); + assert(dg2() == b); +} + +@system unittest +{ + static struct Sunpure { this(this) @safe nothrow {} } + static struct Sthrow { this(this) @safe pure {} } + static struct Sunsafe { this(this) @system pure nothrow {} } + static struct Snocopy { @disable this(this); } + + [].dup!Sunpure; + [].dup!Sthrow; + cast(void) [].dup!Sunsafe; + static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); + static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); + static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); + static assert(!__traits(compiles, () { [].dup!Snocopy; })); + + [].idup!Sunpure; + [].idup!Sthrow; + [].idup!Sunsafe; + static assert(!__traits(compiles, () pure { [].idup!Sunpure; })); + static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; })); + static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; })); + static assert(!__traits(compiles, () { [].idup!Snocopy; })); +} + +@safe unittest +{ + // test that the copy-constructor is called with .dup + static struct ArrElem + { + int a; + this(int a) + { + this.a = a; + } + this(ref const ArrElem) + { + a = 2; + } + this(ref ArrElem) immutable + { + a = 3; + } + } + + auto arr = [ArrElem(1), ArrElem(1)]; + + ArrElem[] b = arr.dup; + assert(b[0].a == 2 && b[1].a == 2); + + immutable ArrElem[] c = arr.idup; + assert(c[0].a == 3 && c[1].a == 3); +} + +@system unittest +{ + static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} } + static struct Sthrow { this(ref const typeof(this)) @safe pure {} } + static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} } + [].dup!Sunpure; + [].dup!Sthrow; + cast(void) [].dup!Sunsafe; + static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); + static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); + static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); + + // for idup to work on structs that have copy constructors, it is necessary + // that the struct defines a copy constructor that creates immutable objects + static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} } + static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} } + static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} } + [].idup!ISunpure; + [].idup!ISthrow; + [].idup!ISunsafe; + static assert(!__traits(compiles, () pure { [].idup!ISunpure; })); + static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; })); + static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; })); +} + +@safe unittest +{ + static int*[] pureFoo() pure { return null; } + { char[] s; immutable x = s.dup; } + { immutable x = (cast(int*[])null).dup; } + { immutable x = pureFoo(); } + { immutable x = pureFoo().dup; } +} + +@safe unittest +{ + auto a = [1, 2, 3]; + auto b = a.dup; + debug(SENTINEL) {} else + assert(b.capacity >= 3); +} + +@system unittest +{ + // Bugzilla 12580 + void[] m = [0]; + shared(void)[] s = [cast(shared)1]; + immutable(void)[] i = [cast(immutable)2]; + + s = s.dup; + static assert(is(typeof(s.dup) == shared(void)[])); + + m = i.dup; + i = m.dup; + i = i.idup; + i = m.idup; + i = s.idup; + i = s.dup; + static assert(!__traits(compiles, m = s.dup)); +} + +@safe unittest +{ + // Bugzilla 13809 + static struct S + { + this(this) {} + ~this() {} + } + + S[] arr; + auto a = arr.dup; +} + +@system unittest +{ + // Bugzilla 16504 + static struct S + { + __gshared int* gp; + int* p; + // postblit and hence .dup could escape + this(this) { gp = p; } + } + + int p; + scope S[1] arr = [S(&p)]; + auto a = arr.dup; // dup does escape +} + +// https://issues.dlang.org/show_bug.cgi?id=21983 +// dup/idup destroys partially constructed arrays on failure +@safe unittest +{ + static struct SImpl(bool postblit) + { + int num; + long l = 0xDEADBEEF; + + static if (postblit) + { + this(this) + { + if (this.num == 3) + throw new Exception(""); + } + } + else + { + this(scope ref const SImpl other) + { + if (other.num == 3) + throw new Exception(""); + + this.num = other.num; + this.l = other.l; + } + } + + ~this() @trusted + { + if (l != 0xDEADBEEF) + { + import core.stdc.stdio; + printf("Unexpected value: %lld\n", l); + fflush(stdout); + assert(false); + } + } + } + + alias Postblit = SImpl!true; + alias Copy = SImpl!false; + + static int test(S)() + { + S[4] arr = [ S(1), S(2), S(3), S(4) ]; + try + { + arr.dup(); + assert(false); + } + catch (Exception) + { + return 1; + } + } + + static assert(test!Postblit()); + assert(test!Postblit()); + + static assert(test!Copy()); + assert(test!Copy()); +} diff --git a/libphobos/libdruntime/core/internal/dassert.d b/libphobos/libdruntime/core/internal/dassert.d index 2c51b86..07486c2 100644 --- a/libphobos/libdruntime/core/internal/dassert.d +++ b/libphobos/libdruntime/core/internal/dassert.d @@ -14,7 +14,7 @@ * * Copyright: D Language Foundation 2018 - 2020 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/druntime/blob/master/src/core/internal/dassert.d, _dassert.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/druntime/src/core/internal/dassert.d, _dassert.d) * Documentation: https://dlang.org/phobos/core_internal_dassert.html */ module core.internal.dassert; diff --git a/libphobos/libdruntime/core/runtime.d b/libphobos/libdruntime/core/runtime.d index d1378af..75e671c 100644 --- a/libphobos/libdruntime/core/runtime.d +++ b/libphobos/libdruntime/core/runtime.d @@ -4,7 +4,7 @@ * Copyright: Copyright Sean Kelly 2005 - 2009. * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly - * Source: $(LINK2 https://github.com/dlang/druntime/blob/master/src/core/runtime.d, _runtime.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/druntime/src/core/runtime.d, _runtime.d) * Documentation: https://dlang.org/phobos/core_runtime.html */ diff --git a/libphobos/libdruntime/core/stdc/errno.d b/libphobos/libdruntime/core/stdc/errno.d index 24b4138..c992a5f 100644 --- a/libphobos/libdruntime/core/stdc/errno.d +++ b/libphobos/libdruntime/core/stdc/errno.d @@ -8,7 +8,7 @@ * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Sean Kelly, Alex Rønne Petersen - * Source: https://github.com/dlang/druntime/blob/master/src/core/stdc/errno.d + * Source: https://github.com/dlang/dmd/blob/master/druntime/src/core/stdc/errno.d * Standards: ISO/IEC 9899:1999 (E) */ diff --git a/libphobos/libdruntime/core/stdc/stdio.d b/libphobos/libdruntime/core/stdc/stdio.d index 2e83811..fc98350 100644 --- a/libphobos/libdruntime/core/stdc/stdio.d +++ b/libphobos/libdruntime/core/stdc/stdio.d @@ -9,7 +9,7 @@ * (See accompanying file LICENSE) * Authors: Sean Kelly, * Alex Rønne Petersen - * Source: https://github.com/dlang/druntime/blob/master/src/core/stdc/stdio.d + * Source: https://github.com/dlang/dmd/blob/master/druntime/src/core/stdc/stdio.d * Standards: ISO/IEC 9899:1999 (E) */ @@ -1286,6 +1286,57 @@ version (MinGW) /// alias __mingw_scanf scanf; } +else version (CRuntime_Glibc) +{ + /// + pragma(printf) + int fprintf(FILE* stream, scope const char* format, scope const ...); + /// + pragma(scanf) + int __isoc99_fscanf(FILE* stream, scope const char* format, scope ...); + /// + alias fscanf = __isoc99_fscanf; + /// + pragma(printf) + int sprintf(scope char* s, scope const char* format, scope const ...); + /// + pragma(scanf) + int __isoc99_sscanf(scope const char* s, scope const char* format, scope ...); + /// + alias sscanf = __isoc99_sscanf; + /// + pragma(printf) + int vfprintf(FILE* stream, scope const char* format, va_list arg); + /// + pragma(scanf) + int __isoc99_vfscanf(FILE* stream, scope const char* format, va_list arg); + /// + alias vfscanf = __isoc99_vfscanf; + /// + pragma(printf) + int vsprintf(scope char* s, scope const char* format, va_list arg); + /// + pragma(scanf) + int __isoc99_vsscanf(scope const char* s, scope const char* format, va_list arg); + /// + alias vsscanf = __isoc99_vsscanf; + /// + pragma(printf) + int vprintf(scope const char* format, va_list arg); + /// + pragma(scanf) + int __isoc99_vscanf(scope const char* format, va_list arg); + /// + alias vscanf = __isoc99_vscanf; + /// + pragma(printf) + int printf(scope const char* format, scope const ...); + /// + pragma(scanf) + int __isoc99_scanf(scope const char* format, scope ...); + /// + alias scanf = __isoc99_scanf; +} else { /// diff --git a/libphobos/libdruntime/core/stdc/wchar_.d b/libphobos/libdruntime/core/stdc/wchar_.d index e8fb94b..d087029 100644 --- a/libphobos/libdruntime/core/stdc/wchar_.d +++ b/libphobos/libdruntime/core/stdc/wchar_.d @@ -127,30 +127,72 @@ alias wchar_t wint_t; /// enum wchar_t WEOF = 0xFFFF; -/// -int fwprintf(FILE* stream, const scope wchar_t* format, scope const ...); -/// -int fwscanf(FILE* stream, const scope wchar_t* format, scope ...); -/// -int swprintf(wchar_t* s, size_t n, const scope wchar_t* format, scope const ...); -/// -int swscanf(const scope wchar_t* s, const scope wchar_t* format, scope ...); -/// -int vfwprintf(FILE* stream, const scope wchar_t* format, va_list arg); -/// -int vfwscanf(FILE* stream, const scope wchar_t* format, va_list arg); -/// -int vswprintf(wchar_t* s, size_t n, const scope wchar_t* format, va_list arg); -/// -int vswscanf(const scope wchar_t* s, const scope wchar_t* format, va_list arg); -/// -int vwprintf(const scope wchar_t* format, va_list arg); -/// -int vwscanf(const scope wchar_t* format, va_list arg); -/// -int wprintf(const scope wchar_t* format, scope const ...); -/// -int wscanf(const scope wchar_t* format, scope ...); +version (CRuntime_Glibc) +{ + /// + int fwprintf(FILE* stream, const scope wchar_t* format, scope const ...); + /// + int __isoc99_fwscanf(FILE* stream, const scope wchar_t* format, scope ...); + /// + alias fwscanf = __isoc99_fwscanf; + /// + int swprintf(wchar_t* s, size_t n, const scope wchar_t* format, scope const ...); + /// + int __isoc99_swscanf(const scope wchar_t* s, const scope wchar_t* format, scope ...); + /// + alias swscanf = __isoc99_swscanf; + /// + int vfwprintf(FILE* stream, const scope wchar_t* format, va_list arg); + /// + int __isoc99_vfwscanf(FILE* stream, const scope wchar_t* format, va_list arg); + /// + alias vfwscanf = __isoc99_vfwscanf; + /// + int vswprintf(wchar_t* s, size_t n, const scope wchar_t* format, va_list arg); + /// + int __isoc99_vswscanf(const scope wchar_t* s, const scope wchar_t* format, va_list arg); + /// + alias vswscanf = __isoc99_vswscanf; + /// + int vwprintf(const scope wchar_t* format, va_list arg); + /// + int __isoc99_vwscanf(const scope wchar_t* format, va_list arg); + /// + alias vwscanf = __isoc99_vwscanf; + /// + int wprintf(const scope wchar_t* format, scope const ...); + /// + int __isoc99_wscanf(const scope wchar_t* format, scope ...); + /// + alias wscanf = __isoc99_wscanf; +} +else +{ + /// + int fwprintf(FILE* stream, const scope wchar_t* format, scope const ...); + /// + int fwscanf(FILE* stream, const scope wchar_t* format, scope ...); + /// + int swprintf(wchar_t* s, size_t n, const scope wchar_t* format, scope const ...); + /// + int swscanf(const scope wchar_t* s, const scope wchar_t* format, scope ...); + /// + int vfwprintf(FILE* stream, const scope wchar_t* format, va_list arg); + /// + int vfwscanf(FILE* stream, const scope wchar_t* format, va_list arg); + /// + int vswprintf(wchar_t* s, size_t n, const scope wchar_t* format, va_list arg); + /// + int vswscanf(const scope wchar_t* s, const scope wchar_t* format, va_list arg); + /// + int vwprintf(const scope wchar_t* format, va_list arg); + /// + int vwscanf(const scope wchar_t* format, va_list arg); + /// + int wprintf(const scope wchar_t* format, scope const ...); + /// + int wscanf(const scope wchar_t* format, scope ...); +} // No unsafe pointer manipulation. @trusted diff --git a/libphobos/libdruntime/core/sys/darwin/mach/getsect.d b/libphobos/libdruntime/core/sys/darwin/mach/getsect.d index fd1a8e4..dc42a2d 100644 --- a/libphobos/libdruntime/core/sys/darwin/mach/getsect.d +++ b/libphobos/libdruntime/core/sys/darwin/mach/getsect.d @@ -468,4 +468,3 @@ const(section)* getsectbynamefromheaderwithswap_64( const scope char* section, int fSwap ); - diff --git a/libphobos/libdruntime/core/sys/dragonflybsd/string.d b/libphobos/libdruntime/core/sys/dragonflybsd/string.d index 4b84227..1a85ba6 100644 --- a/libphobos/libdruntime/core/sys/dragonflybsd/string.d +++ b/libphobos/libdruntime/core/sys/dragonflybsd/string.d @@ -19,4 +19,3 @@ static if (__BSD_VISIBLE) { pure void* memmem(return scope const void* haystack, size_t haystacklen, scope const void* needle, size_t needlelen); } - diff --git a/libphobos/libdruntime/core/sys/linux/sys/time.d b/libphobos/libdruntime/core/sys/linux/sys/time.d index 6ea626e..4b56229 100644 --- a/libphobos/libdruntime/core/sys/linux/sys/time.d +++ b/libphobos/libdruntime/core/sys/linux/sys/time.d @@ -65,4 +65,3 @@ extern (D) pure @safe @nogc nothrow { } } - diff --git a/libphobos/libdruntime/core/sys/linux/sys/xattr.d b/libphobos/libdruntime/core/sys/linux/sys/xattr.d index 2f8d3f3..6446ff8 100644 --- a/libphobos/libdruntime/core/sys/linux/sys/xattr.d +++ b/libphobos/libdruntime/core/sys/linux/sys/xattr.d @@ -66,4 +66,3 @@ ssize_t flistxattr (int __fd, char *list, size_t size); int removexattr (const scope char *path, const scope char *name); int lremovexattr (const scope char *path, const scope char *name); int fremovexattr (int fd, const scope char *name); - diff --git a/libphobos/libdruntime/core/sys/linux/tipc.d b/libphobos/libdruntime/core/sys/linux/tipc.d index 3246e62..4d5d886 100644 --- a/libphobos/libdruntime/core/sys/linux/tipc.d +++ b/libphobos/libdruntime/core/sys/linux/tipc.d @@ -208,4 +208,3 @@ enum: int TIPC_DEST_DROPPABLE = 129, TIPC_CONN_TIMEOUT = 130, } - diff --git a/libphobos/libdruntime/core/sys/posix/signal.d b/libphobos/libdruntime/core/sys/posix/signal.d index 68aee98..542e83a 100644 --- a/libphobos/libdruntime/core/sys/posix/signal.d +++ b/libphobos/libdruntime/core/sys/posix/signal.d @@ -14,7 +14,7 @@ module core.sys.posix.signal; import core.sys.posix.config; public import core.stdc.signal; public import core.sys.posix.sys.types; // for pid_t -//public import core.sys.posix.time; // for timespec, now defined here +public import core.sys.posix.time; // for timespec version (OSX) version = Darwin; @@ -2805,83 +2805,6 @@ else } // -// Timer (TMR) -// -/* -NOTE: This should actually be defined in core.sys.posix.time. - It is defined here instead to break a circular import. - -struct timespec -{ - time_t tv_sec; - int tv_nsec; -} -*/ - -version (linux) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } -} -else version (Darwin) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } -} -else version (FreeBSD) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } -} -else version (NetBSD) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } -} -else version (OpenBSD) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } -} -else version (DragonFlyBSD) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } -} -else version (Solaris) -{ - struct timespec - { - time_t tv_sec; - c_long tv_nsec; - } - - alias timespec timestruc_t; -} -else -{ - static assert(false, "Unsupported platform"); -} - -// // Realtime Signals (RTS) // /* diff --git a/libphobos/libdruntime/core/sys/posix/spawn.d b/libphobos/libdruntime/core/sys/posix/spawn.d index cfa3a40..2064962 100644 --- a/libphobos/libdruntime/core/sys/posix/spawn.d +++ b/libphobos/libdruntime/core/sys/posix/spawn.d @@ -4,7 +4,7 @@ * Copyright: Copyright (C) 2018 by The D Language Foundation, All Rights Reserved * Authors: Petar Kirov * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/druntime/blob/master/src/core/sys/posix/spawn.d, _spawn.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/druntime/src/core/sys/posix/spawn.d, _spawn.d) * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition */ module core.sys.posix.spawn; diff --git a/libphobos/libdruntime/core/sys/posix/stdio.d b/libphobos/libdruntime/core/sys/posix/stdio.d index 077838d..d0d3d60 100644 --- a/libphobos/libdruntime/core/sys/posix/stdio.d +++ b/libphobos/libdruntime/core/sys/posix/stdio.d @@ -487,7 +487,7 @@ else version (CRuntime_Musl) version (HaveMemstream) { - FILE* fmemopen(const scope void* buf, in size_t size, const scope char* mode); + FILE* fmemopen(const scope void* buf, size_t size, const scope char* mode); FILE* open_memstream(char** ptr, size_t* sizeloc); version (CRuntime_UClibc) {} else FILE* open_wmemstream(wchar_t** ptr, size_t* sizeloc); diff --git a/libphobos/libdruntime/core/sys/posix/sys/select.d b/libphobos/libdruntime/core/sys/posix/sys/select.d index 2a659c3..925976d 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/select.d +++ b/libphobos/libdruntime/core/sys/posix/sys/select.d @@ -608,4 +608,3 @@ pure unittest assert(!FD_ISSET(i, &fd)); } } - diff --git a/libphobos/libdruntime/core/sys/posix/time.d b/libphobos/libdruntime/core/sys/posix/time.d index a9be87c8..ff3a3c4 100644 --- a/libphobos/libdruntime/core/sys/posix/time.d +++ b/libphobos/libdruntime/core/sys/posix/time.d @@ -167,9 +167,6 @@ else CLOCK_PROCESS_CPUTIME_ID (TMR|CPT) CLOCK_THREAD_CPUTIME_ID (TMR|TCT) -NOTE: timespec must be defined in core.sys.posix.signal to break - a circular import. - struct timespec { time_t tv_sec; @@ -199,6 +196,69 @@ int timer_getoverrun(timer_t); int timer_settime(timer_t, int, const scope itimerspec*, itimerspec*); */ +version (linux) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} +else version (Darwin) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} +else version (FreeBSD) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} +else version (NetBSD) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} +else version (OpenBSD) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} +else version (DragonFlyBSD) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} +else version (Solaris) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } + + alias timespec timestruc_t; +} +else +{ + static assert(false, "Unsupported platform"); +} + version (CRuntime_Glibc) { enum CLOCK_PROCESS_CPUTIME_ID = 2; diff --git a/libphobos/libdruntime/core/sys/posix/ucontext.d b/libphobos/libdruntime/core/sys/posix/ucontext.d index 20297f5..e8c2f87 100644 --- a/libphobos/libdruntime/core/sys/posix/ucontext.d +++ b/libphobos/libdruntime/core/sys/posix/ucontext.d @@ -1628,4 +1628,3 @@ version (Solaris) int addrtosymstr(uintptr_t, char*, int); int printstack(int); } - diff --git a/libphobos/libdruntime/core/sys/solaris/sys/priocntl.d b/libphobos/libdruntime/core/sys/solaris/sys/priocntl.d index bfbf3ee..c56be3b 100644 --- a/libphobos/libdruntime/core/sys/solaris/sys/priocntl.d +++ b/libphobos/libdruntime/core/sys/solaris/sys/priocntl.d @@ -113,4 +113,3 @@ struct pcadmin_t id_t pc_cid; caddr_t pc_cladmin; } - diff --git a/libphobos/libdruntime/core/sys/solaris/sys/procset.d b/libphobos/libdruntime/core/sys/solaris/sys/procset.d index 43fa997..8bd9115 100644 --- a/libphobos/libdruntime/core/sys/solaris/sys/procset.d +++ b/libphobos/libdruntime/core/sys/solaris/sys/procset.d @@ -50,4 +50,3 @@ void setprocset(ref procset_t psp, idop_t op, idtype_t ltype, id_t lid, idtype_t psp.p_ridtype = rtype; psp.p_rid = rid; } - diff --git a/libphobos/libdruntime/core/sys/windows/cguid.d b/libphobos/libdruntime/core/sys/windows/cguid.d index 0afbc42..d0a8fb9 100644 --- a/libphobos/libdruntime/core/sys/windows/cguid.d +++ b/libphobos/libdruntime/core/sys/windows/cguid.d @@ -10,4 +10,3 @@ module core.sys.windows.cguid; version (Windows): import core.sys.windows.basetyps; - diff --git a/libphobos/libdruntime/core/sys/windows/ntsecpkg.d b/libphobos/libdruntime/core/sys/windows/ntsecpkg.d index d4c93d7..d8c5e95 100644 --- a/libphobos/libdruntime/core/sys/windows/ntsecpkg.d +++ b/libphobos/libdruntime/core/sys/windows/ntsecpkg.d @@ -444,4 +444,3 @@ alias NTSTATUS function(ULONG, PULONG, PSECPKG_FUNCTION_TABLE *, PULONG) SpLsaModeInitializeFn; alias NTSTATUS function(ULONG, PULONG, PSECPKG_USER_FUNCTION_TABLE *, PULONG) SpUserModeInitializeFn; - diff --git a/libphobos/libdruntime/core/sys/windows/olectlid.d b/libphobos/libdruntime/core/sys/windows/olectlid.d index 8bbe657..b58c14a 100644 --- a/libphobos/libdruntime/core/sys/windows/olectlid.d +++ b/libphobos/libdruntime/core/sys/windows/olectlid.d @@ -10,4 +10,3 @@ module core.sys.windows.olectlid; version (Windows): import core.sys.windows.basetyps; - diff --git a/libphobos/libdruntime/core/sys/windows/shlguid.d b/libphobos/libdruntime/core/sys/windows/shlguid.d index 1c0c98f..e0c1af1 100644 --- a/libphobos/libdruntime/core/sys/windows/shlguid.d +++ b/libphobos/libdruntime/core/sys/windows/shlguid.d @@ -16,4 +16,3 @@ import core.sys.windows.basetyps, core.sys.windows.w32api; // I think this is just a helper macro for other win32 headers? //MACRO #define DEFINE_SHLGUID(n,l,w1,w2) DEFINE_GUID(n,l,w1,w2,0xC0,0,0,0,0,0,0,0x46) - diff --git a/libphobos/libdruntime/core/sys/windows/sspi.d b/libphobos/libdruntime/core/sys/windows/sspi.d index 07a2596..21e982d 100644 --- a/libphobos/libdruntime/core/sys/windows/sspi.d +++ b/libphobos/libdruntime/core/sys/windows/sspi.d @@ -380,4 +380,3 @@ version (Unicode) { alias QUERY_SECURITY_PACKAGE_INFO_FN_A QUERY_SECURITY_PACKAGE_INFO_FN; alias INIT_SECURITY_INTERFACE_A INIT_SECURITY_INTERFACE; } - diff --git a/libphobos/libdruntime/object.d b/libphobos/libdruntime/object.d index fe65c09..8ef6548 100644 --- a/libphobos/libdruntime/object.d +++ b/libphobos/libdruntime/object.d @@ -3765,6 +3765,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, if (!is(const(T) : T)) { import core.internal.traits : Unconst; + import core.internal.array.duplication : _dup; static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~ " to "~Unconst!T.stringof~" in dup."); @@ -3786,6 +3787,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, @property T[] dup(T)(const(T)[] a) if (is(const(T) : T)) { + import core.internal.array.duplication : _dup; return _dup!(const(T), T)(a); } @@ -3793,6 +3795,7 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, /// Provide the .idup array property. @property immutable(T)[] idup(T)(T[] a) { + import core.internal.array.duplication : _dup; static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~ " to immutable in idup."); return _dup!(T, immutable(T))(a); @@ -3813,73 +3816,6 @@ private size_t getArrayHash(const scope TypeInfo element, const scope void* ptr, assert(s == "abc"); } -private U[] _dup(T, U)(scope T[] a) pure nothrow @trusted if (__traits(isPOD, T)) -{ - if (__ctfe) - return _dupCtfe!(T, U)(a); - - import core.stdc.string : memcpy; - auto arr = _d_newarrayU(typeid(T[]), a.length); - memcpy(arr.ptr, cast(const(void)*) a.ptr, T.sizeof * a.length); - return *cast(U[]*) &arr; -} - -private U[] _dupCtfe(T, U)(scope T[] a) -{ - static if (is(T : void)) - assert(0, "Cannot dup a void[] array at compile time."); - else - { - U[] res; - foreach (ref e; a) - res ~= e; - return res; - } -} - -private U[] _dup(T, U)(T[] a) if (!__traits(isPOD, T)) -{ - // note: copyEmplace is `@system` inside a `@trusted` block, so the __ctfe branch - // has the extra duty to infer _dup `@system` when the copy-constructor is `@system`. - if (__ctfe) - return _dupCtfe!(T, U)(a); - - import core.lifetime: copyEmplace; - U[] res = () @trusted { - auto arr = cast(U*) _d_newarrayU(typeid(T[]), a.length); - size_t i; - scope (failure) - { - import core.internal.lifetime: emplaceInitializer; - // Initialize all remaining elements to not destruct garbage - foreach (j; i .. a.length) - emplaceInitializer(cast() arr[j]); - } - for (; i < a.length; i++) - { - copyEmplace(a.ptr[i], arr[i]); - } - return cast(U[])(arr[0..a.length]); - } (); - - return res; -} - -// https://issues.dlang.org/show_bug.cgi?id=22107 -@safe unittest -{ - static int i; - @safe struct S - { - this(this) { i++; } - } - - void fun(scope S[] values...) @safe - { - values.dup; - } -} - // HACK: This is a lie. `_d_arraysetcapacity` is neither `nothrow` nor `pure`, but this lie is // necessary for now to prevent breaking code. private extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* arrptr) pure nothrow; @@ -4067,8 +4003,6 @@ auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow @system assert(is(typeof(b3) == immutable(int[]))); } -private extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow; - private void _doPostblit(T)(T[] arr) { // infer static postblit type, run postblit if any @@ -4085,274 +4019,6 @@ private void _doPostblit(T)(T[] arr) } } -@safe unittest -{ - static struct S1 { int* p; } - static struct S2 { @disable this(); } - static struct S3 { @disable this(this); } - - int dg1() pure nothrow @safe - { - { - char[] m; - string i; - m = m.dup; - i = i.idup; - m = i.dup; - i = m.idup; - } - { - S1[] m; - immutable(S1)[] i; - m = m.dup; - i = i.idup; - static assert(!is(typeof(m.idup))); - static assert(!is(typeof(i.dup))); - } - { - S3[] m; - immutable(S3)[] i; - static assert(!is(typeof(m.dup))); - static assert(!is(typeof(i.idup))); - } - { - shared(S1)[] m; - m = m.dup; - static assert(!is(typeof(m.idup))); - } - { - int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0); - } - return 1; - } - - int dg2() pure nothrow @safe - { - { - S2[] m = [S2.init, S2.init]; - immutable(S2)[] i = [S2.init, S2.init]; - m = m.dup; - m = i.dup; - i = m.idup; - i = i.idup; - } - return 2; - } - - enum a = dg1(); - enum b = dg2(); - assert(dg1() == a); - assert(dg2() == b); -} - -@system unittest -{ - static struct Sunpure { this(this) @safe nothrow {} } - static struct Sthrow { this(this) @safe pure {} } - static struct Sunsafe { this(this) @system pure nothrow {} } - static struct Snocopy { @disable this(this); } - - [].dup!Sunpure; - [].dup!Sthrow; - cast(void) [].dup!Sunsafe; - static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); - static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); - static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); - static assert(!__traits(compiles, () { [].dup!Snocopy; })); - - [].idup!Sunpure; - [].idup!Sthrow; - [].idup!Sunsafe; - static assert(!__traits(compiles, () pure { [].idup!Sunpure; })); - static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; })); - static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; })); - static assert(!__traits(compiles, () { [].idup!Snocopy; })); -} - -@safe unittest -{ - // test that the copy-constructor is called with .dup - static struct ArrElem - { - int a; - this(int a) - { - this.a = a; - } - this(ref const ArrElem) - { - a = 2; - } - this(ref ArrElem) immutable - { - a = 3; - } - } - - auto arr = [ArrElem(1), ArrElem(1)]; - - ArrElem[] b = arr.dup; - assert(b[0].a == 2 && b[1].a == 2); - - immutable ArrElem[] c = arr.idup; - assert(c[0].a == 3 && c[1].a == 3); -} - -@system unittest -{ - static struct Sunpure { this(ref const typeof(this)) @safe nothrow {} } - static struct Sthrow { this(ref const typeof(this)) @safe pure {} } - static struct Sunsafe { this(ref const typeof(this)) @system pure nothrow {} } - [].dup!Sunpure; - [].dup!Sthrow; - cast(void) [].dup!Sunsafe; - static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); - static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); - static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); - - // for idup to work on structs that have copy constructors, it is necessary - // that the struct defines a copy constructor that creates immutable objects - static struct ISunpure { this(ref const typeof(this)) immutable @safe nothrow {} } - static struct ISthrow { this(ref const typeof(this)) immutable @safe pure {} } - static struct ISunsafe { this(ref const typeof(this)) immutable @system pure nothrow {} } - [].idup!ISunpure; - [].idup!ISthrow; - [].idup!ISunsafe; - static assert(!__traits(compiles, () pure { [].idup!ISunpure; })); - static assert(!__traits(compiles, () nothrow { [].idup!ISthrow; })); - static assert(!__traits(compiles, () @safe { [].idup!ISunsafe; })); -} - -@safe unittest -{ - static int*[] pureFoo() pure { return null; } - { char[] s; immutable x = s.dup; } - { immutable x = (cast(int*[])null).dup; } - { immutable x = pureFoo(); } - { immutable x = pureFoo().dup; } -} - -@safe unittest -{ - auto a = [1, 2, 3]; - auto b = a.dup; - debug(SENTINEL) {} else - assert(b.capacity >= 3); -} - -@system unittest -{ - // Bugzilla 12580 - void[] m = [0]; - shared(void)[] s = [cast(shared)1]; - immutable(void)[] i = [cast(immutable)2]; - - s = s.dup; - static assert(is(typeof(s.dup) == shared(void)[])); - - m = i.dup; - i = m.dup; - i = i.idup; - i = m.idup; - i = s.idup; - i = s.dup; - static assert(!__traits(compiles, m = s.dup)); -} - -@safe unittest -{ - // Bugzilla 13809 - static struct S - { - this(this) {} - ~this() {} - } - - S[] arr; - auto a = arr.dup; -} - -@system unittest -{ - // Bugzilla 16504 - static struct S - { - __gshared int* gp; - int* p; - // postblit and hence .dup could escape - this(this) { gp = p; } - } - - int p; - scope S[1] arr = [S(&p)]; - auto a = arr.dup; // dup does escape -} - -// https://issues.dlang.org/show_bug.cgi?id=21983 -// dup/idup destroys partially constructed arrays on failure -@safe unittest -{ - static struct SImpl(bool postblit) - { - int num; - long l = 0xDEADBEEF; - - static if (postblit) - { - this(this) - { - if (this.num == 3) - throw new Exception(""); - } - } - else - { - this(scope ref const SImpl other) - { - if (other.num == 3) - throw new Exception(""); - - this.num = other.num; - this.l = other.l; - } - } - - ~this() @trusted - { - if (l != 0xDEADBEEF) - { - import core.stdc.stdio; - printf("Unexpected value: %lld\n", l); - fflush(stdout); - assert(false); - } - } - } - - alias Postblit = SImpl!true; - alias Copy = SImpl!false; - - static int test(S)() - { - S[4] arr = [ S(1), S(2), S(3), S(4) ]; - try - { - arr.dup(); - assert(false); - } - catch (Exception) - { - return 1; - } - } - - static assert(test!Postblit()); - assert(test!Postblit()); - - static assert(test!Copy()); - assert(test!Copy()); -} - /** Destroys the given object and optionally resets to initial state. It's used to _destroy an object, calling its destructor or finalizer so it no longer diff --git a/libphobos/libdruntime/rt/dylib_fixes.c b/libphobos/libdruntime/rt/dylib_fixes.c index e484fed..c1391b8 100644 --- a/libphobos/libdruntime/rt/dylib_fixes.c +++ b/libphobos/libdruntime/rt/dylib_fixes.c @@ -25,4 +25,3 @@ __attribute__((destructor)) static void finalizer () { rt_term(); } - diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 744e5ad..1f0cfbf 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -1516ecad932d88a1618163384e6f69009d125391 +5748ca43fd5c3e31ce7a8511f542b67e5d5a3dc6 The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/etc/c/curl.d b/libphobos/src/etc/c/curl.d index 0c5b727..e6a1043 100644 --- a/libphobos/src/etc/c/curl.d +++ b/libphobos/src/etc/c/curl.d @@ -1372,9 +1372,9 @@ alias curl_TimeCond = int; /** curl_strequal() and curl_strnequal() are subject for removal in a future libcurl, see lib/README.curlx for details */ extern (C) { -int curl_strequal(in const(char) *s1, in const(char) *s2); +int curl_strequal(scope const(char) *s1, scope const(char) *s2); /// ditto -int curl_strnequal(in const(char) *s1, in const(char) *s2, size_t n); +int curl_strnequal(scope const(char) *s1, scope const(char) *s2, size_t n); } enum CurlForm { nothing, /********** the first one is unused ************/ @@ -1464,7 +1464,7 @@ CURLFORMcode curl_formadd(curl_httppost **httppost, curl_httppost **last_post,. * Should return the buffer length passed to it as the argument "len" on * success. */ -alias curl_formget_callback = size_t function(void *arg, in const(char) *buf, size_t len); +alias curl_formget_callback = size_t function(void *arg, const(char) *buf, size_t len); /** * Name: curl_formget() @@ -1494,7 +1494,7 @@ void curl_formfree(curl_httppost *form); * Returns a malloc()'ed string that MUST be curl_free()ed after usage is * complete. DEPRECATED - see lib/README.curlx */ -char * curl_getenv(in const(char) *variable); +char * curl_getenv(scope const(char) *variable); /** * Name: curl_version() @@ -1514,10 +1514,10 @@ char * curl_version(); * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -char * curl_easy_escape(CURL *handle, in const(char) *string, int length); +char * curl_easy_escape(CURL *handle, scope const(char) *string, int length); /** the previous version: */ -char * curl_escape(in const(char) *string, int length); +char * curl_escape(scope const(char) *string, int length); /** @@ -1531,10 +1531,10 @@ char * curl_escape(in const(char) *string, int length); * Conversion Note: On non-ASCII platforms the ASCII %XX codes are * converted into the host encoding. */ -char * curl_easy_unescape(CURL *handle, in const(char) *string, int length, int *outlength); +char * curl_easy_unescape(CURL *handle, scope const(char) *string, int length, int *outlength); /** the previous version */ -char * curl_unescape(in const(char) *string, int length); +char * curl_unescape(scope const(char) *string, int length); /** * Name: curl_free() @@ -1608,7 +1608,7 @@ struct curl_slist * Appends a string to a linked list. If no list exists, it will be created * first. Returns the new list, after appending. */ -curl_slist * curl_slist_append(curl_slist *, in const(char) *); +curl_slist * curl_slist_append(curl_slist *, const(char) *); /** * Name: curl_slist_free_all() diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index 2fcc2ba..b810fbb 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -1027,7 +1027,7 @@ template equal(alias pred = "a == b") } } - private bool equalLoop(Rs...)(Rs rs) + private bool equalLoop(Rs...)(ref Rs rs) { for (; !rs[0].empty; rs[0].popFront) static foreach (r; rs[1 .. $]) diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d index af665c4..300a897 100644 --- a/libphobos/src/std/algorithm/iteration.d +++ b/libphobos/src/std/algorithm/iteration.d @@ -1263,19 +1263,22 @@ public: // filter /** -Implements the higher order filter function. The predicate is passed to -$(REF unaryFun, std,functional), and can either accept a string, or any callable -that can be executed via `pred(element)`. +`filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for +which `predicate(x)` returns `true`. + +The predicate is passed to $(REF unaryFun, std,functional), and can be either a string, or +any callable that can be executed via `pred(element)`. Params: predicate = Function to apply to each element of range Returns: - `filter!(predicate)(range)` returns a new range containing only elements `x` in `range` for - which `predicate(x)` returns `true`. + An input range that contains the filtered elements. If `range` is at least a forward range, the return value of `filter` + will also be a forward range. See_Also: - $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)) + $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)), + $(REF filterBidirectional, std,algorithm,iteration) */ template filter(alias predicate) if (is(typeof(unaryFun!predicate))) diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d index 55a1438..daa4b99 100644 --- a/libphobos/src/std/algorithm/searching.d +++ b/libphobos/src/std/algorithm/searching.d @@ -2512,6 +2512,8 @@ RandomAccessRange find(RandomAccessRange, alias pred, InputRange)( Convenience function. Like find, but only returns whether or not the search was successful. +For more information about `pred` see $(LREF find). + See_Also: $(REF among, std,algorithm,comparison) for checking a value against multiple possibilities. +/ @@ -2622,6 +2624,8 @@ Advances `r` until it finds the first two adjacent elements `a`, `b` that satisfy `pred(a, b)`. Performs $(BIGOH r.length) evaluations of `pred`. +For more information about `pred` see $(LREF find). + Params: pred = The predicate to satisfy. r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to @@ -2698,6 +2702,8 @@ Advances `seq` by calling `seq.popFront` until either `find!(pred)(choices, seq.front)` is `true`, or `seq` becomes empty. Performs $(BIGOH seq.length * choices.length) evaluations of `pred`. +For more information about `pred` see $(LREF find). + Params: pred = The predicate to use for determining a match. seq = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to @@ -2758,6 +2764,8 @@ if (isInputRange!InputRange && isForwardRange!ForwardRange) * Similarly, the haystack is positioned so as `pred` evaluates to `false` for * `haystack.front`. * + * For more information about `pred` see $(LREF find). + * Params: * haystack = The * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search @@ -2882,6 +2890,8 @@ $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the type of `result[0]` and `result[1]` is the same as $(REF takeExactly, std,range). +For more information about `pred` see $(LREF find). + Params: pred = Predicate to use for comparing needle against haystack. haystack = The range to search. @@ -4595,6 +4605,8 @@ $(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one of) the given needle(s) or, if no needles are given, if its front element fulfils predicate `pred`. +For more information about `pred` see $(LREF find). + Params: pred = Predicate to use in comparing the elements of the haystack and the diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 8f6c3bf..9164e07 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -3419,17 +3419,20 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum } } + Target result = cast(Target) (sign ? -ldval : ldval); + // if overflow occurred - enforce(ldval != real.infinity, new ConvException("Range error")); + import std.math : isFinite; + enforce(isFinite(result), new ConvException("Range error")); advanceSource(); static if (doCount) { - return tuple!("data", "count")(cast (Target) (sign ? -ldval : ldval), count); + return tuple!("data", "count")(result, count); } else { - return cast (Target) (sign ? -ldval : ldval); + return result; } } @@ -3785,6 +3788,16 @@ if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum assertThrown!ConvException(parse!double(s)); } +@safe unittest // https://issues.dlang.org/show_bug.cgi?id=22637 +{ + import std.exception : assertThrown, assertNotThrown; + auto src = "9991232549867999698999493543521458974414359998784641646846435132132543645435456345634541999999999999999" + ~ "9999999943321231321311999231345312413646846354354354399999934153465464654646464654134135354199999999996515734999" + ~ "9999999320135273486741354354731567431324134999999999999999999999999999999999999999999999135411.9"; + assertThrown!ConvException(parse!double(src)); + static if (real.max_10_exp > 310) assertNotThrown!ConvException(parse!real(src)); +} + /** Parsing one character off a range returns the first element and calls `popFront`. diff --git a/libphobos/src/std/experimental/checkedint.d b/libphobos/src/std/experimental/checkedint.d index 9237341..2be5a2e 100644 --- a/libphobos/src/std/experimental/checkedint.d +++ b/libphobos/src/std/experimental/checkedint.d @@ -1,6 +1,6 @@ /** - * This module is now deprecated, use $(MREF std, experimental) + * This module is now deprecated, use $(MREF std, checkedint) * instead. * * Copyright: Copyright The D Language Foundation 2005 - 2015. diff --git a/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d index d899db7..f3c6932 100644 --- a/libphobos/src/std/experimental/logger/core.d +++ b/libphobos/src/std/experimental/logger/core.d @@ -4,6 +4,7 @@ Source: $(PHOBOSSRC std/experimental/logger/core.d) */ module std.experimental.logger.core; +import core.atomic : atomicLoad, atomicOp, atomicStore, MemoryOrder; import core.sync.mutex : Mutex; import std.datetime.date : DateTime; import std.datetime.systime : Clock, SysTime; @@ -555,14 +556,14 @@ abstract class Logger Params: lv = `LogLevel` to use for this `Logger` instance. */ - this(LogLevel lv) @safe + this(this This)(LogLevel lv) { this.logLevel_ = lv; this.fatalHandler_ = delegate() { throw new Error("A fatal log message was logged"); }; - this.mutex = new Mutex(); + this.mutex = new typeof(mutex)(); } /** A custom logger must implement this method in order to work in a @@ -661,7 +662,7 @@ abstract class Logger /// Ditto @property final void logLevel(const LogLevel lv) @safe @nogc { - synchronized (mutex) this.logLevel_ = lv; + atomicStore(this.logLevel_, lv); } /** This `delegate` is called in case a log message with @@ -1403,28 +1404,28 @@ abstract class Logger // Thread Global -private __gshared Logger stdSharedDefaultLogger; +private shared Logger stdSharedDefaultLogger; private shared Logger stdSharedLogger; private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all; /* This method returns the global default Logger. * Marked @trusted because of excessive reliance on __gshared data */ -private @property Logger defaultSharedLoggerImpl() @trusted +private @property shared(Logger) defaultSharedLoggerImpl() @trusted { import core.lifetime : emplace; import std.stdio : stderr; __gshared align(__traits(classInstanceAlignment, FileLogger)) - void[__traits(classInstanceSize, FileLogger)] _buffer; + void[__traits(classInstanceSize, FileLogger)] _buffer = void; import std.concurrency : initOnce; initOnce!stdSharedDefaultLogger({ auto buffer = cast(ubyte[]) _buffer; - return emplace!FileLogger(buffer, stderr, LogLevel.info); + return cast(shared) emplace!(FileLogger)(buffer, stderr, LogLevel.info); }()); - return stdSharedDefaultLogger; + return atomicLoad(stdSharedDefaultLogger); } /** This property sets and gets the default `Logger`. Unless set to another @@ -1452,19 +1453,12 @@ if (sharedLog !is myLogger) sharedLog = new myLogger; ------------- */ -@property Logger sharedLog() @safe +@property shared(Logger) sharedLog() @safe { - static auto trustedLoad(ref shared Logger logger) @trusted - { - import core.atomic : atomicLoad, MemoryOrder; - return cast() atomicLoad!(MemoryOrder.acq)(logger); - //FIXME: Casting shared away here. Not good. See issue 16232. - } - // If we have set up our own logger use that - if (auto logger = trustedLoad(stdSharedLogger)) + if (auto logger = atomicLoad!(MemoryOrder.seq)(stdSharedLogger)) { - return logger; + return atomicLoad(logger); } else { @@ -1474,10 +1468,9 @@ if (sharedLog !is myLogger) } /// Ditto -@property void sharedLog(Logger logger) @trusted +@property void sharedLog(shared(Logger) logger) @safe { - import core.atomic : atomicStore, MemoryOrder; - atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger); + atomicStore!(MemoryOrder.seq)(stdSharedLogger, atomicLoad(logger)); } /** This methods get and set the global `LogLevel`. @@ -1523,9 +1516,12 @@ class StdForwardLogger : Logger this.fatalHandler = delegate() {}; } - override protected void writeLogMsg(ref LogEntry payload) + override protected void writeLogMsg(ref LogEntry payload) @trusted { - sharedLog.forwardMsg(payload); + synchronized (sharedLog.mutex) + { + (cast() sharedLog).forwardMsg(payload); + } } } @@ -1535,6 +1531,40 @@ class StdForwardLogger : Logger auto nl1 = new StdForwardLogger(LogLevel.all); } +@safe unittest +{ + import core.thread : Thread, msecs; + + static class RaceLogger : Logger + { + int value; + this() @safe shared + { + super(LogLevel.init); + } + override void writeLogMsg(ref LogEntry payload) @safe + { + import core.thread : Thread, msecs; + if (payload.msg == "foo") + { + value = 42; + () @trusted { Thread.sleep(100.msecs); }(); + assert(value == 42, "Another thread changed the value"); + } + else + { + () @trusted { Thread.sleep(50.msecs); } (); + value = 13; + } + } + } + + sharedLog = new shared RaceLogger; + scope(exit) { sharedLog = null; } + () @trusted { new Thread(() { log("foo"); }).start(); }(); + log("bar"); +} + /** This `LogLevel` is unqiue to every thread. The thread local `Logger` will use this `LogLevel` to filter log calls @@ -1561,7 +1591,7 @@ private @property Logger stdThreadLocalLogImpl() @trusted } /** This function returns a thread unique `Logger`, that by default -propergates all data logged to it to the `sharedLog`. +propagates all data logged to it to the `sharedLog`. These properties can be used to set and get this `Logger`. Every modification to this `Logger` will only be visible in the thread the @@ -1671,10 +1701,12 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto oldunspecificLogger = sharedLog; scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); } - sharedLog = tl1; + () @trusted { + sharedLog = cast(shared) tl1; + }(); log(); assert(tl1.line == __LINE__ - 1); @@ -1793,22 +1825,34 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe assert(l.line == lineNumber); assert(l.logLevel == LogLevel.all); - auto oldunspecificLogger = sharedLog; + Logger oldunspecificLogger; + () @trusted { + oldunspecificLogger = cast() sharedLog; + }(); assert(oldunspecificLogger.logLevel == LogLevel.info, to!string(oldunspecificLogger.logLevel)); assert(l.logLevel == LogLevel.all); - sharedLog = l; + + () @trusted { + sharedLog = cast(shared) l; + }(); + assert(globalLogLevel == LogLevel.all, to!string(globalLogLevel)); scope(exit) { - sharedLog = oldunspecificLogger; + () @trusted { + sharedLog = atomicLoad(cast(shared) oldunspecificLogger); + }(); } - assert(sharedLog.logLevel == LogLevel.all); + () @trusted { + assert((cast() sharedLog).logLevel == LogLevel.all); + }(); + assert(stdThreadLocalLog.logLevel == LogLevel.all); assert(globalLogLevel == LogLevel.all); @@ -1880,13 +1924,14 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile"; FileLogger l = new FileLogger(filename); auto oldunspecificLogger = sharedLog; - sharedLog = l; + + sharedLog = cast(shared) l; scope(exit) { remove(filename); assert(!exists(filename)); - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -1923,7 +1968,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe scope(exit) { remove(filename); - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -1931,8 +1976,11 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe string written = "this should be written to file"; auto l = new FileLogger(filename); - sharedLog = l; - sharedLog.logLevel = LogLevel.critical; + sharedLog = cast(shared) l; + + () @trusted { + (cast() sharedLog).logLevel = LogLevel.critical; + }(); log(LogLevel.error, false, notWritten); log(LogLevel.critical, true, written); @@ -1974,11 +2022,14 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto mem = new TestLogger; mem.fatalHandler = delegate() {}; - sharedLog = mem; + + () @trusted { + sharedLog = cast(shared) mem; + }(); scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2221,11 +2272,14 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto mem = new TestLogger; mem.fatalHandler = delegate() {}; - sharedLog = mem; + + () @trusted { + sharedLog = cast(shared) mem; + }(); scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2477,10 +2531,13 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe stdThreadLocalLog.logLevel = LogLevel.all; - sharedLog = mem; + () @trusted { + sharedLog = cast(shared) mem; + }(); + scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2707,12 +2764,15 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } auto tl = new TestLogger(LogLevel.info); - sharedLog = tl; + + () @trusted { + sharedLog = cast(shared) tl; + }(); trace("trace"); assert(tl.msg.indexOf("trace") == -1); @@ -2730,7 +2790,7 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe scope(exit) { - sharedLog = oldunspecificLogger; + sharedLog = atomicLoad(oldunspecificLogger); globalLogLevel = LogLevel.all; } @@ -2738,7 +2798,10 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe auto tl = new TestLogger(LogLevel.info); logger.insertLogger("required", tl); - sharedLog = logger; + + () @trusted { + sharedLog = cast(shared) logger; + }(); trace("trace"); assert(tl.msg.indexOf("trace") == -1); @@ -2774,14 +2837,12 @@ version (StdUnittest) private void testFuncNames(Logger logger) @safe // Workaround for atomics not allowed in @safe code private auto trustedLoad(T)(ref shared T value) @trusted { - import core.atomic : atomicLoad, MemoryOrder; return atomicLoad!(MemoryOrder.acq)(value); } // ditto private void trustedStore(T)(ref shared T dst, ref T src) @trusted { - import core.atomic : atomicStore, MemoryOrder; atomicStore!(MemoryOrder.rel)(dst, src); } @@ -2789,7 +2850,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted // to shared logger @system unittest { - import core.atomic, core.thread, std.concurrency; + import core.thread, std.concurrency; static shared logged_count = 0; @@ -2826,10 +2887,13 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted auto oldSharedLog = sharedLog; scope(exit) { - sharedLog = oldSharedLog; + sharedLog = atomicLoad(oldSharedLog); } - sharedLog = new IgnoredLog; + () @trusted { + sharedLog = cast(shared) new IgnoredLog; + }(); + Thread[] spawned; foreach (i; 0 .. 4) @@ -2849,7 +2913,9 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted @safe unittest { - auto dl = cast(FileLogger) sharedLog; + auto dl = () @trusted { + return cast(FileLogger) cast() sharedLog; + }(); assert(dl !is null); assert(dl.logLevel == LogLevel.info); assert(globalLogLevel == LogLevel.all); @@ -2946,7 +3012,7 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted auto oldShared = sharedLog; scope(exit) { - sharedLog = oldShared; + sharedLog = atomicLoad(oldShared); if (exists(fn)) { remove(fn); @@ -2956,7 +3022,11 @@ private void trustedStore(T)(ref shared T dst, ref T src) @trusted auto ts = [ "Test log 1", "Test log 2", "Test log 3"]; auto fl = new FileLogger(fn); - sharedLog = fl; + + () @trusted { + sharedLog = cast(shared) fl; + }(); + assert(exists(fn)); foreach (t; ts) diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d index 5112e52..457012e 100644 --- a/libphobos/src/std/experimental/logger/filelogger.d +++ b/libphobos/src/std/experimental/logger/filelogger.d @@ -259,7 +259,7 @@ class FileLogger : Logger file.close(); } -@safe unittest +@system unittest { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d index 9acd23a..3593690 100644 --- a/libphobos/src/std/experimental/logger/multilogger.d +++ b/libphobos/src/std/experimental/logger/multilogger.d @@ -187,7 +187,7 @@ class MultiLogger : Logger assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg); } -@safe unittest +@system unittest { auto dl = cast(FileLogger) sharedLog; assert(dl !is null); diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index b8b4a8c..d6cac41 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -1510,7 +1510,7 @@ private ushort bitmapcount, reserved; attrgroup_t commonattr, volattr, dirattr, fileattr, forkattr; } - extern(C) int setattrlist(in char* path, scope ref attrlist attrs, + extern(C) int setattrlist(scope const(char)* path, scope ref attrlist attrs, scope void* attrbuf, size_t attrBufSize, c_ulong options) nothrow @nogc @system; } diff --git a/libphobos/src/std/format/internal/write.d b/libphobos/src/std/format/internal/write.d index f1d6964..2fd6ff7 100644 --- a/libphobos/src/std/format/internal/write.d +++ b/libphobos/src/std/format/internal/write.d @@ -1337,7 +1337,7 @@ if (is(StringTypeOf!T) && !is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToSt /* Static-size arrays are formatted as dynamic arrays. */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref const(T) obj, +void formatValueImpl(Writer, T, Char)(auto ref Writer w, auto ref T obj, scope const ref FormatSpec!Char f) if (is(StaticArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { @@ -1782,13 +1782,13 @@ void formatChar(Writer)(ref Writer w, in dchar c, in char quote) Associative arrays are formatted by using `':'` and $(D ", ") as separators, and enclosed by `'['` and `']'`. */ -void formatValueImpl(Writer, T, Char)(auto ref Writer w, const(T) obj, scope const ref FormatSpec!Char f) +void formatValueImpl(Writer, T, Char)(auto ref Writer w, T obj, scope const ref FormatSpec!Char f) if (is(AssocArrayTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) { import std.format : enforceFmt, formatValue; import std.range.primitives : put; - AssocArrayTypeOf!(const(T)) val = obj; + AssocArrayTypeOf!T val = obj; const spec = f.spec; enforceFmt(spec == 's' || spec == '(', diff --git a/libphobos/src/std/format/package.d b/libphobos/src/std/format/package.d index 76d68f6..3f6f33a 100644 --- a/libphobos/src/std/format/package.d +++ b/libphobos/src/std/format/package.d @@ -1356,6 +1356,30 @@ if (isSomeChar!Char) assert(result == " 1"); } +// https://issues.dlang.org/show_bug.cgi?id=23245 +@safe unittest +{ + static struct S + { + string toString() { return "S"; } + } + + S[1] s; + assert(format("%s", s) == "[S]"); +} + +// https://issues.dlang.org/show_bug.cgi?id=23246 +@safe unittest +{ + static struct S + { + string toString() { return "S"; } + } + + S[int] s = [0 : S()]; + assert(format("%s", s) == "[0:S]"); +} + /// ditto typeof(fmt) format(alias fmt, Args...)(Args args) if (isSomeString!(typeof(fmt))) diff --git a/libphobos/src/std/math/package.d b/libphobos/src/std/math/package.d index 7443b0d..19982ec 100644 --- a/libphobos/src/std/math/package.d +++ b/libphobos/src/std/math/package.d @@ -383,6 +383,7 @@ template floatTraits(T) enum ushort EXPBIAS = 0x3FE0; enum uint EXPMASK_INT = 0x7FF0_0000; enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only + enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF; enum realFormat = RealFormat.ieeeDouble; version (LittleEndian) { diff --git a/libphobos/src/std/math/rounding.d b/libphobos/src/std/math/rounding.d index 5c8d708..7dbe89b 100644 --- a/libphobos/src/std/math/rounding.d +++ b/libphobos/src/std/math/rounding.d @@ -908,7 +908,9 @@ T floorImpl(T)(const T x) @trusted pure nothrow @nogc // Other kinds of extractors for real formats. static if (F.realFormat == RealFormat.ieeeSingle) - int vi; + uint vi; + else static if (F.realFormat == RealFormat.ieeeDouble) + ulong vi; } floatBits y = void; y.rv = x; @@ -919,15 +921,14 @@ T floorImpl(T)(const T x) @trusted pure nothrow @nogc static if (F.realFormat == RealFormat.ieeeSingle) { int exp = ((y.vi >> (T.mant_dig - 1)) & 0xff) - 0x7f; + enum mantissa_mask = F.MANTISSAMASK_INT; + enum sign_shift = 31; } else static if (F.realFormat == RealFormat.ieeeDouble) { - int exp = ((y.vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff; - - version (LittleEndian) - int pos = 0; - else - int pos = 3; + long exp = ((y.vi >> (T.mant_dig - 1)) & 0x7ff) - 0x3ff; + enum mantissa_mask = F.MANTISSAMASK_LONG; + enum sign_shift = 63; } else static if (F.realFormat == RealFormat.ieeeExtended || F.realFormat == RealFormat.ieeeExtended53) @@ -959,18 +960,21 @@ T floorImpl(T)(const T x) @trusted pure nothrow @nogc return 0.0; } - static if (F.realFormat == RealFormat.ieeeSingle) + static if (F.realFormat == RealFormat.ieeeSingle || + F.realFormat == RealFormat.ieeeDouble) { if (exp < (T.mant_dig - 1)) { // Clear all bits representing the fraction part. - const uint fraction_mask = F.MANTISSAMASK_INT >> exp; + // Note: the fraction mask represents the floating point number 0.999999... + // i.e: `2.0 ^^ (exp - T.mant_dig + 1) * (fraction_mask + 1) == 1.0` + const fraction_mask = mantissa_mask >> exp; if ((y.vi & fraction_mask) != 0) { - // If 'x' is negative, then first substract 1.0 from the value. - if (y.vi < 0) - y.vi += 0x00800000 >> exp; + // If 'x' is negative, then first substract (1.0 - T.epsilon) from the value. + if (y.vi >> sign_shift) + y.vi += fraction_mask; y.vi &= ~fraction_mask; } } diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index 106e51c..b2206ce 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -2762,7 +2762,7 @@ Returns: return a `ref` to the $(D range element), otherwise it will return a copy. */ -auto ref choice(Range, RandomGen = Random)(auto ref Range range, ref RandomGen urng) +auto ref choice(Range, RandomGen = Random)(Range range, ref RandomGen urng) if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) { assert(range.length > 0, @@ -2772,7 +2772,22 @@ if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) } /// ditto -auto ref choice(Range)(auto ref Range range) +auto ref choice(Range)(Range range) +{ + return choice(range, rndGen); +} + +/// ditto +auto ref choice(Range, RandomGen = Random)(ref Range range, ref RandomGen urng) +if (isRandomAccessRange!Range && hasLength!Range && isUniformRNG!RandomGen) +{ + assert(range.length > 0, + __PRETTY_FUNCTION__ ~ ": invalid Range supplied. Range cannot be empty"); + return range[uniform(size_t(0), $, urng)]; +} + +/// ditto +auto ref choice(Range)(ref Range range) { return choice(range, rndGen); } @@ -2827,6 +2842,39 @@ auto ref choice(Range)(auto ref Range range) "Choice did not return a valid element from the given Range"); } +@safe unittest // issue 18631 +{ + auto rng = MinstdRand0(42); + const a = [0,1,2]; + const(int[]) b = [0, 1, 2]; + auto x = choice(a); + auto y = choice(b); + auto z = choice(cast(const)[1, 2, 3]); + auto x1 = choice(a, rng); + auto y1 = choice(b, rng); + auto z1 = choice(cast(const)[1, 2, 3], rng); +} + +@safe unittest // Ref range (issue 18631 PR) +{ + struct TestRange + { + int x; + ref int front() return {return x;} + ref int back() return {return x;} + void popFront() {} + void popBack() {} + bool empty = false; + TestRange save() {return this;} + size_t length = 10; + alias opDollar = length; + ref int opIndex(size_t i) return {return x;} + } + + TestRange r = TestRange(10); + int* s = &choice(r); +} + /** Shuffles elements of `r` using `gen` as a shuffler. `r` must be a random-access range with length. If no RNG is specified, `rndGen` @@ -3008,8 +3056,16 @@ if (isRandomAccessRange!Range) } /** -Rolls a dice with relative probabilities stored in $(D -proportions). Returns the index in `proportions` that was chosen. +Get a random index into a list of weights corresponding to each index + +Similar to rolling a die with relative probabilities stored in `proportions`. +Returns the index in `proportions` that was chosen. + +Note: + Usually, dice are 'fair', meaning that each side has equal probability + to come up, in which case `1 + uniform(0, 6)` can simply be used. + In future Phobos versions, this function might get renamed to something like + `weightedChoice` to avoid confusion. Params: rnd = (optional) random number generator to use; if not @@ -3055,6 +3111,9 @@ if (isNumeric!Num) /// @safe unittest { + auto d6 = 1 + dice(1, 1, 1, 1, 1, 1); // fair dice roll + auto d6b = 1 + dice(2, 1, 1, 1, 1, 1); // double the chance to roll '1' + auto x = dice(0.5, 0.5); // x is 0 or 1 in equal proportions auto y = dice(50, 50); // y is 0 or 1 in equal proportions auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time, diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index 8614dc9..a1fe962 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -1132,10 +1132,9 @@ each item is inferred from the size and type of the input array, respectively. Returns: The slice of `buffer` containing the data that was actually read. This will be shorter than `buffer` if EOF was reached before the buffer -could be filled. +could be filled. If the buffer is empty, it will be returned. -Throws: `Exception` if `buffer` is empty. - `ErrnoException` if the file is not opened or the call to `fread` fails. +Throws: `ErrnoException` if the file is not opened or the call to `fread` fails. `rawRead` always reads in binary mode on Windows. */ @@ -1144,7 +1143,7 @@ Throws: `Exception` if `buffer` is empty. import std.exception : enforce, errnoEnforce; if (!buffer.length) - throw new Exception("rawRead must take a non-empty buffer"); + return buffer; enforce(isOpen, "Attempting to read from an unopened file"); version (Windows) { @@ -1211,6 +1210,16 @@ Throws: `Exception` if `buffer` is empty. } } + // https://issues.dlang.org/show_bug.cgi?id=13893 + @system unittest + { + import std.exception : assertNotThrown; + + File f; + ubyte[0] u; + assertNotThrown(f.rawRead(u)); + } + /** Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file handle. The number of items to write and the size of each diff --git a/libphobos/src/std/sumtype.d b/libphobos/src/std/sumtype.d index 1d375ef..160665c 100644 --- a/libphobos/src/std/sumtype.d +++ b/libphobos/src/std/sumtype.d @@ -1941,79 +1941,8 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) auto ref matchImpl(SumTypes...)(auto ref SumTypes args) if (allSatisfy!(isSumType, SumTypes) && args.length > 0) { - enum typeCount(SumType) = SumType.Types.length; alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); - - /* A TagTuple represents a single possible set of tags that `args` - * could have at runtime. - * - * Because D does not allow a struct to be the controlling expression - * of a switch statement, we cannot dispatch on the TagTuple directly. - * Instead, we must map each TagTuple to a unique integer and generate - * a case label for each of those integers. - * - * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses - * the same technique that's used to map index tuples to memory offsets - * in a multidimensional static array. - * - * For example, when `args` consists of two SumTypes with two member - * types each, the TagTuples corresponding to each case label are: - * - * case 0: TagTuple([0, 0]) - * case 1: TagTuple([1, 0]) - * case 2: TagTuple([0, 1]) - * case 3: TagTuple([1, 1]) - * - * When there is only one argument, the caseId is equal to that - * argument's tag. - */ - static struct TagTuple - { - size_t[SumTypes.length] tags; - alias tags this; - - invariant - { - static foreach (i; 0 .. tags.length) - { - assert(tags[i] < SumTypes[i].Types.length, "Invalid tag"); - } - } - - this(ref const(SumTypes) args) - { - static foreach (i; 0 .. tags.length) - { - tags[i] = args[i].tag; - } - } - - static TagTuple fromCaseId(size_t caseId) - { - TagTuple result; - - // Most-significant to least-significant - static foreach_reverse (i; 0 .. result.length) - { - result[i] = caseId / stride!i; - caseId %= stride!i; - } - - return result; - } - - size_t toCaseId() - { - size_t result; - - static foreach (i; 0 .. tags.length) - { - result += tags[i] * stride!i; - } - - return result; - } - } + alias TagTuple = .TagTuple!(SumTypes); /* * A list of arguments to be passed to a handler needed for the case @@ -2149,6 +2078,81 @@ private template matchImpl(Flag!"exhaustive" exhaustive, handlers...) } } +private enum typeCount(SumType) = SumType.Types.length; + +/* A TagTuple represents a single possible set of tags that `args` + * could have at runtime. + * + * Because D does not allow a struct to be the controlling expression + * of a switch statement, we cannot dispatch on the TagTuple directly. + * Instead, we must map each TagTuple to a unique integer and generate + * a case label for each of those integers. + * + * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses + * the same technique that's used to map index tuples to memory offsets + * in a multidimensional static array. + * + * For example, when `args` consists of two SumTypes with two member + * types each, the TagTuples corresponding to each case label are: + * + * case 0: TagTuple([0, 0]) + * case 1: TagTuple([1, 0]) + * case 2: TagTuple([0, 1]) + * case 3: TagTuple([1, 1]) + * + * When there is only one argument, the caseId is equal to that + * argument's tag. + */ +private struct TagTuple(SumTypes...) +{ + size_t[SumTypes.length] tags; + alias tags this; + + alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes)); + + invariant + { + static foreach (i; 0 .. tags.length) + { + assert(tags[i] < SumTypes[i].Types.length, "Invalid tag"); + } + } + + this(ref const(SumTypes) args) + { + static foreach (i; 0 .. tags.length) + { + tags[i] = args[i].tag; + } + } + + static TagTuple fromCaseId(size_t caseId) + { + TagTuple result; + + // Most-significant to least-significant + static foreach_reverse (i; 0 .. result.length) + { + result[i] = caseId / stride!i; + caseId %= stride!i; + } + + return result; + } + + size_t toCaseId() + { + size_t result; + + static foreach (i; 0 .. tags.length) + { + result += tags[i] * stride!i; + } + + return result; + } +} + // Matching @safe unittest { diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index 8a3e22f..4ecfb10 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -4905,8 +4905,14 @@ if (is(Interface == interface) && is(BaseClass == class)) // - try default first // - only on a failure run & return fallback enum fallback = q{ - scope (failure) return fallback.%1$s(args); - return default_.%1$s(args); + try + { + return default_.%1$s(args); + } + catch (Exception) + { + return fallback.%1$s(args); + } }.format(__traits(identifier, func)); } @@ -6589,15 +6595,11 @@ if (!is(T == class) && !(is(T == interface))) private enum enableGCScan = hasIndirections!T; } - // TODO remove pure when https://issues.dlang.org/show_bug.cgi?id=15862 has been fixed extern(C) private pure nothrow @nogc static { pragma(mangle, "free") void pureFree( void *ptr ); static if (enableGCScan) - { - pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null ); - pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p ); - } + import core.memory : GC; } /// `RefCounted` storage implementation. @@ -6637,7 +6639,7 @@ if (!is(T == class) && !(is(T == interface))) { import std.internal.memory : enforceCalloc; _store = cast(Impl*) enforceCalloc(1, Impl.sizeof); - pureGcAddRange(&_store._payload, T.sizeof); + GC.addRange(&_store._payload, T.sizeof); } else { @@ -6650,7 +6652,7 @@ if (!is(T == class) && !(is(T == interface))) { static if (enableGCScan) { - pureGcRemoveRange(&this._store._payload); + GC.removeRange(&this._store._payload); } pureFree(_store); _store = null; diff --git a/libphobos/src/std/uni/package.d b/libphobos/src/std/uni/package.d index 98735ac..e12a70c 100644 --- a/libphobos/src/std/uni/package.d +++ b/libphobos/src/std/uni/package.d @@ -7032,9 +7032,7 @@ template genericDecodeGrapheme(bool getValue) case RI: if (isRegionalIndicator(ch)) mixin(eat); - else - goto L_End_Extend; - break; + goto L_End_Extend; case L: if (isHangL(ch)) mixin(eat); @@ -7166,6 +7164,10 @@ if (isInputRange!Input && is(immutable ElementType!Input == immutable dchar)) s = "\u11A8\u0308\uAC01"; assert(equal(decodeGrapheme(s)[], "\u11A8\u0308")); assert(equal(decodeGrapheme(s)[], "\uAC01")); + + // Two Union Jacks of the Great Britain + s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7"; + assert(equal(decodeGrapheme(s)[], "\U0001F1EC\U0001F1E7")); } /++ diff --git a/libphobos/testsuite/libphobos.gc/nocollect.d b/libphobos/testsuite/libphobos.gc/nocollect.d index 5df1483..64ed222 100644 --- a/libphobos/testsuite/libphobos.gc/nocollect.d +++ b/libphobos/testsuite/libphobos.gc/nocollect.d @@ -12,4 +12,4 @@ void main() stats = GC.profileStats(); assert(stats.numCollections == 0); -}
\ No newline at end of file +} diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h index 89772a7..75c6cc7 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h @@ -81,9 +81,10 @@ const unsigned struct_kernel_stat64_sz = 104; const unsigned struct_kernel_stat_sz = 144; const unsigned struct_kernel_stat64_sz = 104; #elif defined(__mips__) -const unsigned struct_kernel_stat_sz = SANITIZER_ANDROID - ? FIRST_32_SECOND_64(104, 128) - : FIRST_32_SECOND_64(144, 216); +const unsigned struct_kernel_stat_sz = + SANITIZER_ANDROID + ? FIRST_32_SECOND_64(104, 128) + : FIRST_32_SECOND_64((_MIPS_SIM == _ABIN32) ? 160 : 144, 216); const unsigned struct_kernel_stat64_sz = 104; #elif defined(__s390__) && !defined(__s390x__) const unsigned struct_kernel_stat_sz = 64; diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 6450871..35af704 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,76 @@ +2022-08-05 Jonathan Wakely <jwakely@redhat.com> + + * include/experimental/scope (__cpp_lib_experimental_scope): + Define. + * testsuite/experimental/scopeguard/uniqueres.cc: Check macro. + +2022-08-05 Jonathan Wakely <jwakely@redhat.com> + + * include/Makefile.am: Add new header. + * include/Makefile.in: Regenerate. + * include/experimental/scope: New file. + * testsuite/experimental/scopeguard/uniqueres.cc: New test. + * testsuite/experimental/scopeguard/exit.cc: New test. + +2022-08-04 Jonathan Wakely <jwakely@redhat.com> + + * include/std/string_view (basic_string_view(Range&&)): Add + explicit as per P2499R0. + * testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc: + Adjust implicit conversions. Check implicit conversions fail. + * testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc: + Likewise. + +2022-08-04 Jonathan Wakely <jwakely@redhat.com> + + * include/bits/fs_dir.h (directory_iterator): Add comparison + with std::default_sentinel_t. Remove redundant operator!= for + C++20. + * (recursive_directory_iterator): Likewise. + * include/bits/iterator_concepts.h [!__cpp_lib_concepts] + (default_sentinel_t, default_sentinel): Define even if concepts + are not supported. + * include/bits/regex.h (regex_iterator): Add comparison with + std::default_sentinel_t. Remove redundant operator!= for C++20. + (regex_token_iterator): Likewise. + (regex_token_iterator::_M_end_of_seq()): Add noexcept. + * testsuite/27_io/filesystem/iterators/lwg3719.cc: New test. + * testsuite/28_regex/iterators/regex_iterator/lwg3719.cc: + New test. + * testsuite/28_regex/iterators/regex_token_iterator/lwg3719.cc: + New test. + +2022-08-04 Jonathan Wakely <jwakely@redhat.com> + + * include/std/expected (unexpected::_M_val): Rename to _M_unex. + (bad_expected_access::_M_val): Likewise. + +2022-08-04 Jonathan Wakely <jwakely@redhat.com> + + * include/bits/ios_base.h (__cpp_lib_ios_noreplace): Update + value to 202207L. + * include/std/version (__cpp_lib_ios_noreplace): Likewise. + * testsuite/27_io/basic_ofstream/open/char/noreplace.cc: Check + for new value. + * testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc: + Likewise. + +2022-08-04 Jonathan Wakely <jwakely@redhat.com> + + PR libstdc++/106183 + * include/bits/atomic_wait.h (__waiter_pool_base::_M_notify): + Move increment of _M_ver here. + [!_GLIBCXX_HAVE_PLATFORM_WAIT]: Lock mutex around increment. + Use relaxed memory order and always notify all waiters. + (__waiter_base::_M_do_wait) [!_GLIBCXX_HAVE_PLATFORM_WAIT]: + Check value again after locking mutex. + (__waiter_base::_M_notify): Remove increment of _M_ver. + +2022-08-04 Ulrich Drepper <drepper@gmail.com> + + * python/libstdcxx/v6/printers.py (class StdTuplePrinter): Use + zero-based indeces just like std:get takes. + 2022-07-29 Jonathan Wakely <jwakely@redhat.com> PR libstdc++/104443 diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 069a16e..3eeb407 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -757,6 +757,7 @@ experimental_headers = \ ${experimental_srcdir}/random \ ${experimental_srcdir}/ratio \ ${experimental_srcdir}/regex \ + ${experimental_srcdir}/scope \ ${experimental_srcdir}/set \ ${experimental_srcdir}/simd \ ${experimental_srcdir}/socket \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 36eff73..e24563c 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -1115,6 +1115,7 @@ experimental_headers = \ ${experimental_srcdir}/random \ ${experimental_srcdir}/ratio \ ${experimental_srcdir}/regex \ + ${experimental_srcdir}/scope \ ${experimental_srcdir}/set \ ${experimental_srcdir}/simd \ ${experimental_srcdir}/socket \ diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h index a6d55d3..76ed740 100644 --- a/libstdc++-v3/include/bits/atomic_wait.h +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -221,18 +221,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } void - _M_notify(const __platform_wait_t* __addr, bool __all, bool __bare) noexcept + _M_notify(__platform_wait_t* __addr, [[maybe_unused]] bool __all, + bool __bare) noexcept { - if (!(__bare || _M_waiting())) - return; - #ifdef _GLIBCXX_HAVE_PLATFORM_WAIT - __platform_notify(__addr, __all); + if (__addr == &_M_ver) + { + __atomic_fetch_add(__addr, 1, __ATOMIC_SEQ_CST); + __all = true; + } + + if (__bare || _M_waiting()) + __platform_notify(__addr, __all); #else - if (__all) + { + lock_guard<mutex> __l(_M_mtx); + __atomic_fetch_add(__addr, 1, __ATOMIC_RELAXED); + } + if (__bare || _M_waiting()) _M_cv.notify_all(); - else - _M_cv.notify_one(); #endif } @@ -259,7 +266,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (__val == __old) { lock_guard<mutex> __l(_M_mtx); - _M_cv.wait(_M_mtx); + __atomic_load(__addr, &__val, __ATOMIC_RELAXED); + if (__val == __old) + _M_cv.wait(_M_mtx); } #endif // __GLIBCXX_HAVE_PLATFORM_WAIT } @@ -297,20 +306,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION , _M_addr(_S_wait_addr(__addr, &_M_w._M_ver)) { } - bool - _M_laundered() const - { return _M_addr == &_M_w._M_ver; } - void - _M_notify(bool __all, bool __bare = false) - { - if (_M_laundered()) - { - __atomic_fetch_add(_M_addr, 1, __ATOMIC_SEQ_CST); - __all = true; - } - _M_w._M_notify(_M_addr, __all, __bare); - } + _M_notify(bool __all, bool __bare = false) noexcept + { _M_w._M_notify(_M_addr, __all, __bare); } template<typename _Up, typename _ValFn, typename _Spin = __default_spin_policy> diff --git a/libstdc++-v3/include/bits/fs_dir.h b/libstdc++-v3/include/bits/fs_dir.h index ca37952..bec2b76 100644 --- a/libstdc++-v3/include/bits/fs_dir.h +++ b/libstdc++-v3/include/bits/fs_dir.h @@ -36,8 +36,9 @@ # include <bits/unique_ptr.h> # include <bits/shared_ptr.h> -#if __cplusplus > 201703L +#if __cplusplus >= 202002L # include <compare> // std::strong_ordering +# include <bits/iterator_concepts.h> // std::default_sentinel_t #endif namespace std _GLIBCXX_VISIBILITY(default) @@ -420,9 +421,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 return __pr; } - private: - directory_iterator(const path&, directory_options, error_code*); - friend bool operator==(const directory_iterator& __lhs, const directory_iterator& __rhs) noexcept @@ -431,10 +429,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 && !__lhs._M_dir.owner_before(__rhs._M_dir); } +#if __cplusplus >= 202002L + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3719. Directory iterators should be usable with default sentinel + bool operator==(default_sentinel_t) const noexcept + { return !_M_dir; } +#endif + +#if __cpp_impl_three_way_comparison < 201907L friend bool operator!=(const directory_iterator& __lhs, const directory_iterator& __rhs) noexcept { return !(__lhs == __rhs); } +#endif + + private: + directory_iterator(const path&, directory_options, error_code*); friend class recursive_directory_iterator; @@ -519,9 +529,6 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 void disable_recursion_pending() noexcept; - private: - recursive_directory_iterator(const path&, directory_options, error_code*); - friend bool operator==(const recursive_directory_iterator& __lhs, const recursive_directory_iterator& __rhs) noexcept @@ -530,10 +537,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 && !__lhs._M_dirs.owner_before(__rhs._M_dirs); } +#if __cplusplus >= 202002L + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3719. Directory iterators should be usable with default sentinel + bool operator==(default_sentinel_t) const noexcept + { return !_M_dirs; } +#endif + +#if __cpp_impl_three_way_comparison < 201907L friend bool operator!=(const recursive_directory_iterator& __lhs, const recursive_directory_iterator& __rhs) noexcept { return !(__lhs == __rhs); } +#endif + + private: + recursive_directory_iterator(const path&, directory_options, error_code*); struct _Dir_stack; std::__shared_ptr<_Dir_stack> _M_dirs; diff --git a/libstdc++-v3/include/bits/ios_base.h b/libstdc++-v3/include/bits/ios_base.h index e340971..5b55454 100644 --- a/libstdc++-v3/include/bits/ios_base.h +++ b/libstdc++-v3/include/bits/ios_base.h @@ -474,7 +474,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static const openmode __noreplace = _S_noreplace; #if __cplusplus >= 202100L -#define __cpp_lib_ios_noreplace 202200L +#define __cpp_lib_ios_noreplace 202207L /// Open a file in exclusive mode. static const openmode noreplace = _S_noreplace; #endif diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index a04c970..cf66c63 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -32,15 +32,35 @@ #pragma GCC system_header +#if __cplusplus >= 202002L #include <concepts> #include <bits/ptr_traits.h> // to_address #include <bits/ranges_cmp.h> // identity, ranges::less -#if __cpp_lib_concepts namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION + /** A sentinel type that can be used to check for the end of a range. + * + * For some iterator types the past-the-end sentinel value is independent + * of the underlying sequence, and a default sentinel can be used with them. + * For example, a `std::counted_iterator` keeps a count of how many elements + * remain, and so checking for the past-the-end value only requires checking + * if that count has reached zero. A past-the-end `std::istream_iterator` is + * equal to the default-constructed value, which can be easily checked. + * + * Comparing iterators of these types to `std::default_sentinel` is a + * convenient way to check if the end has been reached. + * + * @since C++20 + */ + struct default_sentinel_t { }; + + /// A default sentinel value. + inline constexpr default_sentinel_t default_sentinel{}; + +#if __cpp_lib_concepts struct input_iterator_tag; struct output_iterator_tag; struct forward_iterator_tag; @@ -924,9 +944,6 @@ namespace ranges inline constexpr unreachable_sentinel_t unreachable_sentinel{}; - struct default_sentinel_t { }; - inline constexpr default_sentinel_t default_sentinel{}; - // This is the namespace for [range.access] CPOs. namespace ranges::__cust_access { @@ -983,7 +1000,8 @@ namespace ranges } // namespace __detail +#endif // C++20 library concepts _GLIBCXX_END_NAMESPACE_VERSION } // namespace std -#endif // C++20 library concepts +#endif // C++20 #endif // _ITERATOR_CONCEPTS_H diff --git a/libstdc++-v3/include/bits/regex.h b/libstdc++-v3/include/bits/regex.h index 24298e3..561c8fc 100644 --- a/libstdc++-v3/include/bits/regex.h +++ b/libstdc++-v3/include/bits/regex.h @@ -28,6 +28,10 @@ * Do not attempt to use it directly. @headername{regex} */ +#if __cplusplus >= 202002L +# include <bits/iterator_concepts.h> // std::default_sentinel_t +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -2760,12 +2764,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 bool operator==(const regex_iterator&) const noexcept; +#if __cplusplus >= 202002L + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3719. Directory iterators should be usable with default sentinel + bool operator==(default_sentinel_t) const noexcept + { return _M_pregex == nullptr; } +#endif + +#if __cpp_impl_three_way_comparison < 201907L /** * @brief Tests the inequivalence of two regex iterators. */ bool operator!=(const regex_iterator& __rhs) const noexcept { return !(*this == __rhs); } +#endif /** * @brief Dereferences a %regex_iterator. @@ -2968,12 +2981,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 bool operator==(const regex_token_iterator& __rhs) const; +#if __cplusplus >= 202002L + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3719. Directory iterators should be usable with default sentinel + bool operator==(default_sentinel_t) const noexcept + { return _M_end_of_seq(); } +#endif + +#if __cpp_impl_three_way_comparison < 201907L /** * @brief Compares a %regex_token_iterator to another for inequality. */ bool operator!=(const regex_token_iterator& __rhs) const { return !(*this == __rhs); } +#endif /** * @brief Dereferences a %regex_token_iterator. @@ -3022,7 +3044,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } constexpr bool - _M_end_of_seq() const + _M_end_of_seq() const noexcept { return _M_result == nullptr; } // [28.12.2.2.4] diff --git a/libstdc++-v3/include/experimental/scope b/libstdc++-v3/include/experimental/scope new file mode 100644 index 0000000..208fadc --- /dev/null +++ b/libstdc++-v3/include/experimental/scope @@ -0,0 +1,497 @@ +// <experimental/scope> -*- C++ -*- + +// Copyright The GNU Toolchain Authors. +// +// This file is part of the GNU ISO C++ Library. This library 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. + +// This library 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// <http://www.gnu.org/licenses/>. + +/** @file experimental/scope + * This is a TS C++ Library header. + * @ingroup libfund-ts + */ + +#ifndef _GLIBCXX_EXPERIMENTAL_SCOPE +#define _GLIBCXX_EXPERIMENTAL_SCOPE 1 + +#pragma GCC system_header + +#if __cplusplus >= 202002L + +#include <concepts> +#include <exception> // uncaught_exceptions +#include <bits/refwrap.h> + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION +namespace experimental::inline fundamentals_v3 +{ +#define __cpp_lib_experimental_scope 201902 + + template<typename _Tp, typename _Up> + concept __not_same_as = !same_as<_Tp, _Up>; + + template<typename _Tp> + concept __not_lvalue_ref = !is_lvalue_reference_v<_Tp>; + + template<typename _Ef> + class [[nodiscard]] scope_exit + { + public: + template<typename _Efp> + requires __not_same_as<remove_cvref_t<_Efp>, scope_exit> + && constructible_from<_Ef, _Efp> + [[nodiscard]] explicit + scope_exit(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>) +#ifdef __cpp_exceptions + try +#endif + : _M_exit_function(__f) + { } +#ifdef __cpp_exceptions + catch (...) { __f(); } +#endif + + template<typename _Efp> + requires __not_same_as<remove_cvref_t<_Efp>, scope_exit> + && constructible_from<_Ef, _Efp> + && __not_lvalue_ref<_Efp> + && is_nothrow_constructible_v<_Ef, _Efp> + explicit + scope_exit(_Efp&& __f) noexcept + : _M_exit_function(std::forward<_Efp>(__f)) + { } + + scope_exit(scope_exit&& __rhs) noexcept + requires is_nothrow_move_constructible_v<_Ef> + : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function)) + { __rhs.release(); } + + scope_exit(scope_exit&& __rhs) + noexcept(is_nothrow_copy_constructible_v<_Ef>) + requires (!is_nothrow_move_constructible_v<_Ef>) + && is_copy_constructible_v<_Ef> + : _M_exit_function(__rhs._M_exit_function) + { __rhs.release(); } + + scope_exit(const scope_exit&) = delete; + scope_exit& operator=(const scope_exit&) = delete; + scope_exit& operator=(scope_exit&&) = delete; + + ~scope_exit() noexcept(noexcept(this->_M_exit_function)) + { + if (_M_execute_on_destruction) + _M_exit_function(); + } + + void release() noexcept { _M_execute_on_destruction = false; } + + private: + [[no_unique_address]] _Ef _M_exit_function; + bool _M_execute_on_destruction = true; + }; + + template<typename _Ef> + scope_exit(_Ef) -> scope_exit<_Ef>; + + template<typename _Ef> + class [[nodiscard]] scope_fail + { + public: + template<typename _Efp> + requires __not_same_as<remove_cvref_t<_Efp>, scope_fail> + && constructible_from<_Ef, _Efp> + explicit + scope_fail(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>) +#ifdef __cpp_exceptions + try +#endif + : _M_exit_function(__f) + { } +#ifdef __cpp_exceptions + catch (...) { __f(); } +#endif + + template<typename _Efp> + requires __not_same_as<remove_cvref_t<_Efp>, scope_fail> + && constructible_from<_Ef, _Efp> + && __not_lvalue_ref<_Efp> + && is_nothrow_constructible_v<_Ef, _Efp> + explicit + scope_fail(_Efp&& __f) noexcept + : _M_exit_function(std::forward<_Efp>(__f)) + { } + + scope_fail(scope_fail&& __rhs) noexcept + requires is_nothrow_move_constructible_v<_Ef> + : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function)) + { __rhs.release(); } + + scope_fail(scope_fail&& __rhs) + noexcept(is_nothrow_copy_constructible_v<_Ef>) + requires (!is_nothrow_move_constructible_v<_Ef>) + && is_copy_constructible_v<_Ef> + : _M_exit_function(__rhs._M_exit_function) + { __rhs.release(); } + + scope_fail(const scope_fail&) = delete; + scope_fail& operator=(const scope_fail&) = delete; + scope_fail& operator=(scope_fail&&) = delete; + + ~scope_fail() noexcept(noexcept(this->_M_exit_function)) + { + if (std::uncaught_exceptions() > _M_uncaught_init) + _M_exit_function(); + } + + void release() noexcept { _M_uncaught_init = __INT_MAX__; } + + private: + [[no_unique_address]] _Ef _M_exit_function; + int _M_uncaught_init = std::uncaught_exceptions(); + }; + + template<typename _Ef> + scope_fail(_Ef) -> scope_fail<_Ef>; + + template<typename _Ef> + class [[nodiscard]] scope_success + { + public: + template<typename _Efp> + requires __not_same_as<remove_cvref_t<_Efp>, scope_success> + && constructible_from<_Ef, _Efp> + explicit + scope_success(_Efp&& __f) noexcept(is_nothrow_constructible_v<_Ef, _Efp&>) + : _M_exit_function(__f) + { } + + template<typename _Efp> + requires __not_same_as<remove_cvref_t<_Efp>, scope_success> + && constructible_from<_Ef, _Efp> + && __not_lvalue_ref<_Efp> + && is_nothrow_constructible_v<_Ef, _Efp> + explicit + scope_success(_Efp&& __f) noexcept + : _M_exit_function(std::forward<_Efp>(__f)) + { } + + scope_success(scope_success&& __rhs) noexcept + requires is_nothrow_move_constructible_v<_Ef> + : _M_exit_function(std::forward<_Ef>(__rhs._M_exit_function)) + { __rhs.release(); } + + scope_success(scope_success&& __rhs) + noexcept(is_nothrow_copy_constructible_v<_Ef>) + requires (!is_nothrow_move_constructible_v<_Ef>) + && is_copy_constructible_v<_Ef> + : _M_exit_function(__rhs._M_exit_function) + { __rhs.release(); } + + scope_success(const scope_success&) = delete; + scope_success& operator=(const scope_success&) = delete; + scope_success& operator=(scope_success&&) = delete; + + ~scope_success() noexcept(noexcept(this->_M_exit_function)) + { + if (std::uncaught_exceptions() <= _M_uncaught_init) + _M_exit_function(); + } + + void release() noexcept { _M_uncaught_init = -__INT_MAX__; } + + private: + [[no_unique_address]] _Ef _M_exit_function; + int _M_uncaught_init = std::uncaught_exceptions(); + }; + + template<typename _Ef> + scope_success(_Ef) -> scope_success<_Ef>; + + template<typename _Resrc, typename _Del> + class [[nodiscard]] unique_resource + { + static_assert(!is_rvalue_reference_v<_Resrc>); + static_assert(!is_reference_v<_Del>); + + struct _Dummy { constexpr void release() { } }; + + template<typename _Tp> + struct _Wrap + { + template<typename _Up> + requires is_constructible_v<_Tp, _Up> + _Wrap(_Up&&) + noexcept(is_nothrow_constructible_v<_Tp, _Up>); + + template<typename _Up, typename _Del2> + requires is_constructible_v<_Tp, _Up> + _Wrap(_Up&& __r, _Del2&& __d) + noexcept(is_nothrow_constructible_v<_Tp, _Up>) + : _M_t(std::forward<_Up>(__r)) + { __d.release(); } + + _Wrap() = default; + + _Wrap(_Wrap&&) = default; + + _Wrap(_Wrap&& __rhs) noexcept(is_nothrow_constructible_v<_Tp, _Tp&>) + requires (!is_nothrow_move_constructible_v<_Tp>) + : _M_t(__rhs._M_t) + { } + + _Wrap& operator=(const _Wrap&) = default; + + _Wrap& operator=(_Wrap&&) = default; + + constexpr _Tp& get() noexcept { return _M_t; } + constexpr const _Tp& get() const noexcept { return _M_t; } + + [[no_unique_address]] _Tp _M_t{}; + }; + + template<typename _Tp> + struct _Wrap<_Tp&> + { + template<typename _Up> + requires is_constructible_v<reference_wrapper<_Tp>, _Up> + _Wrap(_Up&&) + noexcept(is_nothrow_constructible_v<reference_wrapper<_Tp>, _Up>); + + template<typename _Up, typename _Del2> + _Wrap(_Up&& __r, _Del2&& __d) + noexcept(is_nothrow_constructible_v<reference_wrapper<_Tp>, _Up>) + : _M_p(__builtin_addressof(static_cast<_Tp&>(__r))) + { __d.release(); } + + _Wrap() = delete; + + _Wrap(const _Wrap&) = default; + + _Wrap& operator=(const _Wrap&) = default; + + _Tp& get() noexcept { return *_M_p; } + const _Tp& get() const noexcept { return *_M_p; } + + _Tp* _M_p = nullptr; + }; + + using _Res1 = _Wrap<_Resrc>; + + template<typename _Tp, typename _Up> + requires is_constructible_v<_Tp, _Up> + && (is_nothrow_constructible_v<_Tp, _Up> + || is_constructible_v<_Tp, _Up&>) + using _Fwd_t + = __conditional_t<is_nothrow_constructible_v<_Tp, _Up>, _Up, _Up&>; + + template<typename _Tp, typename _Up> + static constexpr _Fwd_t<_Tp, _Up> + _S_fwd(_Up& __u) + { return static_cast<_Fwd_t<_Tp, _Up>&&>(__u); } + + template<typename _Tp, typename _Up, typename _Del2, typename _Res2> + static constexpr auto + _S_guard(_Del2& __d, _Res2& __r) + { + if constexpr (is_nothrow_constructible_v<_Tp, _Up>) + return _Dummy{}; + else + return scope_fail{[&] { __d(__r); }}; + } + + public: + unique_resource() = default; + + template<typename _Res2, typename _Del2> + requires requires { + typename _Fwd_t<_Res1, _Res2>; + typename _Fwd_t<_Del, _Del2>; + } + unique_resource(_Res2&& __r, _Del2&& __d) + noexcept((is_nothrow_constructible_v<_Res1, _Res2> + || is_nothrow_constructible_v<_Res1, _Res2&>) + && + (is_nothrow_constructible_v<_Del, _Del2> + || is_nothrow_constructible_v<_Del, _Del2&>)) + : _M_res(_S_fwd<_Res1, _Res2>(__r), + _S_guard<_Res1, _Res2>(__d, __r)), + _M_del(_S_fwd<_Del, _Del2>(__d), + _S_guard<_Del, _Del2>(__d, _M_res.get())), + _M_exec_on_reset(true) + { } + + unique_resource(unique_resource&& __rhs) noexcept + requires is_nothrow_move_constructible_v<_Res1> + && is_nothrow_move_constructible_v<_Del> + : _M_res(std::move(__rhs._M_res)), + _M_del(std::move(__rhs._M_del)), + _M_exec_on_reset(std::__exchange(__rhs._M_exec_on_reset, false)) + { } + + unique_resource(unique_resource&& __rhs) + requires is_nothrow_move_constructible_v<_Res1> + && (!is_nothrow_move_constructible_v<_Del>) + : _M_res(std::move(__rhs._M_res)), + _M_del(_S_fwd<_Del, _Del>(__rhs._M_del.get()), + scope_fail([&]{ + if (__rhs._M_exec_on_reset) + { + __rhs._M_del.get()(_M_res.get()); + __rhs.release(); + } + })), + _M_exec_on_reset(std::__exchange(__rhs._M_exec_on_reset, false)) + { } + + unique_resource(unique_resource&& __rhs) + requires (!is_nothrow_move_constructible_v<_Res1>) + : unique_resource(__rhs._M_res.get(), __rhs._M_del.get(), _Dummy{}) + { + if (__rhs._M_exec_on_reset) + { + _M_exec_on_reset = true; + __rhs._M_exec_on_reset = false; + } + } + + // 3.3.3.3, Destructor + ~unique_resource() { reset(); } + + // 3.3.3.4, Assignment + unique_resource& + operator=(unique_resource&& __rhs) + noexcept(is_nothrow_move_assignable_v<_Res1> + && is_nothrow_move_assignable_v<_Del>) + { + reset(); + if constexpr (is_nothrow_move_assignable_v<_Res1>) + { + if constexpr (is_nothrow_move_assignable_v<_Del>) + { + _M_res = std::move(__rhs._M_res); + _M_del = std::move(__rhs._M_del); + } + else + { + _M_del = __rhs._M_del; + _M_res = std::move(__rhs._M_res); + } + } + else + { + if constexpr (is_nothrow_move_assignable_v<_Del>) + { + _M_res = __rhs._M_res; + _M_del = std::move(__rhs._M_del); + } + else + { + _M_res = __rhs._M_res; + _M_del = __rhs._M_del; + } + } + _M_exec_on_reset = std::__exchange(__rhs._M_exec_on_reset, false); + return *this; + } + + // 3.3.3.5, Other member functions + void + reset() noexcept + { + if (_M_exec_on_reset) + { + _M_exec_on_reset = false; + _M_del.get()(_M_res.get()); + } + } + + template<typename _Res2> + void + reset(_Res2&& __r) + { + reset(); + if constexpr (is_nothrow_assignable_v<_Res1&, _Res2>) + _M_res.get() = std::forward<_Res2>(__r); + else + _M_res.get() = const_cast<const remove_reference_t<_Res2>&>(__r); + _M_exec_on_reset = true; + } + + void + release() noexcept + { _M_exec_on_reset = false; } + + const _Resrc& + get() const noexcept + { return _M_res.get(); } + + add_lvalue_reference_t<remove_pointer_t<_Resrc>> + operator*() const noexcept + requires is_pointer_v<_Resrc> && (!is_void_v<remove_pointer_t<_Resrc>>) + { return *get(); } + + _Resrc operator->() const noexcept + requires is_pointer_v<_Resrc> + { return _M_res.get(); } + + const _Del& + get_deleter() const noexcept + { return _M_del.get(); } + + private: + [[no_unique_address]] _Res1 _M_res{}; + [[no_unique_address]] _Wrap<_Del> _M_del{}; + bool _M_exec_on_reset = false; + + template<typename _Res2, typename _Del2, typename _St> + friend unique_resource<decay_t<_Res2>, decay_t<_Del2>> + make_unique_resource_checked(_Res2&&, const _St&, _Del2&&) + noexcept(is_nothrow_constructible_v<decay_t<_Res2>, _Res2> + && is_nothrow_constructible_v<decay_t<_Del2>, _Del2>); + + template<typename _Res2, typename _Del2> + unique_resource(_Res2&& __r, _Del2&& __d, _Dummy __noop) + noexcept(is_nothrow_constructible_v<_Resrc, _Res2> + && is_nothrow_constructible_v<_Del, _Del2>) + : _M_res(std::forward<_Res2>(__r), __noop), + _M_del(std::forward<_Del>(__d), __noop) + { } + }; + + template<typename _Resrc, typename _Del> + unique_resource(_Resrc, _Del) -> unique_resource<_Resrc, _Del>; + + template<typename _Resrc, typename _Del, typename _St = decay_t<_Resrc>> + unique_resource<decay_t<_Resrc>, decay_t<_Del>> + make_unique_resource_checked(_Resrc&& __r, const _St& __invalid, _Del&& __d) + noexcept(is_nothrow_constructible_v<decay_t<_Resrc>, _Resrc> + && is_nothrow_constructible_v<decay_t<_Del>, _Del>) + { + if (__r == __invalid) + return { std::forward<_Resrc>(__r), std::forward<_Del>(__d), {} }; + return { std::forward<_Resrc>(__r), std::forward<_Del>(__d) }; + } + +} // namespace experimental::fundamentals_v3 +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // C++20 +#endif // _GLIBCXX_EXPERIMENTAL_SCOPE diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected index 3446d6d..3ee13aa 100644 --- a/libstdc++-v3/include/std/expected +++ b/libstdc++-v3/include/std/expected @@ -95,32 +95,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION class bad_expected_access : public bad_expected_access<void> { public: explicit - bad_expected_access(_Er __e) : _M_val(std::move(__e)) { } + bad_expected_access(_Er __e) : _M_unex(std::move(__e)) { } // XXX const char* what() const noexcept override; [[nodiscard]] _Er& error() & noexcept - { return _M_val; } + { return _M_unex; } [[nodiscard]] const _Er& error() const & noexcept - { return _M_val; } + { return _M_unex; } [[nodiscard]] _Er&& error() && noexcept - { return std::move(_M_val); } + { return std::move(_M_unex); } [[nodiscard]] const _Er&& error() const && noexcept - { return std::move(_M_val); } + { return std::move(_M_unex); } private: - _Er _M_val; + _Er _M_unex; }; /// Tag type for constructing unexpected values in a std::expected @@ -175,7 +175,7 @@ namespace __expected constexpr explicit unexpected(_Err&& __e) noexcept(is_nothrow_constructible_v<_Er, _Err>) - : _M_val(std::forward<_Err>(__e)) + : _M_unex(std::forward<_Err>(__e)) { } template<typename... _Args> @@ -183,7 +183,7 @@ namespace __expected constexpr explicit unexpected(in_place_t, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Er, _Args...>) - : _M_val(std::forward<_Args>(__args)...) + : _M_unex(std::forward<_Args>(__args)...) { } template<typename _Up, typename... _Args> @@ -192,7 +192,7 @@ namespace __expected unexpected(in_place_t, initializer_list<_Up> __il, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&, _Args...>) - : _M_val(__il, std::forward<_Args>(__args)...) + : _M_unex(__il, std::forward<_Args>(__args)...) { } constexpr unexpected& operator=(const unexpected&) = default; @@ -201,33 +201,33 @@ namespace __expected [[nodiscard]] constexpr const _Er& - error() const & noexcept { return _M_val; } + error() const & noexcept { return _M_unex; } [[nodiscard]] constexpr _Er& - error() & noexcept { return _M_val; } + error() & noexcept { return _M_unex; } [[nodiscard]] constexpr const _Er&& - error() const && noexcept { return std::move(_M_val); } + error() const && noexcept { return std::move(_M_unex); } [[nodiscard]] constexpr _Er&& - error() && noexcept { return std::move(_M_val); } + error() && noexcept { return std::move(_M_unex); } constexpr void swap(unexpected& __other) noexcept(is_nothrow_swappable_v<_Er>) { static_assert( is_swappable_v<_Er> ); using std::swap; - swap(_M_val, __other._M_val); + swap(_M_unex, __other._M_unex); } template<typename _Err> [[nodiscard]] friend constexpr bool operator==(const unexpected& __x, const unexpected<_Err>& __y) - { return __x._M_val == __y.error(); } + { return __x._M_unex == __y.error(); } friend constexpr void swap(unexpected& __x, unexpected& __y) @@ -236,7 +236,7 @@ namespace __expected { __x.swap(__y); } private: - _Er _M_val; + _Er _M_unex; }; template<typename _Er> unexpected(_Er) -> unexpected<_Er>; diff --git a/libstdc++-v3/include/std/string_view b/libstdc++-v3/include/std/string_view index bccf4d1..30ff136 100644 --- a/libstdc++-v3/include/std/string_view +++ b/libstdc++-v3/include/std/string_view @@ -162,7 +162,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }) && (!requires { typename _DRange::traits_type; } || is_same_v<typename _DRange::traits_type, _Traits>) - constexpr + constexpr explicit basic_string_view(_Range&& __r) noexcept(noexcept(ranges::size(__r)) && noexcept(ranges::data(__r))) : _M_len(ranges::size(__r)), _M_str(ranges::data(__r)) diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 5edca2f..6b61879 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -316,7 +316,7 @@ # define __cpp_lib_expected 202202L #endif #define __cpp_lib_invoke_r 202106L -#define __cpp_lib_ios_noreplace 202200L +#define __cpp_lib_ios_noreplace 202207L #if __cpp_lib_concepts # undef __cpp_lib_optional # define __cpp_lib_optional 202110L diff --git a/libstdc++-v3/python/libstdcxx/v6/printers.py b/libstdc++-v3/python/libstdcxx/v6/printers.py index 17c33c1..d70c8d5 100644 --- a/libstdc++-v3/python/libstdcxx/v6/printers.py +++ b/libstdc++-v3/python/libstdcxx/v6/printers.py @@ -611,9 +611,9 @@ class StdTuplePrinter: # the value "as is". fields = impl.type.fields () if len (fields) < 1 or fields[0].name != "_M_head_impl": - return ('[%d]' % self.count, impl) + return ('[%d]' % (self.count - 1), impl) else: - return ('[%d]' % self.count, impl['_M_head_impl']) + return ('[%d]' % (self.count - 1), impl['_M_head_impl']) def __init__ (self, typename, val): self.typename = strip_versioned_namespace(typename) diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc index bd50c30..a5745fc 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/char/range_c++20.cc @@ -36,7 +36,7 @@ test01() }; R r; - std::string_view s = r; + std::string_view s{r}; VERIFY( s == r.str ); VERIFY( s.data() == std::ranges::data(r) ); VERIFY( s.size() == std::ranges::size(r) ); @@ -50,10 +50,15 @@ test01() static_assert( std::ranges::contiguous_range<R2> ); static_assert( std::ranges::sized_range<R2> ); R2 r2; - std::string_view s2 = r2; // uses conversion to string_view + std::string_view s2(r2); // uses conversion to string_view VERIFY( s2 == "Out of range" ); VERIFY( std::string_view(const_cast<const R2&>(r2)) == s2 ); + // And again using copy-initialization instead of direct-initialization. + std::string_view s2_implicit = r2; // uses conversion to string_view + VERIFY( s2_implicit == "Out of range" ); + VERIFY( std::string_view(const_cast<const R2&>(r2)) == s2_implicit ); + struct R3 : R { using R::begin; @@ -91,7 +96,7 @@ test01() static_assert( std::ranges::contiguous_range<R5> ); static_assert( std::ranges::sized_range<R5> ); R5 r5; - std::string_view s5 = r5; // Uses range constructor + std::string_view s5(r5); // Uses range constructor VERIFY( s5 == r5.str ); s5 = std::string_view(std::move(r5)); // In C++20 this used conversion op. VERIFY( s5 == r5.str ); // In C++23 it uses range constructor. @@ -156,15 +161,30 @@ test04() }; R r; - std::basic_string_view s = r; // Use deduction guide. + std::basic_string_view s(r); // Use deduction guide. static_assert( std::is_same_v<decltype(s), std::string_view> ); } +void +test05() +{ + struct R + { + const char* begin() const { return nullptr; } + const char* end() const { return nullptr; } + }; + + // P2499R0 string_view range constructor should be explicit + // P2516R0 string_view is implicitly convertible from what? + static_assert( ! std::is_convertible_v<R, std::string_view> ); +} + int main() { test01(); test02(); test03(); test04(); + test05(); } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc index 0b28220..af3c986 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/cons/wchar_t/range_c++20.cc @@ -36,7 +36,7 @@ test01() }; R r; - std::wstring_view s = r; + std::wstring_view s{r}; VERIFY( s == r.str ); VERIFY( s.data() == std::ranges::data(r) ); VERIFY( s.size() == std::ranges::size(r) ); @@ -50,10 +50,15 @@ test01() static_assert( std::ranges::contiguous_range<R2> ); static_assert( std::ranges::sized_range<R2> ); R2 r2; - std::wstring_view s2 = r2; // uses conversion to wstring_view + std::wstring_view s2(r2); // uses conversion to wstring_view VERIFY( s2 == L"Out of range" ); VERIFY( std::wstring_view(const_cast<const R2&>(r2)) == s2 ); + // And again using copy-initialization instead of direct-initialization. + std::wstring_view s2_implicit = r2; // uses conversion to wstring_view + VERIFY( s2_implicit == L"Out of range" ); + VERIFY( std::wstring_view(const_cast<const R2&>(r2)) == s2_implicit ); + struct R3 : R { using R::begin; @@ -91,10 +96,10 @@ test01() static_assert( std::ranges::contiguous_range<R5> ); static_assert( std::ranges::sized_range<R5> ); R5 r5; - std::wstring_view s5 = r5; // Uses range constructor + std::wstring_view s5(r5); // Uses range constructor VERIFY( s5 == r5.str ); s5 = std::wstring_view(std::move(r5)); // In C++20 this used conversion op. - VERIFY( s5 == r5.str ); // In C++23 it uses range constructor. + VERIFY( s5 == r5.str ); // In C++23 it uses range constructor. wchar_t arr[] = L"arrangement\0with\0nulls"; std::wstring_view sa = arr; // Does not use range constructor @@ -156,15 +161,30 @@ test04() }; R r; - std::basic_string_view s = r; // Use deduction guide. + std::basic_string_view s(r); // Use deduction guide. static_assert( std::is_same_v<decltype(s), std::wstring_view> ); } +void +test05() +{ + struct R + { + const wchar_t* begin() const { return nullptr; } + const wchar_t* end() const { return nullptr; } + }; + + // P2499R0 string_view range constructor should be explicit + // P2516R0 string_view is implicitly convertible from what? + static_assert( ! std::is_convertible_v<R, std::wstring_view> ); +} + int main() { test01(); test02(); test03(); test04(); + test05(); } diff --git a/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc index e39f592..56ff2d7 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc @@ -2,10 +2,10 @@ #include <ios> -#if __cplusplus >= 202200L +#if __cplusplus >= 202207L #ifndef __cpp_lib_ios_noreplace # error "Feature-test macro for ios::noreplace missing in <ios>" -#elif __cpp_lib_ios_noreplace < 202200L +#elif __cpp_lib_ios_noreplace < 202207L # error "Feature-test macro for ios::noreplace has wrong value in <ios>" #endif #endif diff --git a/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc index 77f1186..f0425cd 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc @@ -2,10 +2,10 @@ #include <version> -#if __cplusplus >= 202200L +#if __cplusplus >= 202207L #ifndef __cpp_lib_ios_noreplace # error "Feature-test macro for ios::noreplace missing in <version>" -#elif __cpp_lib_ios_noreplace < 202200L +#elif __cpp_lib_ios_noreplace < 202207L # error "Feature-test macro for ios::noreplace has wrong value in <version>" #endif #endif diff --git a/libstdc++-v3/testsuite/27_io/filesystem/iterators/lwg3719.cc b/libstdc++-v3/testsuite/27_io/filesystem/iterators/lwg3719.cc new file mode 100644 index 0000000..c19cddc --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/iterators/lwg3719.cc @@ -0,0 +1,39 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } +// { dg-require-filesystem-ts "" } + +#include <filesystem> +#include <iterator> +#include <testsuite_hooks.h> + +// LWG 3719. Directory iterators should be usable with default sentinel + +void +test_dir_iter() +{ + std::filesystem::directory_iterator d0; + VERIFY( d0 == std::default_sentinel ); + std::filesystem::directory_iterator d1("."); + VERIFY( d1 != std::default_sentinel ); + + static_assert( noexcept(d0 == std::default_sentinel) ); + static_assert( noexcept(d0 != std::default_sentinel) ); +} + +void +test_recursive_dir_iter() +{ + std::filesystem::recursive_directory_iterator d0; + VERIFY( d0 == std::default_sentinel ); + std::filesystem::recursive_directory_iterator d1("."); + VERIFY( d1 != std::default_sentinel ); + + static_assert( noexcept(d0 == std::default_sentinel) ); + static_assert( noexcept(d0 != std::default_sentinel) ); +} + +int main() +{ + test_dir_iter(); + test_recursive_dir_iter(); +} diff --git a/libstdc++-v3/testsuite/28_regex/iterators/regex_iterator/lwg3719.cc b/libstdc++-v3/testsuite/28_regex/iterators/regex_iterator/lwg3719.cc new file mode 100644 index 0000000..e8c8f79 --- /dev/null +++ b/libstdc++-v3/testsuite/28_regex/iterators/regex_iterator/lwg3719.cc @@ -0,0 +1,29 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include <regex> +#include <iterator> +#include <testsuite_hooks.h> + +// LWG 3719. Directory iterators should be usable with default sentinel + +void +test_iter() +{ + std::sregex_token_iterator r0; + VERIFY( r0 == std::default_sentinel ); + std::string haystack = "a needle in a haystack"; + std::regex needle("needle"); + std::sregex_iterator r1(haystack.begin(), haystack.end(), needle); + VERIFY( r1 != std::default_sentinel ); + ++r1; + VERIFY( r1 == std::default_sentinel ); + + static_assert( noexcept(r0 == std::default_sentinel) ); // GCC extension + static_assert( noexcept(r0 != std::default_sentinel) ); // GCC extension +} + +int main() +{ + test_iter(); +} diff --git a/libstdc++-v3/testsuite/28_regex/iterators/regex_token_iterator/lwg3719.cc b/libstdc++-v3/testsuite/28_regex/iterators/regex_token_iterator/lwg3719.cc new file mode 100644 index 0000000..5c36ace --- /dev/null +++ b/libstdc++-v3/testsuite/28_regex/iterators/regex_token_iterator/lwg3719.cc @@ -0,0 +1,29 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include <regex> +#include <iterator> +#include <testsuite_hooks.h> + +// LWG 3719. Directory iterators should be usable with default sentinel + +void +test_iter() +{ + std::sregex_iterator r0; + VERIFY( r0 == std::default_sentinel ); + std::string haystack = "a needle in a haystack"; + std::regex needle("needle"); + std::sregex_iterator r1(haystack.begin(), haystack.end(), needle); + VERIFY( r1 != std::default_sentinel ); + ++r1; + VERIFY( r1 == std::default_sentinel ); + + static_assert( noexcept(r0 == std::default_sentinel) ); // GCC extension + static_assert( noexcept(r0 != std::default_sentinel) ); // GCC extension +} + +int main() +{ + test_iter(); +} diff --git a/libstdc++-v3/testsuite/experimental/scopeguard/exit.cc b/libstdc++-v3/testsuite/experimental/scopeguard/exit.cc new file mode 100644 index 0000000..60616d1 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/scopeguard/exit.cc @@ -0,0 +1,300 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include <experimental/scope> +#include <testsuite_hooks.h> + +int da_funk = 0; +void funk() { ++da_funk; } + +struct ThrowingCopy +{ + ThrowingCopy() = default; + ThrowingCopy(ThrowingCopy&&) noexcept(false) { VERIFY(false); } + ThrowingCopy(const ThrowingCopy&) { if (nocopy) throw 1; } + + void operator()() const noexcept { ++counter; } + + static ThrowingCopy create() noexcept { nocopy = false; return {}; } + + static bool nocopy; + static int counter; +}; + +bool ThrowingCopy::nocopy = false; +int ThrowingCopy::counter = 0; + +void +test_exit() +{ + using std::experimental::scope_exit; + + int counter = 0; + auto d = [&counter] () { ++counter; }; + + { + scope_exit e(d); + } + VERIFY( counter == 1 ); + + try + { + scope_exit e(d); + throw 1; + } + catch (int) + { + } + VERIFY( counter == 2 ); + + { + scope_exit e(d); + scope_exit e2(std::move(e)); + } + VERIFY( counter == 3 ); + + { + scope_exit e(d); + e.release(); + } + VERIFY( counter == 3 ); + + try + { + scope_exit e(d); + e.release(); + throw 1; + } + catch (int) + { + } + VERIFY( counter == 3 ); + + { + da_funk = 0; + scope_exit<void(&)()> e(funk); + } + VERIFY( da_funk == 1 ); + + static_assert(!std::is_move_assignable_v<scope_exit<void(*)()>>); + static_assert(!std::is_move_assignable_v<scope_exit<void(&)()>>); + static_assert(!std::is_move_assignable_v<scope_exit<ThrowingCopy>>); + static_assert(!std::is_move_assignable_v<scope_exit<decltype(d)>>); + + { + ThrowingCopy::counter = 0; + try + { + scope_exit<ThrowingCopy> e(ThrowingCopy::create()); + ThrowingCopy::nocopy = true; + scope_exit<ThrowingCopy> e2(std::move(e)); + VERIFY(false); + } + catch (int) + { + } + VERIFY( ThrowingCopy::counter == 1 ); + + scope_exit<ThrowingCopy> e(ThrowingCopy::create()); + try + { + ThrowingCopy::nocopy = true; + scope_exit<ThrowingCopy> e2(std::move(e)); + VERIFY(false); + } + catch (int) + { + } + VERIFY( ThrowingCopy::counter == 1 ); + } + VERIFY( ThrowingCopy::counter == 2 ); +} + +void +test_fail() +{ + using std::experimental::scope_fail; + + int counter = 0; + auto d = [&counter] () { ++counter; }; + + { + scope_fail f(d); + } + VERIFY( counter == 0 ); + + try + { + scope_fail f(d); + throw 1; + } + catch (int) + { + } + VERIFY( counter == 1 ); + + { + scope_fail f(d); + f.release(); + } + VERIFY( counter == 1 ); + + try + { + scope_fail f(d); + scope_fail f2(std::move(f)); + throw 1; + } + catch(int) + { + } + VERIFY( counter == 2 ); + + try + { + scope_fail f(d); + f.release(); + throw 1; + } + catch (int) + { + } + VERIFY( counter == 2 ); + + try + { + da_funk = 0; + scope_fail<void(&)()> e(funk); + throw 1; + } + catch (int) + { + } + VERIFY( da_funk == 1 ); + + static_assert(!std::is_move_assignable_v<scope_fail<void(*)()>>); + static_assert(!std::is_move_assignable_v<scope_fail<void(&)()>>); + static_assert(!std::is_move_assignable_v<scope_fail<ThrowingCopy>>); + static_assert(!std::is_move_assignable_v<scope_fail<decltype(d)>>); + + { + ThrowingCopy::counter = 0; + try + { + scope_fail<ThrowingCopy> f(ThrowingCopy::create()); + ThrowingCopy::nocopy = true; + scope_fail<ThrowingCopy> f2(std::move(f)); + VERIFY(false); + } + catch (int) + { + } + VERIFY( ThrowingCopy::counter == 1 ); + + scope_fail<ThrowingCopy> f(ThrowingCopy::create()); + try + { + ThrowingCopy::nocopy = true; + scope_fail<ThrowingCopy> f2(std::move(f)); + VERIFY(false); + } + catch (int) + { + } + VERIFY( ThrowingCopy::counter == 1 ); + } + VERIFY( ThrowingCopy::counter == 1 ); +} + +void +test_success() +{ + using std::experimental::scope_success; + + int counter = 0; + auto d = [&counter] () { ++counter; }; + + { + scope_success s(d); + } + VERIFY( counter == 1 ); + + try + { + scope_success s(d); + throw 1; + } + catch (int) + { + } + VERIFY( counter == 1 ); + + { + scope_success s(d); + scope_success s2(std::move(s)); + } + VERIFY( counter == 2 ); + + { + scope_success s(d); + s.release(); + } + VERIFY( counter == 2 ); + + try + { + scope_success s(d); + s.release(); + throw 1; + } + catch (int) + { + } + VERIFY( counter == 2 ); + + { + da_funk = 0; + scope_success<void(&)()> e(funk); + } + VERIFY( da_funk == 1 ); + + static_assert(!std::is_move_assignable_v<scope_success<void(*)()>>); + static_assert(!std::is_move_assignable_v<scope_success<void(&)()>>); + static_assert(!std::is_move_assignable_v<scope_success<ThrowingCopy>>); + static_assert(!std::is_move_assignable_v<scope_success<decltype(d)>>); + + { + ThrowingCopy::counter = 0; + try + { + scope_success<ThrowingCopy> s(ThrowingCopy::create()); + ThrowingCopy::nocopy = true; + scope_success<ThrowingCopy> s2(std::move(s)); + VERIFY(false); + } + catch (int) + { + } + VERIFY( ThrowingCopy::counter == 0 ); + + scope_success<ThrowingCopy> s(ThrowingCopy::create()); + try + { + ThrowingCopy::nocopy = true; + scope_success<ThrowingCopy> s2(std::move(s)); + VERIFY(false); + } + catch (int) + { + } + VERIFY( ThrowingCopy::counter == 0 ); + } + VERIFY( ThrowingCopy::counter == 1 ); +} + +int main() +{ + test_exit(); + test_fail(); + test_success(); +} diff --git a/libstdc++-v3/testsuite/experimental/scopeguard/uniqueres.cc b/libstdc++-v3/testsuite/experimental/scopeguard/uniqueres.cc new file mode 100644 index 0000000..fe9d6ee --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/scopeguard/uniqueres.cc @@ -0,0 +1,366 @@ +// { dg-options "-std=gnu++20" } +// { dg-do run { target c++20 } } + +#include <experimental/scope> +#include <testsuite_hooks.h> + +#ifndef __cpp_lib_experimental_scope +# error Feature-test macro is not defined. +#elif __cpp_lib_experimental_scope < 201902 +# error Feature-test macro has bad value. +#endif + +using std::experimental::unique_resource; + +void +test_default_cons() +{ + struct val { int i; }; + + struct del + { + void operator()(val) const { VERIFY(false); } + int i; + }; + + static_assert( std::is_default_constructible_v<unique_resource<val, del>> ); + static_assert( !std::is_default_constructible_v<unique_resource<val&, del>> ); + // GCC extension: + static_assert( std::is_nothrow_default_constructible_v<unique_resource<val, del>> ); + struct exval : val { exval() noexcept(false) { } }; + static_assert( !std::is_nothrow_default_constructible_v<unique_resource<exval, del>> ); + + unique_resource<val, del> res; + VERIFY( res.get().i == 0 ); // value-initialized + VERIFY( res.get_deleter().i == 0 ); // value-initialized +} + +void +test_cons() +{ + struct val { int i; }; + + struct del + { + void operator()(val v) const { VERIFY(v.i == i); } + int i; + }; + + auto r1 = unique_resource<val, del>(val{}, del{}); + VERIFY( r1.get().i == 0 ); + VERIFY( r1.get_deleter().i == 0 ); + + auto r2 = unique_resource<val, del>(1, 2); + VERIFY( r2.get().i == 1 ); + VERIFY( r2.get_deleter().i == 2 ); + r2.release(); + + val v{3}; + auto r3 = unique_resource<val&, del>(v, 3); + + static_assert( !std::is_constructible_v<unique_resource<val&, del>, val, del> ); + static_assert( !std::is_constructible_v<unique_resource<val&, del>, int, del> ); + static_assert( !std::is_constructible_v<unique_resource<val&, del>, const val&, del> ); + + del d4{4}; + auto r4 = unique_resource(std::ref(v), std::ref(d4)); + --d4.i; + + static_assert( std::is_same_v<decltype(r4), + unique_resource<std::reference_wrapper<val>, + std::reference_wrapper<del>>> ); + static_assert( !std::is_constructible_v<decltype(r4), val, del> ); + + int counter = 0, dcounter = 99; + { + unique_resource r(std::ref(counter), + [&dcounter] (int& i) { ++dcounter; ++i; }); + } + VERIFY( counter == 1 ); + VERIFY( dcounter == 100 ); + + { + struct NothrowMove + { + NothrowMove() noexcept { } + NothrowMove(NothrowMove&&) noexcept(true) { } + NothrowMove(const NothrowMove&) { throw 1; } + }; + + unique_resource r(NothrowMove{}, + [&dcounter] (NothrowMove&) { ++dcounter; }); + } + VERIFY( dcounter == 101 ); + + { + struct ThrowOnCopy + { + ThrowOnCopy() noexcept { } + ThrowOnCopy(ThrowOnCopy&&) noexcept(false) { VERIFY(false); }; + ThrowOnCopy(const ThrowOnCopy&) { throw 1; } + explicit ThrowOnCopy(val) noexcept(false) { VERIFY(false); } + explicit ThrowOnCopy(val&) noexcept(false) { } + }; + auto d = [&dcounter] (auto&) { ++dcounter; }; + + unique_resource r(val(1), d); // uses ThrowOnCopy(val&) + + try { + unique_resource r(ThrowOnCopy{}, d); // uses copy constructor + VERIFY( false ); + } catch (int) { + VERIFY( dcounter == 102 ); + } + } + VERIFY( dcounter == 103 ); + + { + struct CopyVal + { + explicit CopyVal(const val& v) : i(v.i) { } + int i; + }; + + struct Del + { + void operator()(const val&) { VERIFY(false); } + void operator()(const CopyVal& c) { ref = c.i; } + int& ref; + }; + + struct CopyDel + { + explicit CopyDel(Del&&) noexcept(false) { VERIFY(false); } + explicit CopyDel(const Del&) noexcept(false) { throw 1; } + void operator()(const val&) = delete; + void operator()(const CopyVal&) { VERIFY(false); } + }; + + try { + // CopyVal is constructed from val(11), then initializing CopyDel throws. + // The CopyVal member is passed to the Del argument to be freed. + unique_resource<CopyVal, CopyDel> r(val(11), Del{dcounter}); + VERIFY( false ); + } catch (int) { + VERIFY( dcounter == 11 ); + } + } +} + +void +test_move_cons() +{ + { + struct Del + { + void operator()(int) const { VERIFY(false); } + }; + + unique_resource<int, Del> r0; + auto rr0 = std::move(r0); + VERIFY( r0.get() == 0 ); + VERIFY( rr0.get() == 0 ); + + struct DelThrowingCopy + { + DelThrowingCopy() = default; + DelThrowingCopy(const DelThrowingCopy&) { throw 1; } + void operator()(int) const { VERIFY(false); } + }; + + unique_resource<int, DelThrowingCopy> r1; + try { + auto rr1 = std::move(r1); // Initializing deleter throws. + VERIFY( false ); + } catch (int) { + } + } + + { + struct Res + { + Res() = default; + Res(Res&& r) noexcept : moved(r.moved) { r.moved = true; } + Res(Res& r) : moved(r.moved) { } + bool moved = false; + }; + + unique_resource r(Res{}, [](const auto&) { }); + auto rr = std::move(r); + VERIFY( r.get().moved == true ); + VERIFY( rr.get().moved == false ); + } + + { + struct Res2 + { + Res2() = default; + Res2(Res2&& r) noexcept(false) : moved(r.moved) { r.moved = false; } + Res2(Res2& r) : moved(r.moved) { } + bool moved = false; + }; + + unique_resource r2(Res2{}, [](const auto&) { }); + auto rr2 = std::move(r2); + VERIFY( r2.get().moved == false ); + VERIFY( rr2.get().moved == false ); + } + + { + struct ThrowingCopy + { + ThrowingCopy(int) { } + ThrowingCopy(const ThrowingCopy&) { throw 1; } + }; + + int dcounter = 0; + { + auto d = [&dcounter] (const auto&) { ++dcounter; }; + unique_resource<ThrowingCopy, decltype(d)> r(1, d); + try { + auto rr = std::move(r); // Ownership of resource left with 'r' + VERIFY(false); + } catch (int) { + VERIFY( dcounter == 0 ); + } + } + VERIFY( dcounter == 1 ); + } +} + +int called1 = 0; + +void +test_assign() +{ + struct ThrowingDel + { + ThrowingDel() = default; + ThrowingDel(int& called) : called(called) { } + ThrowingDel(const ThrowingDel&) = default; + ThrowingDel& operator=(const ThrowingDel&) { throw 1; } + + void operator()(int i) const noexcept { ++called; } + int& called = called1; + }; + + int called2 = 0; + { + unique_resource<int, ThrowingDel> r1; + VERIFY( r1.get() == 0 ); + unique_resource<int, ThrowingDel> r2(2, ThrowingDel{called2}); + VERIFY( r2.get() == 2 ); + try + { + r1 = std::move(r2); + VERIFY( false ); + } + catch (int) + { + } + VERIFY( called1 == 0 ); // r1.reset() was called, but did nothing. + VERIFY( called2 == 0 ); // r2.reset() not called. + VERIFY( r1.get() == 0 ); + VERIFY( r2.get() == 2 ); + } + VERIFY( called1 == 0 ); // r1.reset() was called, but did nothing. + VERIFY( called2 == 1 ); // r2 destructor invoked its deleter. +} + +void +test_modifiers() +{ + int dcounter = 0; + auto d = [&dcounter] (int i) { dcounter += i; }; + unique_resource<int, decltype(d)> r(1, d); + r.reset(); + VERIFY( dcounter == 1 ); + r.reset(2); + VERIFY( dcounter == 1 ); + r.release(); + VERIFY( dcounter == 1 ); + r.release(); + VERIFY( dcounter == 1 ); + r.reset(3); + VERIFY( dcounter == 1 ); + r.reset(4); + VERIFY( dcounter == 4 ); +} + +template<typename T> concept has_star = requires (T& t) { *t; }; +template<typename T> concept has_arrow = requires (T& t) { t.operator->(); }; + +void +test_observers() +{ + struct D { void operator()(int* p) const noexcept { delete p; } }; + int* p = new int(3); + unique_resource<int*, D> r(p, D{}); + VERIFY( r.get() == p ); + VERIFY( *r == 3 ); + VERIFY( r.operator->() == p ); + (void) r.get_deleter(); + + using R1 = unique_resource<int, void(*)(int)>; + static_assert( ! has_star<R1> ); + static_assert( ! has_arrow<R1> ); + using R2 = unique_resource<const void*, void(*)(const void*)>; + static_assert( ! has_star<R2> ); + static_assert( has_arrow<R2> ); +} + +void +test_make_checked() +{ + struct Boolish { + explicit operator bool() const noexcept { return val; } + bool val; + }; + + using std::experimental::make_unique_resource_checked; + + { + struct ThrowingCopy + { + ThrowingCopy(int i) : val(i) { } + ThrowingCopy(const ThrowingCopy&) { throw 1; } + Boolish operator==(int i) const noexcept { return {i == val}; } + int val; + }; + + int dcounter = 0; + auto d = [&dcounter] (const auto&) { ++dcounter; }; + + try + { + (void) make_unique_resource_checked(ThrowingCopy(1), 0, d); + VERIFY(false); + } + catch (int) + { + VERIFY(dcounter == 1); + } + + dcounter = 0; + try + { + (void) make_unique_resource_checked(ThrowingCopy(1), 1, d); + VERIFY(false); + } + catch (int) + { + VERIFY(dcounter == 0); + } + } +} + +int main() +{ + test_default_cons(); + test_cons(); + test_move_cons(); + test_assign(); + test_modifiers(); + test_observers(); + test_make_checked(); +} diff --git a/lto-plugin/ChangeLog b/lto-plugin/ChangeLog index eb37870..23cee8d 100644 --- a/lto-plugin/ChangeLog +++ b/lto-plugin/ChangeLog @@ -1,3 +1,9 @@ +2022-08-01 Martin Liska <mliska@suse.cz> + + PR lto/106170 + * configure.ac: Replace $target with $host. + * configure: Regenerate. + 2022-07-14 Martin Liska <mliska@suse.cz> PR bootstrap/106156 diff --git a/lto-plugin/configure b/lto-plugin/configure index fdb8a59..f165eae 100755 --- a/lto-plugin/configure +++ b/lto-plugin/configure @@ -6015,7 +6015,7 @@ fi use_locking=no ac_lto_plugin_extra_ldflags= -case $target in +case $host in riscv*) # do not use locking as pthread depends on libatomic ;; diff --git a/lto-plugin/configure.ac b/lto-plugin/configure.ac index e69a7bd..0a72027 100644 --- a/lto-plugin/configure.ac +++ b/lto-plugin/configure.ac @@ -91,7 +91,7 @@ AM_CONDITIONAL(LTO_PLUGIN_USE_SYMVER_SUN, [test "x$lto_plugin_use_symver" = xsun use_locking=no ac_lto_plugin_extra_ldflags= -case $target in +case $host in riscv*) # do not use locking as pthread depends on libatomic ;; |