diff options
Diffstat (limited to 'gcc/d')
240 files changed, 24460 insertions, 18084 deletions
diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog index e1bfdde..b6815d3 100644 --- a/gcc/d/ChangeLog +++ b/gcc/d/ChangeLog @@ -1,227 +1,432 @@ -2024-07-21 Sam James <sam@gentoo.org> +2025-04-29 Iain Buclaw <ibuclaw@gdcproject.org> - * Make-lang.in (WARN_DFLAGS): Drop NOCOMMON_FLAG. + PR d/103044 + * d-tree.h (build_clear_padding_call): New prototype. + * d-codegen.cc (build_clear_padding_call): New function. + (build_memset_call): Remove generated call to __builtin_memcpy. + (build_address): Replace generated call to __builtin_memset with + __builtin_clear_padding. + (build_array_from_exprs): Likewise. + * expr.cc (ExprVisitor::visit (AssignExp *)): Remove generated call to + __builtin_memset. + (ExprVisitor::visit (ArrayLiteralExp *)): Likewise. Insert call to + __builtin_clear_padding after copying array into GC memory. + (ExprVisitor::visit (StructLiteralExp *)): Remove generated call to + __builtin_memset. + * toir.cc (IRVisitor::visit (ReturnStatement *)): Likewise. -2024-06-05 Kewen Lin <linkw@linux.ibm.com> - Iain Buclaw <ibuclaw@gdcproject.org> +2025-04-17 Iain Buclaw <ibuclaw@gdcproject.org> - * d-target.cc (Target::_init): Use int_size_in_bytes of - long_double_type_node to replace the expression with - LONG_DOUBLE_TYPE_SIZE for c.long_doublesize assignment. + * dmd/MERGE: Merge upstream dmd 956e73d64e. -2024-04-19 Iain Buclaw <ibuclaw@gdcproject.org> +2025-04-15 Iain Buclaw <ibuclaw@gdcproject.org> - PR d/111650 - * decl.cc (get_fndecl_arguments): Move generation of frame type to ... - (DeclVisitor::visit (FuncDeclaration *)): ... here, after the call to - build_closure. + PR d/119826 + * types.cc (TypeVisitor::visit (TypeEnum *)): Propagate flags of main + enum types to all forward-referenced variants. -2024-04-06 Iain Buclaw <ibuclaw@gdcproject.org> +2025-04-15 Iain Buclaw <ibuclaw@gdcproject.org> - * dmd/MERGE: Merge upstream dmd b65767825f. - * dmd/VERSION: Bump version to v2.108.0. + PR d/119799 + * decl.cc (DeclVisitor::visit (VarDeclaration *)): Check front-end + type size before building the VAR_DECL. Allow C symbols to have a + size of `0'. -2024-03-17 Iain Buclaw <ibuclaw@gdcproject.org> +2025-04-15 Iain Buclaw <ibuclaw@gdcproject.org> - * dmd/MERGE: Merge upstream dmd 855353a1d9. - * dmd/VERSION: + PR d/119817 + * imports.cc (ImportVisitor::visit (OverloadSet *)): Don't push + NULL_TREE to vector of import symbols. -2024-03-10 Iain Buclaw <ibuclaw@gdcproject.org> +2025-04-12 Iain Buclaw <ibuclaw@gdcproject.org> - PR d/112285 - PR d/112290 - * d-target.cc (Target::preferPassByRef): Return true for all static - array and struct types. + PR d/109023 + * d-compiler.cc: Include dmd/errors.h. + (Compiler::onImport): Implement. + * d-lang.cc (d_handle_option): Handle -finclude-imports. + (d_parse_file): Run semantic on included imports. + * gdc.texi: Document -finclude-imports. + * lang.opt: Add finclude-imports. + * lang.opt.urls: Regenerate. -2024-03-03 Iain Buclaw <ibuclaw@gdcproject.org> +2025-04-12 Iain Buclaw <ibuclaw@gdcproject.org> - * dmd/MERGE: Merge upstream dmd f8bae04558. - * dmd/VERSION: Bump version to v2.108.0-beta.1. + PR d/119758 + * d-lang.cc (d_parse_file): Use endswith in test for -fonly= argument. + * d-spec.cc (lang_specific_driver): Rework -fonly= and pass all input + files to the front-end compiler when the option is seen. + +2025-04-11 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 1b34fea478. + +2025-04-09 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/118309 + * modules.cc: Include debug.h + (d_finish_compilation): Call debug_hooks->type_decl on all TYPE_DECLs. + * types.cc: Remove toplev.h include. + (finish_aggregate_type): Don't call rest_of_type_compilation or + rest_of_decl_compilation on type. + (TypeVisitor::visit (TypeEnum *)): Likewise. + +2025-04-09 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/117832 + * d-tree.h (build_padded_constructor): New prototype. + * d-codegen.cc (build_padded_constructor): New function. + (d_array_value): Call it. + (build_memset_call): Likewise. + (build_struct_literal): Likewise. + (underlying_complex_expr): Likewise. + (build_array_from_val): Likewise. + (build_array_from_exprs): Likewise. + (d_build_call): Likewise. + (get_frame_for_symbol): Likewise. + * d-convert.cc (convert_for_rvalue): Likewise. + (convert_for_assignment): Likewise. + * decl.cc (class DeclVisitor): Likewise. + * expr.cc (class ExprVisitor): Likewise. + * modules.cc (layout_moduleinfo): Likewise. + * typeinfo.cc (class TypeInfoVisitor): Likewise. + +2025-04-08 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 51816cd01d. + +2025-04-06 Sandra Loosemore <sloosemore@baylibre.com> + + * lang.opt.urls: Regenerate. + +2025-04-02 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd ed17b3e95d. + +2025-03-31 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd c6863be720. + * dmd/VERSION: Bump version to v2.111.0. + +2025-03-31 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/117002 + * decl.cc (aggregate_initializer_decl): Set explicit decl alignment of + class instance. + * expr.cc (ExprVisitor::visit (NewExp *)): Likewise. + * types.cc (TypeVisitor::visit (TypeClass *)): Mark the record type of + classes as packed. + +2025-03-30 Sandra Loosemore <sloosemore@baylibre.com> + + * lang.opt.urls: Regenerate. + +2025-03-26 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 02a64d2e13. + +2025-03-23 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/117621 + * types.cc (finish_aggregate_type): Propagate TYPE_PACKED to variants. + +2025-03-22 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 032e24446b. + * dmd/VERSION: Bump version to v2.111.0-rc.1. + +2025-03-22 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 9d2f034398. + +2025-03-22 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 94950cae58. + * d-lang.cc (d_handle_option): Add case for CppStdRevisionCpp23. + * gdc.texi: Document -fextern-std=c++23. + * lang.opt (fextern-std=): Add c++23. + +2025-03-22 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 8db14cf846. + +2025-03-22 Iain Buclaw <ibuclaw@gdcproject.org> + + * runtime.def (INVARIANT): Update signature of run-time function. + +2025-03-20 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/118545 + * d-lang.cc (d_handle_option): Adjust quoted options. + +2025-03-18 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd fde0f8c40a. + +2025-03-18 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 51be8bb729. + +2025-03-16 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 603225372b. + * dmd/VERSION: Bump version to v2.111.0-beta.1. * d-builtins.cc (build_frontend_type): Update for new front-end interface. - * d-codegen.cc (build_assert_call): Likewise. - * d-convert.cc (d_array_convert): Likewise. - * decl.cc (get_vtable_decl): Likewise. - * expr.cc (ExprVisitor::visit (EqualExp *)): Likewise. - (ExprVisitor::visit (VarExp *)): Likewise. - (ExprVisitor::visit (ArrayLiteralExp *)): Likewise. - (ExprVisitor::visit (AssocArrayLiteralExp)): Likewise. - * intrinsics.cc (build_shuffle_mask_type): Likewise. - (maybe_warn_intrinsic_mismatch): Likewise. - * runtime.cc (get_libcall_type): Likewise. - * typeinfo.cc (TypeInfoVisitor::layout_string): Likewise. - (TypeInfoVisitor::visit(TypeInfoTupleDeclaration *)): Likewise. - -2024-03-03 Iain Buclaw <ibuclaw@gdcproject.org> - - PR d/114171 - * d-codegen.cc (lower_struct_comparison): Keep alignment of original - type in reinterpret cast for comparison. - -2024-02-25 Iain Buclaw <ibuclaw@gdcproject.org> - - * dmd/MERGE: Merge upstream dmd ceff48bf7d. - -2024-02-17 Iain Buclaw <ibuclaw@gdcproject.org> - - * dmd/MERGE: Merge upstream dmd 9471b25db9. - * dmd/VERSION: Bump version to v2.107.1-rc.1. - * Make-lang.in (D_FRONTEND_OBJS): Add d/cxxfrontend.o. - * d-attribs.cc (build_attributes): Update for new front-end interface. - * d-builtins.cc (build_frontend_type): Likewise. - (strip_type_modifiers): Likewise. - (covariant_with_builtin_type_p): Likewise. - * d-codegen.cc (declaration_type): Likewise. - (parameter_type): Likewise. - (build_array_struct_comparison): Likewise. - (void_okay_p): Likewise. - * d-convert.cc (convert_expr): Likewise. - (check_valist_conversion): Likewise. - * d-lang.cc (d_generate_ddoc_file): Likewise. - (d_parse_file): Likewise. - * d-target.cc (TargetCPP::toMangle): Likewise. - (TargetCPP::typeInfoMangle): Likewise. - (TargetCPP::thunkMangle): Likewise. - (TargetCPP::parameterType): Likewise. - * decl.cc (d_mangle_decl): Likewise. - (DeclVisitor::visit): Likewise. - (DeclVisitor::visit (CAsmDeclaration *)): New method. - (get_symbol_decl): Update for new front-end interface. + * decl.cc (Class DeclVisitor): Likewise. + (maybe_build_decl_tree): Likewise. + (get_vtable_decl): Likewise. (layout_class_initializer): Likewise. - * expr.cc (ExprVisitor::visit): Likewise. - * intrinsics.cc (maybe_set_intrinsic): Likewise. - (expand_intrinsic_rotate): Likewise. - * modules.cc (layout_moduleinfo_fields): Likewise. - (layout_moduleinfo): Likewise. - * runtime.cc (get_libcall_type): Likewise. - * typeinfo.cc (make_frontend_typeinfo): Likewise. - (TypeInfoVisitor::visit): Likewise. - (create_typeinfo): Likewise. - * types.cc (same_type_p): Likewise. - (build_ctype): Likewise. - -2024-02-12 Iain Buclaw <ibuclaw@gdcproject.org> - - PR d/113125 - * types.cc (TypeVisitor::visit (TypeStruct *)): Generate TYPE_DECL and - apply UDAs to opaque struct declarations. - -2024-02-12 Iain Buclaw <ibuclaw@gdcproject.org> - - PR d/113772 - * dmd/MERGE: Merge upstream dmd 11240a9663. - * d-builtins.cc (build_frontend_type): Update for new front-end + * expr.cc (class ExprVisitor): Likewise. + (ExprVisitor::visit (NewExp *)): Implement placement new for class, + struct, and pointer types. + * modules.cc (get_internal_fn): Update for new front-end interface. + +2025-03-16 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 53a1cc8d13. + * d-tree.h (create_typeinfo): Change second parameter to Scope *. + (speculative_type_p): Remove prototype. + * d-frontend.cc (getTypeInfoType): Adjust. + * decl.cc: Include dmd/typinf.h. + (DeclVisitor::visit (TypeInfoDeclaration *)): Update for new front-end interface. - * types.cc (same_type_p): Likewise. + * typeinfo.cc (create_typeinfo): Likewise. + (class SpeculativeTypeVisitor): Remove class. + (speculative_type_p): Remove function. + +2025-03-16 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd ffbad272b6. + * d-tree.h (make_location_t): Add overload taking a const SourceLoc &. + * d-codegen.cc (make_location_t): Likewise. + * d-diagnostic.cc (d_diagnostic_report_diagnostic): Change first + parameter type to const SourceLoc &. + (verrorReport): Update for new front-end interface. + (verrorReportSupplemental): Likewise. + * d-frontend.cc (eval_builtin): Likewise. + (getTypeInfoType): Likewise. + * d-lang.cc (d_parse_file): Likewise. + * d-target.cc (Target::va_listType): Likewise. + (Target::getTargetInfo): Likewise. + * decl.cc (build_decl_tree): Likewise. + * imports.cc (ImportVisitor::visit (Module *)): Likewise. + * modules.cc (get_internal_fn): Likewise. -2024-02-12 Iain Buclaw <ibuclaw@gdcproject.org> +2025-03-15 Iain Buclaw <ibuclaw@gdcproject.org> - PR d/113758 - * d-codegen.cc (d_build_call): Force a TARGET_EXPR when callee - destorys its arguments. - * decl.cc (DeclVisitor::visit (VarDeclaration *)): Set - SET_DECL_VALUE_EXPR on the temporary variable to make it a placeholder - for the TARGET_EXPR_SLOT. + * dmd/MERGE: Merge upstream dmd d29e3eca45. + * d-codegen.cc (can_elide_copy_p): Update for new front-end interface. + * d-lang.cc (d_handle_option): Likewise. + * expr.cc (class ExprVisitor): Likewise. -2024-02-04 Iain Buclaw <ibuclaw@gdcproject.org> +2025-03-15 Iain Buclaw <ibuclaw@gdcproject.org> - * dmd/MERGE: Merge upstream dmd a6f1083699. - * dmd/VERSION: Bump version to v2.107.0 - * Make-lang.in (D_FRONTEND_OBJS): Add d/pragmasem.o. - * d-builtins.cc (strip_type_modifiers): Update for new front-end - interface. - * d-codegen.cc (declaration_type): Likewise. - (parameter_type): Likewise. - * d-target.cc (TargetCPP::parameterType): Likewise. - * expr.cc (ExprVisitor::visit (IndexExp *)): Likewise. - (ExprVisitor::visit (VarExp *)): Likewise. - (ExprVisitor::visit (AssocArrayLiteralExp *)): Likewise. - * runtime.cc (get_libcall_type): Likewise. - * typeinfo.cc (TypeInfoVisitor::visit (TypeInfoConstDeclaration *)): - Likewise. - (TypeInfoVisitor::visit (TypeInfoInvariantDeclaration *)): Likewise. - (TypeInfoVisitor::visit (TypeInfoSharedDeclaration *)): Likewise. - (TypeInfoVisitor::visit (TypeInfoWildDeclaration *)): Likewise. - * types.cc (build_ctype): Likewise. - -2024-02-03 Iain Buclaw <ibuclaw@gdcproject.org> - - * dmd/MERGE: Merge upstream dmd e770945277. - * Make-lang.in (D_FRONTEND_OBJS): Add d/basicmangle.o, d/enumsem.o, - d/funcsem.o, d/templatesem.o. - * d-builtins.cc (build_frontend_type): Update for new front-end + * dmd/MERGE: Merge upstream b7e3b3b617. + +2025-03-11 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/119139 + * decl.cc (get_symbol_decl): Don't set TREE_READONLY for __result + declarations. + +2025-02-28 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/116961 + * d-codegen.cc (build_float_cst): Change new_value type from real_t to + real_value. + * d-ctfloat.cc (CTFloat::fabs): Default initialize the return value. + (CTFloat::ldexp): Likewise. + (CTFloat::parse): Likewise. + * d-longdouble.cc (longdouble::add): Likewise. + (longdouble::sub): Likewise. + (longdouble::mul): Likewise. + (longdouble::div): Likewise. + (longdouble::mod): Likewise. + (longdouble::neg): Likewise. + * d-port.cc (Port::isFloat32LiteralOutOfRange): Likewise. + (Port::isFloat64LiteralOutOfRange): Likewise. + +2025-02-25 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/118654 + * implement-d.texi: Document CET version and traits key. + +2025-02-25 Iain Buclaw <ibuclaw@gdcproject.org> + + * Make-lang.in (check_gdc_parallelize): Increase to 128. + +2025-01-29 Arsen Arsenović <arsen@aarsen.me> + Jakub Jelinek <jakub@redhat.com> + + PR d/118477 + * Make-lang.in (DCOMPILE, DPOSTCOMPILE): Use $(basename $(@F)) + instead of $(*F). + +2025-01-22 Arsen Arsenović <arsen@aarsen.me> + + * lang-specs.h: Replace %{nostdinc*} with %{nostdinc}. + +2025-01-20 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/114434 + * expr.cc (ExprVisitor::visit (PtrExp *)): Get the offset as a + dinteger_t rather than a size_t. + (ExprVisitor::visit (SymOffExp *)): Likewise. + +2025-01-18 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd d115713410. + +2025-01-16 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/115249 + * typeinfo.cc (create_tinfo_types): Update internal Typenfo + representation. + (TypeInfoVisitor::visit (TypeInfoClassDeclaration *)): Likewise. + +2025-01-14 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/118438 + PR d/118448 + PR d/118449 + * dmd/MERGE: Merge upstream dmd d6f693b46a. + * d-incpath.cc (add_import_paths): Update for new front-end interface. + +2025-01-12 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd c7902293d7. + * dmd/VERSION: Bump version to v2.110.0-rc.1. + +2025-01-12 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd c57da0cf59. + * d-codegen.cc (can_elide_copy_p): New. + (d_build_call): Use it. + * d-lang.cc (d_post_options): Update for new front-end interface. + +2025-01-11 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 82a5d2a7c4. + * d-lang.cc (d_handle_option): Handle new option `-fpreview=safer'. + * expr.cc (ExprVisitor::NewExp): Remove gcc_unreachable for the + generation of `_d_newThrowable'. + * lang.opt: Add -fpreview=safer. + +2025-01-11 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 2b89c2909d. + * Make-lang.in (D_FRONTEND_OBJS): Rename d/basicmangle.o to + d/mangle-basic.o, d/cppmangle.o to d/mangle-cpp.o, and d/dmangle.o to + d/mangle-package.o. + (d/mangle-%.o): New rule. + * d-builtins.cc (maybe_set_builtin_1): Update for new front-end interface. - * d-codegen.cc (declaration_type): Likewise. - (parameter_type): Likewise. - * d-incpath.cc (add_globalpaths): Likewise. - (add_filepaths): Likewise. - (add_import_paths): Likewise. + * d-diagnostic.cc (verrorReport): Likewise. + (verrorReportSupplemental): Likewise. + * d-frontend.cc (getTypeInfoType): Likewise. * d-lang.cc (d_init_options): Likewise. (d_handle_option): Likewise. - (d_parse_file): Likewise. - * decl.cc (DeclVisitor::finish_vtable): Likewise. - (DeclVisitor::visit (FuncDeclaration *)): Likewise. - (get_symbol_decl): Likewise. - * expr.cc (ExprVisitor::visit (StringExp *)): Likewise. - Implement support for 8-byte hexadecimal strings. - * typeinfo.cc (create_tinfo_types): Update internal TypeInfo - representation. - (TypeInfoVisitor::visit (TypeInfoConstDeclaration *)): Update for new - front-end interface. - (TypeInfoVisitor::visit (TypeInfoInvariantDeclaration *)): Likewise. - (TypeInfoVisitor::visit (TypeInfoSharedDeclaration *)): Likewise. - (TypeInfoVisitor::visit (TypeInfoWildDeclaration *)): Likewise. - (TypeInfoVisitor::visit (TypeInfoClassDeclaration *)): Move data for - TypeInfo_Class.nameSig to the end of the object. - (create_typeinfo): Update for new front-end interface. - -2024-02-02 Iain Buclaw <ibuclaw@gdcproject.org> - - * dmd/MERGE: Merge upstream dmd bce5c1f7b5. - * d-attribs.cc (build_attributes): Update for new front-end interface. - * d-lang.cc (d_parse_file): Likewise. - * decl.cc (DeclVisitor::visit (VarDeclaration *)): Likewise. - * expr.cc (build_lambda_tree): New function. - (ExprVisitor::visit (FuncExp *)): Use build_lambda_tree. - (ExprVisitor::visit (SymOffExp *)): Likewise. - (ExprVisitor::visit (VarExp *)): Likewise. - * typeinfo.cc (create_tinfo_types): Add two ulong fields to internal - TypeInfo representation. - (TypeInfoVisitor::visit (TypeInfoClassDeclaration *)): Emit stub data - for TypeInfo_Class.nameSig. - (TypeInfoVisitor::visit (TypeInfoStructDeclaration *)): Update for new - front-end interface. - -2024-02-02 Iain Buclaw <ibuclaw@gdcproject.org> - - * dmd/MERGE: Merge upstream dmd d8e3976a58. - * dmd/VERSION: Bump version to v2.107.0-beta.1. - * d-lang.cc (d_parse_file): Update for new front-end interface. - * modules.cc (struct module_info): Add standalonectors. - (build_module_tree): Implement @standalone. - (register_module_decl): Likewise. - -2024-02-02 Iain Buclaw <ibuclaw@gdcproject.org> - - * dmd/MERGE: Merge upstream dmd f1a045928e. - * dmd/VERSION: Bump version to v2.106.1-rc.1. - * gdc.texi (fignore-unknown-pragmas): Update documentation. - * d-builtins.cc (covariant_with_builtin_type_p): Update for new - front-end interface. - * d-lang.cc (d_parse_file): Likewise. - * typeinfo.cc (make_frontend_typeinfo): Likewise. + (d_post_options): Likewise. + * d-target.cc (TargetC::contributesToAggregateAlignment): New. + * d-tree.h (create_typeinfo): Adjust prototype. + * decl.cc (layout_struct_initializer): Update for new front-end + interface. + * typeinfo.cc (create_typeinfo): Remove generate parameter. + * types.cc (layout_aggregate_members): Update for new front-end + interface. + +2025-01-10 Iain Buclaw <ibuclaw@gdcproject.org> -2024-01-04 David Malcolm <dmalcolm@redhat.com> + * dmd/MERGE: Merge upstream dmd 4ccb01fde5. + * Make-lang.in (D_FRONTEND_OBJS): Rename d/foreachvar.o to + d/visitor-foreachvar.o, d/visitor.o to d/visitor-package.o, and + d/statement_rewrite_walker.o to d/visitor-statement_rewrite_walker.o. + (D_FRONTEND_OBJS): Rename + d/{parsetime,permissive,postorder,transitive}visitor.o to + d/visitor-{parsetime,permissive,postorder,transitive}.o. + (D_FRONTEND_OBJS): Remove d/sapply.o. + (d.tags): Add dmd/common/*.h. + (d/visitor-%.o:): New rule. + * d-codegen.cc (get_frameinfo): Update for new front-end interface. - * lang.opt.urls: New file, autogenerated by - regenerate-opt-urls.py. +2025-01-10 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 6884b433d2. + * d-builtins.cc (build_frontend_type): Update for new front-end + interface. + (d_build_builtins_module): Likewise. + (matches_builtin_type): Likewise. + (covariant_with_builtin_type_p): Likewise. + * d-codegen.cc (lower_struct_comparison): Likewise. + (call_side_effect_free_p): Likewise. + * d-compiler.cc (Compiler::paintAsType): Likewise. + * d-convert.cc (convert_expr): Likewise. + (convert_for_assignment): Likewise. + * d-target.cc (Target::isVectorTypeSupported): Likewise. + (Target::isVectorOpSupported): Likewise. + (Target::isReturnOnStack): Likewise. + * decl.cc (get_symbol_decl): Likewise. + * expr.cc (build_return_dtor): Likewise. + * imports.cc (class ImportVisitor): Likewise. + * toir.cc (class IRVisitor): Likewise. + * types.cc (class TypeVisitor): Likewise. + +2025-01-10 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 34875cd6e1. + * dmd/VERSION: Bump version to v2.110.0-beta.1. + * Make-lang.in (D_FRONTEND_OBJS): Add d/deps.o, d/timetrace.o. + * decl.cc (class DeclVisitor): Update for new front-end interface. + * expr.cc (class ExprVisitor): Likewise + * typeinfo.cc (check_typeinfo_type): Likewise. + +2025-01-05 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 66b93fc24a. + * dmd/VERSION: Bump version to v2.109.1. + * d-builtins.cc (build_frontend_type): Update for new front-end + interface. + (matches_builtin_type): Likewise. + * d-codegen.cc (identity_compare_p): Likewise. + (call_side_effect_free_p): Likewise. + * d-convert.cc (convert_expr): Likewise. + (check_valist_conversion): Likewise. + * d-lang.cc (d_types_compatible_p): Likewise. + * d-target.cc (Target::isVectorTypeSupported): Likewise. + (Target::isReturnOnStack): Likewise. + (Target::preferPassByRef): Likewise. + * decl.cc (class DeclVisitor): Likewise. + * expr.cc (class ExprVisitor): Likewise. + * typeinfo.cc (class TypeInfoVisitor): Likewise. + * types.cc (class TypeVisitor): Likewise. + +2025-01-05 Iain Buclaw <ibuclaw@gdcproject.org> + + * decl.cc (DeclVisitor::finish_vtable): Update for new front-end + interface. + * dmd/MERGE: Merge upstream dmd 07bc5b9b3c. + * dmd/VERSION: Bump version to v2.109.0. + +2025-01-05 Iain Buclaw <ibuclaw@symmetryinvestments.com> + + * Make-lang.in (D_FRONTEND_OBJS): Add d/attribsem.o, + d/common-charactertables.o, d/common-identifiertables.o. + * d-attribs.cc (apply_user_attributes): Update for new front-end + interface. + * d-builtins.cc (d_init_versions): Predefine CppRuntime_GNU. + * d-incpath.cc (add_globalpaths): Update for new front-end interface. + (add_filepaths): Likewise. + (add_import_paths): Likewise. + * d-lang.cc (d_post_options): Likewise. + * dmd/MERGE: Merge upstream dmd c11e1d1708. + * dmd/VERSION: Bump version to v2.108.1. -2024-01-03 Jakub Jelinek <jakub@redhat.com> +2025-01-02 Jakub Jelinek <jakub@redhat.com> * gdc.texi: Bump @copyrights-d year. -Copyright (C) 2024 Free Software Foundation, Inc. +Copyright (C) 2025 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright diff --git a/gcc/d/ChangeLog-2006 b/gcc/d/ChangeLog-2006 index 4160b0f..f5c686b 100644 --- a/gcc/d/ChangeLog-2006 +++ b/gcc/d/ChangeLog-2006 @@ -945,7 +945,6 @@ 2006-06-01 David Friedman <dvdfrdmn@users.sf.net> * Start of SourceForge repository - Copyright (C) 2006 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2007 b/gcc/d/ChangeLog-2007 index a2e5043..e95a195 100644 --- a/gcc/d/ChangeLog-2007 +++ b/gcc/d/ChangeLog-2007 @@ -1331,7 +1331,6 @@ * d-codegen.cc (convertTo): Use 64-bit for Tarray, Tsarray conversion. * d-codegen.{h, cc} (darrayVal): use uinteger_t arg - Copyright (C) 2007 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2008 b/gcc/d/ChangeLog-2008 index 23dc712..fa0c585 100644 --- a/gcc/d/ChangeLog-2008 +++ b/gcc/d/ChangeLog-2008 @@ -322,7 +322,6 @@ outer class functions. * Rename patch-build_gcc-4.0 to patch-build_gcc-4.0.x - Copyright (C) 2008 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2009 b/gcc/d/ChangeLog-2009 index 90a204d..057ae98 100644 --- a/gcc/d/ChangeLog-2009 +++ b/gcc/d/ChangeLog-2009 @@ -176,7 +176,6 @@ * d-glue.cc, d-objfile.cc, d-codegen.cc, d-lang.h, d-builtins2.cc, d-convert.cc, d-codegen.h: Replace calls to build macro by appropriate buildN function (build is removed in GCC > 4.1). - Copyright (C) 2009 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2010 b/gcc/d/ChangeLog-2010 index 40c8fa4..f430aebe 100644 --- a/gcc/d/ChangeLog-2010 +++ b/gcc/d/ChangeLog-2010 @@ -1475,7 +1475,6 @@ * phobos/std/string.d: Fix a set of bugs in std.string.split which made delemiters of length > 1 segfault. - Copyright (C) 2010 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2011 b/gcc/d/ChangeLog-2011 index a5c1558..e4fce46 100644 --- a/gcc/d/ChangeLog-2011 +++ b/gcc/d/ChangeLog-2011 @@ -1239,7 +1239,6 @@ d/d-glue.cc, d/d-lang-45.h, d/d-lang.cc, d/d-lang.h, d/d-objfile.cc: Declare d_build_decl as extern "C". Add function d_build_decl_loc. [29253025adb2] - Copyright (C) 2011 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2012 b/gcc/d/ChangeLog-2012 index 741747b..443c025 100644 --- a/gcc/d/ChangeLog-2012 +++ b/gcc/d/ChangeLog-2012 @@ -848,7 +848,6 @@ (ReturnStatement::toIR): Don't call postblit on nrvo returns. (DtorExpStatement::toIR): Don't call destructor if var is returned as the nrvo variable. - Copyright (C) 2012 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2013 b/gcc/d/ChangeLog-2013 index eeb12c9..c863a30 100644 --- a/gcc/d/ChangeLog-2013 +++ b/gcc/d/ChangeLog-2013 @@ -1212,7 +1212,6 @@ (d_init): Fix definition of D_LP64 version. * setup-gcc.sh: Likewise. * target-ver-syms.sh: Likewise. - Copyright (C) 2013 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2014 b/gcc/d/ChangeLog-2014 index cf8c8ac..fded72b 100644 --- a/gcc/d/ChangeLog-2014 +++ b/gcc/d/ChangeLog-2014 @@ -651,7 +651,6 @@ (d_finish_function): Set function local if function body was compiled. * d-decls.cc (Dsymbol::toSymbolX): Use unsigned integer format for the prefix string length. - Copyright (C) 2014 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2015 b/gcc/d/ChangeLog-2015 index 918068b..8e6a3bd 100644 --- a/gcc/d/ChangeLog-2015 +++ b/gcc/d/ChangeLog-2015 @@ -762,7 +762,6 @@ 2015-01-02 Iain Buclaw <ibuclaw@gdcproject.org> * d-codegen.h (build_boolop): Don't eagerly fold comparison expressions. - Copyright (C) 2015 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2016 b/gcc/d/ChangeLog-2016 index dbd7573..9db9890 100644 --- a/gcc/d/ChangeLog-2016 +++ b/gcc/d/ChangeLog-2016 @@ -1253,7 +1253,6 @@ function attributes. * d-codegen.h (LibCallFlag): Remove type. * runtime.def: Replace LibCallFlag with ECF everywhere. - Copyright (C) 2016 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2017 b/gcc/d/ChangeLog-2017 index 4f64c31..be37a8d 100644 --- a/gcc/d/ChangeLog-2017 +++ b/gcc/d/ChangeLog-2017 @@ -1166,7 +1166,6 @@ (ExprVisitor::lvalue_p): New function. (ExprVisitor::visit(AssignExp)): Check for dtor in array assignments. (ExprVisitor::visit(TypeidExp)): Cast result to expression type. - Copyright (C) 2017 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2022 b/gcc/d/ChangeLog-2022 index 7630f24..48279d7 100644 --- a/gcc/d/ChangeLog-2022 +++ b/gcc/d/ChangeLog-2022 @@ -824,7 +824,6 @@ 2022-01-03 Jakub Jelinek <jakub@redhat.com> * gdc.texi: Bump @copyrights-d year. - Copyright (C) 2022 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2023 b/gcc/d/ChangeLog-2023 index 5510b99..2cf8a33 100644 --- a/gcc/d/ChangeLog-2023 +++ b/gcc/d/ChangeLog-2023 @@ -484,7 +484,6 @@ 2023-01-02 Jakub Jelinek <jakub@redhat.com> * gdc.texi: Bump @copyrights-d year. - Copyright (C) 2023 Free Software Foundation, Inc. diff --git a/gcc/d/ChangeLog-2024 b/gcc/d/ChangeLog-2024 new file mode 100644 index 0000000..a2d9cf4 --- /dev/null +++ b/gcc/d/ChangeLog-2024 @@ -0,0 +1,294 @@ +2024-11-22 Andrew Pinski <quic_apinski@quicinc.com> + + PR bootstrap/117737 + * d-attribs.cc (INCLUDE_MEMORY): Remove. + * d-builtins.cc (INCLUDE_MEMORY): Remove. + * d-codegen.cc (INCLUDE_MEMORY): Remove. + * d-convert.cc (INCLUDE_MEMORY): Remove. + * d-diagnostic.cc (INCLUDE_MEMORY): Remove. + * d-frontend.cc (INCLUDE_MEMORY): Remove. + * d-lang.cc (INCLUDE_MEMORY): Remove. + * d-longdouble.cc (INCLUDE_MEMORY): Remove. + * d-target.cc (INCLUDE_MEMORY): Remove. + * decl.cc (INCLUDE_MEMORY): Remove. + * expr.cc (INCLUDE_MEMORY): Remove. + * intrinsics.cc (INCLUDE_MEMORY): Remove. + * modules.cc (INCLUDE_MEMORY): Remove. + * toir.cc (INCLUDE_MEMORY): Remove. + * typeinfo.cc (INCLUDE_MEMORY): Remove. + * types.cc (INCLUDE_MEMORY): Remove. + +2024-10-29 David Malcolm <dmalcolm@redhat.com> + + PR other/116613 + * d-diagnostic.cc (d_diagnostic_report_diagnostic): Update for + m_printer becoming reference printer. + +2024-10-24 David Malcolm <dmalcolm@redhat.com> + Gaius Mulley <gaiusmod2@gmail.com> + + PR other/116613 + * d-attribs.cc: Add #define INCLUDE_MEMORY. + * d-builtins.cc: Likewise. + * d-codegen.cc: Likewise. + * d-convert.cc: Likewise. + * d-diagnostic.cc: Likewise. + * d-frontend.cc: Likewise. + * d-lang.cc: Likewise. + * d-longdouble.cc: Likewise. + * d-target.cc: Likewise. + * decl.cc: Likewise. + * expr.cc: Likewise. + * intrinsics.cc: Likewise. + * modules.cc: Likewise. + * toir.cc: Likewise. + * typeinfo.cc: Likewise. + * types.cc: Likewise. + +2024-09-25 Mikael Morin <mikael@gcc.gnu.org> + + PR other/116801 + * lang.opt.urls: Regenerate. + +2024-09-09 David Malcolm <dmalcolm@redhat.com> + + * d-diagnostic.cc (d_diagnostic_report_diagnostic): Update for + renaming of diagnostic_info field. + +2024-09-09 David Malcolm <dmalcolm@redhat.com> + + PR other/116613 + * d-diagnostic.cc (d_diagnostic_report_diagnostic): Rename + diagnostic_context's "printer" field to "m_printer". + +2024-09-02 Richard Sandiford <richard.sandiford@arm.com> + + * toir.cc (IRVisitor): Rename ASM_INPUT_P to ASM_BASIC_P. + +2024-07-21 Sam James <sam@gentoo.org> + + * Make-lang.in (WARN_DFLAGS): Drop NOCOMMON_FLAG. + +2024-06-05 Kewen Lin <linkw@linux.ibm.com> + Iain Buclaw <ibuclaw@gdcproject.org> + + * d-target.cc (Target::_init): Use int_size_in_bytes of + long_double_type_node to replace the expression with + LONG_DOUBLE_TYPE_SIZE for c.long_doublesize assignment. + +2024-04-19 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/111650 + * decl.cc (get_fndecl_arguments): Move generation of frame type to ... + (DeclVisitor::visit (FuncDeclaration *)): ... here, after the call to + build_closure. + +2024-04-06 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd b65767825f. + * dmd/VERSION: Bump version to v2.108.0. + +2024-03-17 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 855353a1d9. + * dmd/VERSION: + +2024-03-10 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/112285 + PR d/112290 + * d-target.cc (Target::preferPassByRef): Return true for all static + array and struct types. + +2024-03-03 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd f8bae04558. + * dmd/VERSION: Bump version to v2.108.0-beta.1. + * d-builtins.cc (build_frontend_type): Update for new front-end + interface. + * d-codegen.cc (build_assert_call): Likewise. + * d-convert.cc (d_array_convert): Likewise. + * decl.cc (get_vtable_decl): Likewise. + * expr.cc (ExprVisitor::visit (EqualExp *)): Likewise. + (ExprVisitor::visit (VarExp *)): Likewise. + (ExprVisitor::visit (ArrayLiteralExp *)): Likewise. + (ExprVisitor::visit (AssocArrayLiteralExp)): Likewise. + * intrinsics.cc (build_shuffle_mask_type): Likewise. + (maybe_warn_intrinsic_mismatch): Likewise. + * runtime.cc (get_libcall_type): Likewise. + * typeinfo.cc (TypeInfoVisitor::layout_string): Likewise. + (TypeInfoVisitor::visit(TypeInfoTupleDeclaration *)): Likewise. + +2024-03-03 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/114171 + * d-codegen.cc (lower_struct_comparison): Keep alignment of original + type in reinterpret cast for comparison. + +2024-02-25 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd ceff48bf7d. + +2024-02-17 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd 9471b25db9. + * dmd/VERSION: Bump version to v2.107.1-rc.1. + * Make-lang.in (D_FRONTEND_OBJS): Add d/cxxfrontend.o. + * d-attribs.cc (build_attributes): Update for new front-end interface. + * d-builtins.cc (build_frontend_type): Likewise. + (strip_type_modifiers): Likewise. + (covariant_with_builtin_type_p): Likewise. + * d-codegen.cc (declaration_type): Likewise. + (parameter_type): Likewise. + (build_array_struct_comparison): Likewise. + (void_okay_p): Likewise. + * d-convert.cc (convert_expr): Likewise. + (check_valist_conversion): Likewise. + * d-lang.cc (d_generate_ddoc_file): Likewise. + (d_parse_file): Likewise. + * d-target.cc (TargetCPP::toMangle): Likewise. + (TargetCPP::typeInfoMangle): Likewise. + (TargetCPP::thunkMangle): Likewise. + (TargetCPP::parameterType): Likewise. + * decl.cc (d_mangle_decl): Likewise. + (DeclVisitor::visit): Likewise. + (DeclVisitor::visit (CAsmDeclaration *)): New method. + (get_symbol_decl): Update for new front-end interface. + (layout_class_initializer): Likewise. + * expr.cc (ExprVisitor::visit): Likewise. + * intrinsics.cc (maybe_set_intrinsic): Likewise. + (expand_intrinsic_rotate): Likewise. + * modules.cc (layout_moduleinfo_fields): Likewise. + (layout_moduleinfo): Likewise. + * runtime.cc (get_libcall_type): Likewise. + * typeinfo.cc (make_frontend_typeinfo): Likewise. + (TypeInfoVisitor::visit): Likewise. + (create_typeinfo): Likewise. + * types.cc (same_type_p): Likewise. + (build_ctype): Likewise. + +2024-02-12 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/113125 + * types.cc (TypeVisitor::visit (TypeStruct *)): Generate TYPE_DECL and + apply UDAs to opaque struct declarations. + +2024-02-12 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/113772 + * dmd/MERGE: Merge upstream dmd 11240a9663. + * d-builtins.cc (build_frontend_type): Update for new front-end + interface. + * types.cc (same_type_p): Likewise. + +2024-02-12 Iain Buclaw <ibuclaw@gdcproject.org> + + PR d/113758 + * d-codegen.cc (d_build_call): Force a TARGET_EXPR when callee + destorys its arguments. + * decl.cc (DeclVisitor::visit (VarDeclaration *)): Set + SET_DECL_VALUE_EXPR on the temporary variable to make it a placeholder + for the TARGET_EXPR_SLOT. + +2024-02-04 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd a6f1083699. + * dmd/VERSION: Bump version to v2.107.0 + * Make-lang.in (D_FRONTEND_OBJS): Add d/pragmasem.o. + * d-builtins.cc (strip_type_modifiers): Update for new front-end + interface. + * d-codegen.cc (declaration_type): Likewise. + (parameter_type): Likewise. + * d-target.cc (TargetCPP::parameterType): Likewise. + * expr.cc (ExprVisitor::visit (IndexExp *)): Likewise. + (ExprVisitor::visit (VarExp *)): Likewise. + (ExprVisitor::visit (AssocArrayLiteralExp *)): Likewise. + * runtime.cc (get_libcall_type): Likewise. + * typeinfo.cc (TypeInfoVisitor::visit (TypeInfoConstDeclaration *)): + Likewise. + (TypeInfoVisitor::visit (TypeInfoInvariantDeclaration *)): Likewise. + (TypeInfoVisitor::visit (TypeInfoSharedDeclaration *)): Likewise. + (TypeInfoVisitor::visit (TypeInfoWildDeclaration *)): Likewise. + * types.cc (build_ctype): Likewise. + +2024-02-03 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd e770945277. + * Make-lang.in (D_FRONTEND_OBJS): Add d/basicmangle.o, d/enumsem.o, + d/funcsem.o, d/templatesem.o. + * d-builtins.cc (build_frontend_type): Update for new front-end + interface. + * d-codegen.cc (declaration_type): Likewise. + (parameter_type): Likewise. + * d-incpath.cc (add_globalpaths): Likewise. + (add_filepaths): Likewise. + (add_import_paths): Likewise. + * d-lang.cc (d_init_options): Likewise. + (d_handle_option): Likewise. + (d_parse_file): Likewise. + * decl.cc (DeclVisitor::finish_vtable): Likewise. + (DeclVisitor::visit (FuncDeclaration *)): Likewise. + (get_symbol_decl): Likewise. + * expr.cc (ExprVisitor::visit (StringExp *)): Likewise. + Implement support for 8-byte hexadecimal strings. + * typeinfo.cc (create_tinfo_types): Update internal TypeInfo + representation. + (TypeInfoVisitor::visit (TypeInfoConstDeclaration *)): Update for new + front-end interface. + (TypeInfoVisitor::visit (TypeInfoInvariantDeclaration *)): Likewise. + (TypeInfoVisitor::visit (TypeInfoSharedDeclaration *)): Likewise. + (TypeInfoVisitor::visit (TypeInfoWildDeclaration *)): Likewise. + (TypeInfoVisitor::visit (TypeInfoClassDeclaration *)): Move data for + TypeInfo_Class.nameSig to the end of the object. + (create_typeinfo): Update for new front-end interface. + +2024-02-02 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd bce5c1f7b5. + * d-attribs.cc (build_attributes): Update for new front-end interface. + * d-lang.cc (d_parse_file): Likewise. + * decl.cc (DeclVisitor::visit (VarDeclaration *)): Likewise. + * expr.cc (build_lambda_tree): New function. + (ExprVisitor::visit (FuncExp *)): Use build_lambda_tree. + (ExprVisitor::visit (SymOffExp *)): Likewise. + (ExprVisitor::visit (VarExp *)): Likewise. + * typeinfo.cc (create_tinfo_types): Add two ulong fields to internal + TypeInfo representation. + (TypeInfoVisitor::visit (TypeInfoClassDeclaration *)): Emit stub data + for TypeInfo_Class.nameSig. + (TypeInfoVisitor::visit (TypeInfoStructDeclaration *)): Update for new + front-end interface. + +2024-02-02 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd d8e3976a58. + * dmd/VERSION: Bump version to v2.107.0-beta.1. + * d-lang.cc (d_parse_file): Update for new front-end interface. + * modules.cc (struct module_info): Add standalonectors. + (build_module_tree): Implement @standalone. + (register_module_decl): Likewise. + +2024-02-02 Iain Buclaw <ibuclaw@gdcproject.org> + + * dmd/MERGE: Merge upstream dmd f1a045928e. + * dmd/VERSION: Bump version to v2.106.1-rc.1. + * gdc.texi (fignore-unknown-pragmas): Update documentation. + * d-builtins.cc (covariant_with_builtin_type_p): Update for new + front-end interface. + * d-lang.cc (d_parse_file): Likewise. + * typeinfo.cc (make_frontend_typeinfo): Likewise. + +2024-01-04 David Malcolm <dmalcolm@redhat.com> + + * lang.opt.urls: New file, autogenerated by + regenerate-opt-urls.py. + +2024-01-03 Jakub Jelinek <jakub@redhat.com> + + * gdc.texi: Bump @copyrights-d year. + +Copyright (C) 2024 Free Software Foundation, Inc. + +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 077668f..2d444c9 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -1,5 +1,5 @@ # Make-lang.in -- Top level -*- makefile -*- fragment for the D frontend. -# Copyright (C) 2006-2024 Free Software Foundation, Inc. +# Copyright (C) 2006-2025 Free Software Foundation, Inc. # GCC is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -65,8 +65,8 @@ ALL_DFLAGS = $(DFLAGS-$@) $(GDCFLAGS) -fversion=IN_GCC $(CHECKING_DFLAGS) \ $(WARN_DFLAGS) DCOMPILE.base = $(GDC) -c $(ALL_DFLAGS) -o $@ -DCOMPILE = $(DCOMPILE.base) -MT $@ -MMD -MP -MF $(@D)/$(DEPDIR)/$(*F).TPo -DPOSTCOMPILE = @mv $(@D)/$(DEPDIR)/$(*F).TPo $(@D)/$(DEPDIR)/$(*F).Po +DCOMPILE = $(DCOMPILE.base) -MT $@ -MMD -MP -MF $(@D)/$(DEPDIR)/$(basename $(@F)).TPo +DPOSTCOMPILE = @mv $(@D)/$(DEPDIR)/$(basename $(@F)).TPo $(@D)/$(DEPDIR)/$(basename $(@F)).Po DLINKER = $(GDC) $(NO_PIE_FLAG) -lstdc++ # Like LINKER, but use a mutex for serializing front end links. @@ -84,24 +84,25 @@ D_FRONTEND_OBJS = \ d/arrayop.o \ d/arraytypes.o \ d/attrib.o \ + d/attribsem.o \ d/ast_node.o \ d/astcodegen.o \ d/astenums.o \ - d/basicmangle.o \ d/blockexit.o \ d/builtin.o \ d/canthrow.o \ d/chkformat.o \ d/clone.o \ d/common-bitfields.o \ + d/common-charactertables.o \ d/common-file.o \ + d/common-identifiertables.o \ d/common-outbuffer.o \ d/common-smallbuffer.o \ d/compiler.o \ d/cond.o \ d/constfold.o \ d/cparse.o \ - d/cppmangle.o \ d/ctfeexpr.o \ d/ctorflow.o \ d/cxxfrontend.o \ @@ -110,10 +111,10 @@ D_FRONTEND_OBJS = \ d/declaration.o \ d/delegatize.o \ d/denum.o \ + d/deps.o \ d/dimport.o \ d/dinterpret.o \ d/dmacro.o \ - d/dmangle.o \ d/dmodule.o \ d/doc.o \ d/dscope.o \ @@ -131,7 +132,6 @@ D_FRONTEND_OBJS = \ d/expression.o \ d/expressionsem.o \ d/file_manager.o \ - d/foreachvar.o \ d/func.o \ d/funcsem.o \ d/globals.o \ @@ -152,6 +152,9 @@ D_FRONTEND_OBJS = \ d/lambdacomp.o \ d/lexer.o \ d/location.o \ + d/mangle-basic.o \ + d/mangle-cpp.o \ + d/mangle-package.o \ d/mtype.o \ d/mustuse.o \ d/nogc.o \ @@ -161,9 +164,6 @@ D_FRONTEND_OBJS = \ d/opover.o \ d/optimize.o \ d/parse.o \ - d/parsetimevisitor.o \ - d/permissivevisitor.o \ - d/postordervisitor.o \ d/pragmasem.o \ d/printast.o \ d/root-aav.o \ @@ -185,12 +185,10 @@ D_FRONTEND_OBJS = \ d/root-utf.o \ d/rootobject.o \ d/safe.o \ - d/sapply.o \ d/semantic2.o \ d/semantic3.o \ d/sideeffect.o \ d/statement.o \ - d/statement_rewrite_walker.o \ d/statementsem.o \ d/staticassert.o \ d/staticcond.o \ @@ -198,13 +196,19 @@ D_FRONTEND_OBJS = \ d/target.o \ d/templateparamsem.o \ d/templatesem.o \ + d/timetrace.o \ d/tokens.o \ d/traits.o \ - d/transitivevisitor.o \ d/typesem.o \ d/typinf.o \ d/utils.o \ - d/visitor.o + d/visitor-foreachvar.o \ + d/visitor-package.o \ + d/visitor-parsetime.o \ + d/visitor-permissive.o \ + d/visitor-postorder.o \ + d/visitor-statement_rewrite_walker.o \ + d/visitor-transitive.o # Language-specific object files for D. D_OBJS = \ @@ -291,7 +295,7 @@ d.srcextra: d.tags: force cd $(srcdir)/d; \ - $(ETAGS) -o TAGS.sub *.cc *.h dmd/*.h dmd/root/*.h; \ + $(ETAGS) -o TAGS.sub *.cc *.h dmd/*.h dmd/common/*.h dmd/root/*.h; \ $(ETAGS) --include TAGS.sub --include ../TAGS.sub d.man: doc/gdc.1 @@ -304,7 +308,7 @@ d.srcman: doc/gdc.1 check-d: check-gdc lang_checks += check-gdc lang_checks_parallelized += check-gdc -check_gdc_parallelize = 10 +check_gdc_parallelize = 128 # No D-specific selftests. selftest-d: @@ -416,6 +420,14 @@ d/common-%.o: d/dmd/common/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) +d/mangle-%.o: d/dmd/mangle/%.d + $(DCOMPILE) $(D_INCLUDES) $< + $(DPOSTCOMPILE) + d/root-%.o: d/dmd/root/%.d $(DCOMPILE) $(D_INCLUDES) $< $(DPOSTCOMPILE) + +d/visitor-%.o: d/dmd/visitor/%.d + $(DCOMPILE) $(D_INCLUDES) $< + $(DPOSTCOMPILE) diff --git a/gcc/d/config-lang.in b/gcc/d/config-lang.in index 13cd177..d2c1a03 100644 --- a/gcc/d/config-lang.in +++ b/gcc/d/config-lang.in @@ -1,5 +1,5 @@ # config-lang.in -- Top level configure fragment for gcc D frontend. -# Copyright (C) 2006-2024 Free Software Foundation, Inc. +# Copyright (C) 2006-2025 Free Software Foundation, Inc. # GCC is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc index 0f7ca10..77315dc 100644 --- a/gcc/d/d-attribs.cc +++ b/gcc/d/d-attribs.cc @@ -1,5 +1,5 @@ /* d-attribs.c -- D attributes handling. - Copyright (C) 2015-2024 Free Software Foundation, Inc. + Copyright (C) 2015-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -440,7 +440,7 @@ apply_user_attributes (Dsymbol *sym, tree node) if (TYPE_P (node) && !COMPLETE_TYPE_P (node)) attr_flags |= ATTR_FLAG_TYPE_IN_PLACE; - Expressions *attrs = uda->getAttributes (); + Expressions *attrs = dmd::getAttributes (uda); decl_attributes (&node, build_attributes (attrs), attr_flags); input_location = saved_location; diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc index 4546c0e..9c14645 100644 --- a/gcc/d/d-builtins.cc +++ b/gcc/d/d-builtins.cc @@ -1,5 +1,5 @@ /* d-builtins.cc -- GCC builtins support for D. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -139,8 +139,8 @@ build_frontend_type (tree type) dtype = Type::basic[i]; /* Search for type matching size and signedness. */ - if (unsignedp != dtype->isunsigned () - || size != dtype->size ()) + if (unsignedp != dtype->isUnsigned () + || size != dmd::size (dtype)) continue; return dmd::addMod (dtype, mod); @@ -157,7 +157,7 @@ build_frontend_type (tree type) dtype = Type::basic[i]; /* Search for type matching size. */ - if (dtype->size () != size) + if (dmd::size (dtype) != size) continue; return dmd::addMod (dtype, mod); @@ -174,7 +174,7 @@ build_frontend_type (tree type) dtype = Type::basic[i]; /* Search for type matching size. */ - if (dtype->size () != size) + if (dmd::size (dtype) != size) continue; return dmd::addMod (dtype, mod); @@ -215,7 +215,7 @@ build_frontend_type (tree type) break; dtype = dmd::addMod (dmd::sarrayOf (dtype, nunits), mod); - if (target.isVectorTypeSupported (dtype->size (), dtype->nextOf ())) + if (target.isVectorTypeSupported (dmd::size (dtype), dtype->nextOf ())) break; dtype = dmd::addMod (TypeVector::create (dtype), mod); @@ -279,7 +279,7 @@ build_frontend_type (tree type) NULL); vd->parent = sdecl; vd->offset = tree_to_uhwi (byte_position (field)); - vd->semanticRun = PASS::semanticdone; + vd->semanticRun (PASS::semanticdone); vd->csym = field; sdecl->members->push (vd); sdecl->fields.push (vd); @@ -522,6 +522,7 @@ d_init_versions (void) targetdm.d_cpu_versions (); targetdm.d_os_versions (); + VersionCondition::addPredefinedGlobalIdent ("CppRuntime_GNU"); VersionCondition::addPredefinedGlobalIdent ("CppRuntime_Gcc"); } @@ -571,8 +572,8 @@ d_build_builtins_module (Module *m) tf->trust = !DECL_ASSEMBLER_NAME_SET_P (decl) ? TRUST::safe : TREE_NOTHROW (decl) ? TRUST::trusted : TRUST::system; - tf->isnothrow (true); - tf->isnogc (true); + tf->isNothrow (true); + tf->isNogc (true); FuncDeclaration *func = FuncDeclaration::create (Loc (), Loc (), @@ -711,11 +712,11 @@ matches_builtin_type (Type *t1, Type *t2) if (((tb1->isTypePointer () && tb2->isTypePointer ()) || (tb1->isTypeVector () && tb2->isTypeVector ())) - && tb1->implicitConvTo (tb2) != MATCH::nomatch) + && dmd::implicitConvTo (tb1, tb2) != MATCH::nomatch) return true; - if (tb1->isintegral () == tb2->isintegral () - && tb1->size () == tb2->size ()) + if (tb1->isIntegral () == tb2->isIntegral () + && dmd::size (tb1) == dmd::size (tb2)) return true; return false; @@ -738,7 +739,7 @@ covariant_with_builtin_type_p (Type *t1, Type *t2) /* Check for obvious reasons why types may be distinct. */ if (tf1 == NULL || tf2 == NULL - || tf1->isref () != tf2->isref () + || tf1->isRef () != tf2->isRef () || tf1->parameterList.varargs != tf2->parameterList.varargs || tf1->parameterList.length () != tf2->parameterList.length ()) return false; @@ -776,7 +777,7 @@ maybe_set_builtin_1 (Dsymbol *d) if (ad != NULL) { /* Recursively search through attribute decls. */ - Dsymbols *decls = ad->include (NULL); + Dsymbols *decls = dmd::include (ad, NULL); if (decls && decls->length) { for (size_t i = 0; i < decls->length; i++) diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index 2b3089b..e35f75a 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -1,5 +1,5 @@ /* d-codegen.cc -- Code generation and routines for manipulation of GCC trees. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,24 +43,33 @@ along with GCC; see the file COPYING3. If not see #include "d-tree.h" -/* Return the GCC location for the D frontend location LOC. */ +/* Return the GCC location for the D frontend source location LOC. */ location_t -make_location_t (const Loc &loc) +make_location_t (const SourceLoc &loc) { location_t gcc_location = input_location; - if (const char *filename = loc.filename ()) + if (loc.filename.length != 0) { - linemap_add (line_table, LC_ENTER, 0, filename, loc.linnum ()); - linemap_line_start (line_table, loc.linnum (), 0); - gcc_location = linemap_position_for_column (line_table, loc.charnum ()); + linemap_add (line_table, LC_ENTER, 0, loc.filename.ptr, loc.line); + linemap_line_start (line_table, loc.line, 0); + gcc_location = linemap_position_for_column (line_table, loc.column); linemap_add (line_table, LC_LEAVE, 0, NULL, 0); } return gcc_location; } +/* Likewise, but converts LOC from a compact opaque location. */ + +location_t +make_location_t (const Loc loc) +{ + const SourceLoc sloc = loc.toSourceLoc (); + return make_location_t (sloc); +} + /* Return the DECL_CONTEXT for symbol DSYM. */ tree @@ -246,15 +255,15 @@ build_integer_cst (dinteger_t value, tree type) tree build_float_cst (const real_t &value, Type *totype) { - real_t new_value; + real_value new_value; TypeBasic *tb = totype->isTypeBasic (); gcc_assert (tb != NULL); tree type_node = build_ctype (tb); - real_convert (&new_value.rv (), TYPE_MODE (type_node), &value.rv ()); + real_convert (&new_value, TYPE_MODE (type_node), &value.rv ()); - return build_real (type_node, new_value.rv ()); + return build_real (type_node, new_value); } /* Returns the .length component from the D dynamic array EXP. */ @@ -308,7 +317,7 @@ d_array_value (tree type, tree len, tree data) CONSTRUCTOR_APPEND_ELT (ce, len_field, len); CONSTRUCTOR_APPEND_ELT (ce, ptr_field, data); - return build_constructor (type, ce); + return build_padded_constructor (type, ce); } /* Returns value representing the array length of expression EXP. @@ -631,6 +640,37 @@ force_target_expr (tree exp) return build_target_expr (decl, exp); } +/* Determine whether expression EXP can have a copy of its value elided. */ + +static bool +can_elide_copy_p (Expression *exp) +{ + /* Explicit `__rvalue(exp)'. */ + if (exp->rvalue ()) + return true; + + /* Look for variable expression. */ + Expression *last = exp; + while (CommaExp *ce = last->isCommaExp ()) + last = ce->e2; + + if (VarExp *ve = last->isVarExp ()) + { + if (VarDeclaration *vd = ve->var->isVarDeclaration ()) + { + /* Variable is an implicit copy of an lvalue. */ + if (vd->storage_class & STCrvalue) + return true; + + /* The destructor is going to run on the variable. */ + if (vd->isArgDtorVar ()) + return true; + } + } + + return false; +} + /* Returns the address of the expression EXP. */ tree @@ -677,10 +717,11 @@ build_address (tree exp) if (AGGREGATE_TYPE_P (TREE_TYPE (exp)) && !aggregate_value_p (TREE_TYPE (exp), exp)) { - tree tmp = build_local_temp (TREE_TYPE (exp)); - init = compound_expr (init, build_memset_call (tmp)); - init = compound_expr (init, modify_expr (tmp, exp)); - exp = tmp; + tree target = force_target_expr (exp); + tree ptr = build_address (TARGET_EXPR_SLOT (target)); + init = compound_expr (init, target); + init = compound_expr (init, build_clear_padding_call (ptr)); + exp = TARGET_EXPR_SLOT (target); } else exp = force_target_expr (exp); @@ -851,14 +892,13 @@ build_memset_call (tree ptr, tree num) } /* Use a zero constant to fill the destination if setting the entire object. - For CONSTRUCTORs, the memcpy() is lowered to a ref-all pointer assignment, - which can then be merged with other stores to the object. */ + For CONSTRUCTORs, also set CONSTRUCTOR_ZERO_PADDING_BITS. */ tree valtype = TREE_TYPE (TREE_TYPE (ptr)); if (tree_int_cst_equal (TYPE_SIZE_UNIT (valtype), num)) { tree cst = build_zero_cst (valtype); if (TREE_CODE (cst) == CONSTRUCTOR) - return build_memcpy_call (ptr, build_address (cst), num); + CONSTRUCTOR_ZERO_PADDING_BITS (cst) = 1; return modify_expr (build_deref (ptr), cst); } @@ -867,6 +907,18 @@ build_memset_call (tree ptr, tree num) ptr, integer_zero_node, num); } +/* Build a call to built-in clear_padding(), clears padding bits inside of the + object representation of object pointed by PTR. */ + +tree +build_clear_padding_call (tree ptr) +{ + gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr))); + + return build_call_expr (builtin_decl_explicit (BUILT_IN_CLEAR_PADDING), 1, + ptr); +} + /* Return TRUE if the struct SD is suitable for comparison using memcmp. This is because we don't guarantee that padding is zero-initialized for a stack variable, so we can't use memcmp to compare struct values. */ @@ -904,7 +956,7 @@ identity_compare_p (StructDeclaration *sd) if (offset != vd->offset) return false; - offset += vd->type->size (); + offset += dmd::size (vd->type); } } @@ -972,15 +1024,15 @@ lower_struct_comparison (tree_code code, StructDeclaration *sd, /* Compare inner data structures. */ tcmp = lower_struct_comparison (code, ts->sym, t1ref, t2ref); } - else if (type->ty != TY::Tvector && type->isintegral ()) + else if (type->ty != TY::Tvector && type->isIntegral ()) { /* Integer comparison, no special handling required. */ tcmp = build_boolop (code, t1ref, t2ref); } - else if (type->ty != TY::Tvector && type->isfloating ()) + else if (type->ty != TY::Tvector && type->isFloating ()) { /* Floating-point comparison, don't compare padding in type. */ - if (!type->iscomplex ()) + if (!type->isComplex ()) tcmp = build_float_identity (code, t1ref, t2ref); else { @@ -1165,7 +1217,7 @@ build_struct_literal (tree type, vec <constructor_elt, va_gc> *init) { /* If the initializer was empty, use default zero initialization. */ if (vec_safe_is_empty (init)) - return build_constructor (type, NULL); + return build_padded_constructor (type, NULL); /* Struct literals can be seen for special enums representing `_Complex', make sure to reinterpret the literal as the correct type. */ @@ -1266,7 +1318,7 @@ build_struct_literal (tree type, vec <constructor_elt, va_gc> *init) /* Ensure that we have consumed all values. */ gcc_assert (vec_safe_is_empty (init) || ANON_AGGR_TYPE_P (type)); - tree ctor = build_constructor (type, ve); + tree ctor = build_padded_constructor (type, ve); if (constant_p) TREE_CONSTANT (ctor) = 1; @@ -1274,6 +1326,17 @@ build_struct_literal (tree type, vec <constructor_elt, va_gc> *init) return ctor; } +/* Return a new zero padded CONSTRUCTOR node whose type is TYPE and values are + in the vec pointed to by VALS. */ + +tree +build_padded_constructor (tree type, vec<constructor_elt, va_gc> *vals) +{ + tree ctor = build_constructor (type, vals); + CONSTRUCTOR_ZERO_PADDING_BITS (ctor) = 1; + return ctor; +} + /* Given the TYPE of an anonymous field inside T, return the FIELD_DECL for the field. If not found return NULL_TREE. Because anonymous types can nest, we must also search all @@ -1607,7 +1670,7 @@ underlying_complex_expr (tree type, tree expr) real_part (expr)); CONSTRUCTOR_APPEND_ELT (ve, TREE_CHAIN (TYPE_FIELDS (type)), imaginary_part (expr)); - return build_constructor (type, ve); + return build_padded_constructor (type, ve); } /* Replace type in the reinterpret cast with a cast to the record type. */ @@ -1812,7 +1875,7 @@ build_array_from_val (Type *type, tree val) for (size_t i = 0; i < dims; i++) CONSTRUCTOR_APPEND_ELT (elms, size_int (i), val); - return build_constructor (build_ctype (type), elms); + return build_padded_constructor (build_ctype (type), elms); } /* Build a static array of type TYPE from an array of EXPS. @@ -1839,15 +1902,13 @@ build_array_from_exprs (Type *type, Expressions *exps, bool const_p) /* Create a new temporary to store the array. */ tree var = build_local_temp (satype); + /* Initialize the temporary. */ + tree assign = modify_expr (var, build_padded_constructor (satype, elms)); + /* Fill any alignment holes with zeroes. */ - TypeStruct *ts = etype->baseElemOf ()->isTypeStruct (); - tree init = NULL; - if (ts && (!identity_compare_p (ts->sym) || ts->sym->isUnionDeclaration ())) - init = build_memset_call (var); + tree clear_padding = build_clear_padding_call (build_address (var)); - /* Initialize the temporary. */ - tree assign = modify_expr (var, build_constructor (satype, elms)); - return compound_expr (compound_expr (init, assign), var); + return compound_expr (compound_expr (assign, clear_padding), var); } @@ -2119,7 +2180,7 @@ call_side_effect_free_p (FuncDeclaration *func, Type *type) /* Must be a `nothrow' function. */ TypeFunction *tf = func->type->toTypeFunction (); - if (!tf->isnothrow ()) + if (!tf->isNothrow ()) return false; /* Return type can't be `void' or `noreturn', as that implies all work is @@ -2128,7 +2189,7 @@ call_side_effect_free_p (FuncDeclaration *func, Type *type) return false; /* Only consider it as `pure' if it can't modify its arguments. */ - if (func->isPure () == PURE::const_) + if (dmd::isPure (func) == PURE::const_) return true; } @@ -2137,7 +2198,7 @@ call_side_effect_free_p (FuncDeclaration *func, Type *type) TypeFunction *tf = get_function_type (type); /* Must be a `nothrow` function type. */ - if (tf == NULL || !tf->isnothrow ()) + if (tf == NULL || !tf->isNothrow ()) return false; /* Return type can't be `void' or `noreturn', as that implies all work is @@ -2261,7 +2322,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object, if (empty_aggregate_p (TREE_TYPE (targ)) && !TREE_ADDRESSABLE (targ) && TREE_CODE (targ) != CONSTRUCTOR) { - tree t = build_constructor (TREE_TYPE (targ), NULL); + tree t = build_padded_constructor (TREE_TYPE (targ), NULL); targ = build2 (COMPOUND_EXPR, TREE_TYPE (t), targ, t); } @@ -2280,8 +2341,10 @@ d_build_call (TypeFunction *tf, tree callable, tree object, - The ABI of the function expects the callee to destroy its arguments; when the caller is handles destruction, then `targ' has already been made into a temporary. */ - if (arg->op == EXP::structLiteral || (!sd->postblit && !sd->dtor) - || target.isCalleeDestroyingArgs (tf)) + if (!can_elide_copy_p (arg) + && (arg->op == EXP::structLiteral + || (!sd->postblit && !sd->dtor) + || target.isCalleeDestroyingArgs (tf))) targ = force_target_expr (targ); targ = convert (build_reference_type (TREE_TYPE (targ)), @@ -2571,7 +2634,7 @@ get_frame_for_symbol (Dsymbol *sym) framefields = DECL_CHAIN (framefields); } - frame_ref = build_address (build_constructor (type, ve)); + frame_ref = build_address (build_padded_constructor (type, ve)); } } @@ -2915,7 +2978,7 @@ get_frameinfo (FuncDeclaration *fd) symbols, give it a decent error for now. */ if (requiresClosure != fd->requiresClosure && (fd->nrvo_var || !global.params.useGC)) - fd->checkClosure (); + dmd::checkClosure (fd); /* Set-up a closure frame, this will be allocated on the heap. */ FRAMEINFO_CREATES_FRAME (ffi) = 1; diff --git a/gcc/d/d-compiler.cc b/gcc/d/d-compiler.cc index 3eb3a76..e18f5d3 100644 --- a/gcc/d/d-compiler.cc +++ b/gcc/d/d-compiler.cc @@ -1,5 +1,5 @@ /* d-compiler.cc -- D frontend interface to the gcc back-end. - Copyright (C) 2020-2024 Free Software Foundation, Inc. + Copyright (C) 2020-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "dmd/compiler.h" +#include "dmd/errors.h" #include "dmd/expression.h" #include "dmd/identifier.h" #include "dmd/module.h" @@ -46,9 +47,9 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type) Type *tb = type->toBasetype (); - if (expr->type->isintegral ()) + if (expr->type->isIntegral ()) cst = build_integer_cst (expr->toInteger (), build_ctype (expr->type)); - else if (expr->type->isfloating ()) + else if (expr->type->isFloating ()) cst = build_float_cst (expr->toReal (), expr->type); else if (expr->op == EXP::arrayLiteral) { @@ -60,13 +61,13 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type) for (size_t i = 0; i < elements->length; i++) { Expression *e = (*elements)[i]; - if (e->type->isintegral ()) + if (e->type->isIntegral ()) { tree value = build_integer_cst (e->toInteger (), build_ctype (e->type)); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } - else if (e->type->isfloating ()) + else if (e->type->isFloating ()) { tree value = build_float_cst (e->toReal (), e->type); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); @@ -164,7 +165,39 @@ Compiler::onParseModule (Module *m) driver intends on compiling the import. */ bool -Compiler::onImport (Module *) +Compiler::onImport (Module *m) { - return false; + if (!includeImports) + return false; + + if (m->filetype != FileType::d && m->filetype != FileType::c) + return false; + + /* All imports modules are included except those in the runtime library. */ + ModuleDeclaration *md = m->md; + if (md && md->id) + { + if (md->packages.length >= 1) + { + if (!strcmp (md->packages.ptr[0]->toChars (), "core") + || !strcmp (md->packages.ptr[0]->toChars (), "std") + || !strcmp (md->packages.ptr[0]->toChars (), "gcc") + || !strcmp (md->packages.ptr[0]->toChars (), "etc")) + return false; + } + else if (!strcmp (md->id->toChars (), "object")) + return false; + } + else if (m->ident) + { + if (!strcmp (m->ident->toChars (), "object")) + return false; + } + + /* This import will be compiled. */ + if (global.params.v.verbose) + message ("compileimport (%s)", m->srcfile.toChars ()); + + compiledImports.push (m); + return true; } diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc index 5c79cdf..c5b0d65 100644 --- a/gcc/d/d-convert.cc +++ b/gcc/d/d-convert.cc @@ -1,5 +1,5 @@ /* d-convert.cc -- Data type conversion routines. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -384,7 +384,7 @@ convert_expr (tree exp, Type *etype, Type *totype) case TY::Tstruct: if (tbtype->ty == TY::Tstruct) { - if (totype->size () == etype->size ()) + if (dmd::size (totype) == dmd::size (etype)) { /* Allowed to cast to structs with same type size. */ result = build_vconvert (build_ctype (totype), exp); @@ -467,8 +467,8 @@ convert_expr (tree exp, Type *etype, Type *totype) else if (tbtype->ty == TY::Tarray) { dinteger_t dim = ebtype->isTypeSArray ()->dim->toInteger (); - dinteger_t esize = ebtype->nextOf ()->size (); - dinteger_t tsize = tbtype->nextOf ()->size (); + dinteger_t esize = dmd::size (ebtype->nextOf ()); + dinteger_t tsize = dmd::size (tbtype->nextOf ()); tree ptrtype = build_ctype (dmd::pointerTo (tbtype->nextOf ())); @@ -498,10 +498,11 @@ convert_expr (tree exp, Type *etype, Type *totype) { /* And allows casting a static array to any struct type too. Type sizes should have already been checked by the frontend. */ - gcc_assert (totype->size () == etype->size ()); + gcc_assert (dmd::size (totype) == dmd::size (etype)); result = build_vconvert (build_ctype (totype), exp); } - else if (tbtype->ty == TY::Tvector && tbtype->size () == ebtype->size ()) + else if (tbtype->ty == TY::Tvector + && dmd::size (tbtype) == dmd::size (ebtype)) { /* Allow casting from array to vector as if its an unaligned load. */ tree type = build_ctype (totype); @@ -526,8 +527,8 @@ convert_expr (tree exp, Type *etype, Type *totype) else if (tbtype->ty == TY::Tarray) { /* Assume tvoid->size() == 1. */ - dinteger_t fsize = ebtype->nextOf ()->toBasetype ()->size (); - dinteger_t tsize = tbtype->nextOf ()->toBasetype ()->size (); + dinteger_t fsize = dmd::size (ebtype->nextOf ()->toBasetype ()); + dinteger_t tsize = dmd::size (tbtype->nextOf ()->toBasetype ()); if (fsize != tsize) { @@ -594,7 +595,7 @@ convert_expr (tree exp, Type *etype, Type *totype) case TY::Tvector: if (tbtype->ty == TY::Tsarray) { - if (tbtype->size () == ebtype->size ()) + if (dmd::size (tbtype) == dmd::size (ebtype)) return build_vconvert (build_ctype (totype), exp); } break; @@ -602,8 +603,8 @@ convert_expr (tree exp, Type *etype, Type *totype) default: /* All casts between imaginary and non-imaginary result in 0.0, except for casts between complex and imaginary types. */ - if (!ebtype->iscomplex () && !tbtype->iscomplex () - && (ebtype->isimaginary () != tbtype->isimaginary ())) + if (!ebtype->isComplex () && !tbtype->isComplex () + && (ebtype->isImaginary () != tbtype->isImaginary ())) { warning (OPT_Wcast_result, "cast from %qs to %qs will produce zero result", @@ -687,7 +688,7 @@ convert_for_rvalue (tree expr, Type *etype, Type *totype) CONSTRUCTOR_APPEND_ELT (elms, index, value); } - return build_constructor (build_ctype (totype), elms); + return build_padded_constructor (build_ctype (totype), elms); } } @@ -740,7 +741,7 @@ check_valist_conversion (Expression *expr, Type *totype, bool in_assignment) && valist_array_p (decl->type)); /* OK if conversion between types is allowed. */ - if (type->implicitConvTo (totype) != MATCH::nomatch) + if (dmd::implicitConvTo (type, totype) != MATCH::nomatch) return; if (in_assignment) @@ -787,7 +788,7 @@ convert_for_assignment (Expression *expr, Type *totype, bool literalp) TypeSArray *sa_type = tbtype->isTypeSArray (); uinteger_t count = sa_type->dim->toUInteger (); - tree ctor = build_constructor (build_ctype (totype), NULL); + tree ctor = build_padded_constructor (build_ctype (totype), NULL); if (count) { vec <constructor_elt, va_gc> *ce = NULL; @@ -813,7 +814,7 @@ convert_for_assignment (Expression *expr, Type *totype, bool literalp) /* D Front end uses IntegerExp(0) to mean zero-init an array or structure. */ if ((tbtype->ty == TY::Tsarray || tbtype->ty == TY::Tstruct) - && ebtype->isintegral ()) + && ebtype->isIntegral ()) { tree ret = build_expr (expr, false, literalp); gcc_assert (integer_zerop (ret)); diff --git a/gcc/d/d-ctfloat.cc b/gcc/d/d-ctfloat.cc index e77365a..659d561 100644 --- a/gcc/d/d-ctfloat.cc +++ b/gcc/d/d-ctfloat.cc @@ -1,5 +1,5 @@ /* d-ctfloat.cc -- D frontend interface to the gcc back-end. - Copyright (C) 2020-2024 Free Software Foundation, Inc. + Copyright (C) 2020-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ along with GCC; see the file COPYING3. If not see real_t CTFloat::fabs (real_t r) { - real_t x; + real_t x = {}; real_arithmetic (&x.rv (), ABS_EXPR, &r.rv (), NULL); return x.normalize (); } @@ -43,7 +43,7 @@ CTFloat::fabs (real_t r) real_t CTFloat::ldexp (real_t r, int exp) { - real_t x; + real_t x = {}; real_ldexp (&x.rv (), &r.rv (), exp); return x.normalize (); } @@ -87,7 +87,7 @@ CTFloat::isInfinity (real_t r) real_t CTFloat::parse (const char *buffer, bool &overflow) { - real_t r; + real_t r = {}; real_from_string3 (&r.rv (), buffer, TYPE_MODE (long_double_type_node)); /* Front-end checks overflow to see if the value is representable. */ diff --git a/gcc/d/d-diagnostic.cc b/gcc/d/d-diagnostic.cc index b2accf9..55cb42e 100644 --- a/gcc/d/d-diagnostic.cc +++ b/gcc/d/d-diagnostic.cc @@ -1,5 +1,5 @@ /* d-diagnostics.cc -- D frontend interface to gcc diagnostics. - Copyright (C) 2017-2024 Free Software Foundation, Inc. + Copyright (C) 2017-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -183,13 +183,14 @@ escape_d_format (const char *format) front-end, which does not get translated by the gcc diagnostic routines. */ static void ATTRIBUTE_GCC_DIAG(3,0) -d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format, - va_list ap, diagnostic_t kind, bool verbatim) +d_diagnostic_report_diagnostic (const SourceLoc &loc, int opt, + const char *format, va_list ap, + diagnostic_t kind, bool verbatim) { va_list argp; va_copy (argp, ap); - if (loc.filename () || !verbatim) + if (loc.filename.length != 0 || !verbatim) { rich_location rich_loc (line_table, make_location_t (loc)); diagnostic_info diagnostic; @@ -198,7 +199,7 @@ d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format, diagnostic_set_info_translated (&diagnostic, xformat, &argp, &rich_loc, kind); if (opt != 0) - diagnostic.option_index = opt; + diagnostic.option_id = opt; diagnostic_report_diagnostic (global_dc, &diagnostic); } @@ -207,8 +208,9 @@ d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format, /* Write verbatim messages with no location direct to stream. */ text_info text (expand_d_format (format), &argp, errno, nullptr); - pp_format_verbatim (global_dc->printer, &text); - pp_newline_and_flush (global_dc->printer); + pretty_printer *const pp = global_dc->get_reference_printer (); + pp_format_verbatim (pp, &text); + pp_newline_and_flush (pp); } va_end (argp); @@ -219,8 +221,8 @@ d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format, error count depending on how KIND is treated. */ void D_ATTRIBUTE_FORMAT(2,0) ATTRIBUTE_GCC_DIAG(2,0) -verrorReport (const Loc& loc, const char *format, va_list ap, ErrorKind kind, - const char *prefix1, const char *prefix2) +verrorReport (const SourceLoc loc, const char *format, va_list ap, + ErrorKind kind, const char *prefix1, const char *prefix2) { diagnostic_t diag_kind = DK_UNSPECIFIED; int opt = 0; @@ -240,7 +242,7 @@ verrorReport (const Loc& loc, const char *format, va_list ap, ErrorKind kind, } else if (kind == ErrorKind::warning) { - if (global.gag || global.params.warnings == DIAGNOSTICoff) + if (global.gag || global.params.useWarnings == DIAGNOSTICoff) { if (global.gag) global.gaggedWarnings++; @@ -249,7 +251,7 @@ verrorReport (const Loc& loc, const char *format, va_list ap, ErrorKind kind, } /* Warnings don't count if not treated as errors. */ - if (global.params.warnings == DIAGNOSTICerror) + if (global.params.useWarnings == DIAGNOSTICerror) global.warnings++; diag_kind = DK_WARNING; @@ -303,7 +305,7 @@ verrorReport (const Loc& loc, const char *format, va_list ap, ErrorKind kind, explicit location LOC. This doesn't increase the global error count. */ void D_ATTRIBUTE_FORMAT(2,0) ATTRIBUTE_GCC_DIAG(2,0) -verrorReportSupplemental (const Loc& loc, const char* format, va_list ap, +verrorReportSupplemental (const SourceLoc loc, const char* format, va_list ap, ErrorKind kind) { if (kind == ErrorKind::error) @@ -313,7 +315,7 @@ verrorReportSupplemental (const Loc& loc, const char* format, va_list ap, } else if (kind == ErrorKind::warning) { - if (global.params.warnings == DIAGNOSTICoff || global.gag) + if (global.params.useWarnings == DIAGNOSTICoff || global.gag) return; } else if (kind == ErrorKind::deprecation) diff --git a/gcc/d/d-frontend.cc b/gcc/d/d-frontend.cc index 0d7f4a3..3778cc6 100644 --- a/gcc/d/d-frontend.cc +++ b/gcc/d/d-frontend.cc @@ -1,5 +1,5 @@ /* d-frontend.cc -- D frontend interface to the gcc back-end. - Copyright (C) 2013-2024 Free Software Foundation, Inc. + Copyright (C) 2013-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -52,7 +52,7 @@ isBuiltin (FuncDeclaration *fd) Return result; NULL if cannot evaluate it. */ Expression * -eval_builtin (const Loc &loc, FuncDeclaration *fd, Expressions *arguments) +eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments) { if (fd->builtin == BUILTIN::unimp) return NULL; @@ -79,11 +79,11 @@ eval_builtin (const Loc &loc, FuncDeclaration *fd, Expressions *arguments) /* Build and return typeinfo type for TYPE. */ Type * -getTypeInfoType (const Loc &loc, Type *type, Scope *sc, bool genObjCode) +getTypeInfoType (Loc loc, Type *type, Scope *sc) { gcc_assert (type->ty != TY::Terror); check_typeinfo_type (loc, sc); - create_typeinfo (type, sc ? sc->_module->importedFrom : NULL, genObjCode); + create_typeinfo (type, sc); return type->vtinfo->type; } diff --git a/gcc/d/d-frontend.h b/gcc/d/d-frontend.h index 7cc5e5e..00f3666 100644 --- a/gcc/d/d-frontend.h +++ b/gcc/d/d-frontend.h @@ -1,5 +1,5 @@ /* d-frontend.h -- D frontend interface to the gcc back-end. - Copyright (C) 2019-2024 Free Software Foundation, Inc. + Copyright (C) 2019-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/d-gimplify.cc b/gcc/d/d-gimplify.cc index 55a5608..9e31f3a 100644 --- a/gcc/d/d-gimplify.cc +++ b/gcc/d/d-gimplify.cc @@ -1,5 +1,5 @@ /* D-specific tree lowering bits; see also gimple.cc. - Copyright (C) 2020-2024 Free Software Foundation, Inc. + Copyright (C) 2020-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/d-incpath.cc b/gcc/d/d-incpath.cc index 32ab0b7..155dc99 100644 --- a/gcc/d/d-incpath.cc +++ b/gcc/d/d-incpath.cc @@ -1,5 +1,5 @@ /* d-incpath.cc -- Set up combined import paths for the D frontend. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -67,47 +67,41 @@ prefixed_path (const char *path, const char *iprefix) /* Add PATHS to the global import lookup path. */ static void -add_globalpaths (Strings *paths) +add_globalpaths (Strings &paths) { - if (paths) + for (size_t i = 0; i < paths.length; i++) { - for (size_t i = 0; i < paths->length; i++) - { - const char *path = (*paths)[i]; - const char *target = lrealpath (path); - - if (target == NULL || !FileName::exists (target)) - { - if (target) - free (CONST_CAST (char *, target)); - continue; - } + const char *path = paths[i]; + const char *target = lrealpath (path); - global.path.push (target); + if (target == NULL || !FileName::exists (target)) + { + if (target) + free (CONST_CAST (char *, target)); + continue; } + + global.path.push (target); } } /* Add PATHS to the global file import lookup path. */ static void -add_filepaths (Strings *paths) +add_filepaths (Strings &paths) { - if (paths) + for (size_t i = 0; i < paths.length; i++) { - for (size_t i = 0; i < paths->length; i++) - { - const char *path = (*paths)[i]; - const char *target = lrealpath (path); - - if (!FileName::exists (target)) - { - free (CONST_CAST (char *, target)); - continue; - } + const char *path = paths[i]; + const char *target = lrealpath (path); - global.filePath.push (target); + if (!FileName::exists (target)) + { + free (CONST_CAST (char *, target)); + continue; } + + global.filePath.push (target); } } @@ -139,7 +133,7 @@ add_import_paths (const char *iprefix, const char *imultilib, bool stdinc) bool found = false; for (size_t i = 0; i < global.params.imppath.length; i++) { - if (strcmp (path, global.params.imppath[i]) == 0) + if (strcmp (path, global.params.imppath[i].path) == 0) { found = true; break; @@ -166,9 +160,13 @@ add_import_paths (const char *iprefix, const char *imultilib, bool stdinc) /* Add import search paths. */ for (size_t i = 0; i < global.params.imppath.length; i++) { - const char *path = global.params.imppath[i]; + const char *path = global.params.imppath[i].path; if (path) - add_globalpaths (FileName::splitPath (path)); + { + Strings array; + FileName::appendSplitPath (path, array); + add_globalpaths (array); + } } /* Add string import search paths. */ @@ -176,6 +174,10 @@ add_import_paths (const char *iprefix, const char *imultilib, bool stdinc) { const char *path = global.params.fileImppath[i]; if (path) - add_filepaths (FileName::splitPath (path)); + { + Strings array; + FileName::appendSplitPath (path, array); + add_filepaths (array); + } } } diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 89ffa7e..ec2ea59 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -1,5 +1,5 @@ /* d-lang.cc -- Language-dependent hooks for D. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -302,7 +302,7 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options) /* Warnings and deprecations are disabled by default. */ global.params.useDeprecated = DIAGNOSTICinform; - global.params.warnings = DIAGNOSTICoff; + global.params.useWarnings = DIAGNOSTICoff; global.params.v.errorLimit = flag_max_errors; global.params.v.messageStyle = MessageStyle::gnu; @@ -450,7 +450,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, break; case OPT_fdebug: - global.params.debuglevel = value ? 1 : 0; + global.params.debugEnabled = value ? true : false; break; case OPT_fdebug_: @@ -460,7 +460,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, break; } - error ("bad argument for %<-fdebug%>: %qs", arg); + error ("bad argument for %<-fdebug=%>: %qs", arg); break; case OPT_fdoc: @@ -510,6 +510,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, case CppStdRevisionCpp14: case CppStdRevisionCpp17: case CppStdRevisionCpp20: + case CppStdRevisionCpp23: global.params.cplusplus = (CppStdRevision) value; break; @@ -522,6 +523,10 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, global.params.ignoreUnsupportedPragmas = value; break; + case OPT_finclude_imports: + includeImports = true; + break; + case OPT_finvariants: global.params.useInvariants = value ? CHECKENABLEon : CHECKENABLEoff; break; @@ -533,7 +538,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, case OPT_fmodule_file_: global.params.modFileAliasStrings.push (arg); if (!strchr (arg, '=')) - error ("bad argument for %<-fmodule-file%>: %qs", arg); + error ("bad argument for %<-fmodule-file=%>: %qs", arg); break; case OPT_fmoduleinfo: @@ -563,6 +568,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, global.params.previewIn = value; global.params.fix16997 = value; global.params.noSharedAccess = FeatureState::enabled; + global.params.safer = FeatureState::enabled; global.params.rvalueRefParam = FeatureState::enabled; global.params.inclusiveInContracts = value; global.params.systemVariables = FeatureState::enabled; @@ -613,6 +619,10 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, global.params.noSharedAccess = FeatureState::enabled; break; + case OPT_fpreview_safer: + global.params.safer = FeatureState::enabled; + break; + case OPT_fpreview_rvaluerefparam: global.params.rvalueRefParam = FeatureState::enabled; break; @@ -695,7 +705,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, break; } - error ("bad argument for %<-fversion%>: %qs", arg); + error ("bad argument for %<-fversion=%>: %qs", arg); break; case OPT_H: @@ -772,7 +782,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, case OPT_Wall: if (value) - global.params.warnings = DIAGNOSTICinform; + global.params.useWarnings = DIAGNOSTICinform; break; case OPT_Wdeprecated: @@ -781,7 +791,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, case OPT_Werror: if (value) - global.params.warnings = DIAGNOSTICerror; + global.params.useWarnings = DIAGNOSTICerror; break; case OPT_Wspeculative: @@ -907,7 +917,7 @@ d_post_options (const char ** fn) /* Error about use of deprecated features. */ if (global.params.useDeprecated == DIAGNOSTICinform - && global.params.warnings == DIAGNOSTICerror) + && global.params.useWarnings == DIAGNOSTICerror) global.params.useDeprecated = DIAGNOSTICerror; if (flag_excess_precision == EXCESS_PRECISION_DEFAULT) @@ -920,7 +930,8 @@ d_post_options (const char ** fn) global.params.v.errorLimit = flag_max_errors; global.params.v.showColumns = flag_show_column; - global.params.v.printErrorContext = flag_diagnostics_show_caret; + global.params.v.errorPrintMode = flag_diagnostics_show_caret + ? ErrorPrintMode::printErrorContext : ErrorPrintMode::simpleError; /* Keep the front-end location type in sync with params. */ Loc::set (global.params.v.showColumns, global.params.v.messageStyle); @@ -933,7 +944,12 @@ d_post_options (const char ** fn) /* The front-end parser only has access to `compileEnv', synchronize its fields with params. */ global.compileEnv.previewIn = global.params.previewIn; + global.compileEnv.transitionIn = global.params.v.vin; global.compileEnv.ddocOutput = global.params.ddoc.doOutput; + global.compileEnv.cCharLookupTable = + IdentifierCharLookup::forTable (IdentifierTable::C11); + global.compileEnv.dCharLookupTable = + IdentifierCharLookup::forTable (IdentifierTable::LR); if (warn_return_type == -1) warn_return_type = 0; @@ -1073,9 +1089,9 @@ d_parse_file (void) /* Buffer for contents of .ddoc files. */ OutBuffer ddocbuf; - /* In this mode, the first file name is supposed to be a duplicate - of one of the input files. */ - if (d_option.fonly && strcmp (d_option.fonly, main_input_filename) != 0) + /* In this mode, the main input file is supposed to be the same as the one + given by -fonly=. */ + if (d_option.fonly && !endswith (main_input_filename, d_option.fonly)) error ("%<-fonly=%> argument is different from first input file name"); for (size_t i = 0; i < num_in_fnames; i++) @@ -1102,7 +1118,7 @@ d_parse_file (void) if (count < 0) { - error (Loc ("stdin", 0, 0), "%s", xstrerror (errno)); + error (Loc::singleFilename ("stdin"), "%s", xstrerror (errno)); free (buffer); continue; } @@ -1297,6 +1313,21 @@ d_parse_file (void) dmd::semantic3 (m, NULL); } + if (includeImports) + { + for (size_t i = 0; i < compiledImports.length; i++) + { + Module *m = compiledImports[i]; + gcc_assert (m->isRoot ()); + + if (global.params.v.verbose) + message ("semantic3 %s", m->toChars ()); + + dmd::semantic3 (m, NULL); + modules.push (m); + } + } + Module::runDeferredSemantic3 (); /* Check again, incase semantic3 pass loaded any more modules. */ @@ -1705,8 +1736,8 @@ d_types_compatible_p (tree x, tree y) return true; /* Type system allows implicit conversion between. */ - if (tx->implicitConvTo (ty) != MATCH::nomatch - || ty->implicitConvTo (tx) != MATCH::nomatch) + if (dmd::implicitConvTo (tx, ty) != MATCH::nomatch + || dmd::implicitConvTo (ty, tx) != MATCH::nomatch) return true; } diff --git a/gcc/d/d-longdouble.cc b/gcc/d/d-longdouble.cc index e4c8c5e6..193a828 100644 --- a/gcc/d/d-longdouble.cc +++ b/gcc/d/d-longdouble.cc @@ -1,5 +1,5 @@ /* d-longdouble.cc -- Software floating-point emulation for the frontend. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -113,7 +113,7 @@ longdouble::to_bool (void) const longdouble longdouble::add (const longdouble &r) const { - longdouble x; + longdouble x = {}; real_arithmetic (&x.rv (), PLUS_EXPR, &this->rv (), &r.rv ()); return x.normalize (); } @@ -121,7 +121,7 @@ longdouble::add (const longdouble &r) const longdouble longdouble::sub (const longdouble &r) const { - longdouble x; + longdouble x = {}; real_arithmetic (&x.rv (), MINUS_EXPR, &this->rv (), &r.rv ()); return x.normalize (); } @@ -129,7 +129,7 @@ longdouble::sub (const longdouble &r) const longdouble longdouble::mul (const longdouble &r) const { - longdouble x; + longdouble x = {}; real_arithmetic (&x.rv (), MULT_EXPR, &this->rv (), &r.rv ()); return x.normalize (); } @@ -137,7 +137,7 @@ longdouble::mul (const longdouble &r) const longdouble longdouble::div (const longdouble &r) const { - longdouble x; + longdouble x = {}; real_arithmetic (&x.rv (), RDIV_EXPR, &this->rv (), &r.rv ()); return x.normalize (); } @@ -145,7 +145,7 @@ longdouble::div (const longdouble &r) const longdouble longdouble::mod (const longdouble &r) const { - longdouble x; + longdouble x = {}; real_value q; if (r.rv ().cl == rvc_zero || REAL_VALUE_ISINF (this->rv ())) @@ -172,7 +172,7 @@ longdouble::mod (const longdouble &r) const longdouble longdouble::neg (void) const { - longdouble x; + longdouble x = {}; real_arithmetic (&x.rv (), NEGATE_EXPR, &this->rv (), NULL); return x.normalize (); } diff --git a/gcc/d/d-port.cc b/gcc/d/d-port.cc index f15f24d..a66fabc 100644 --- a/gcc/d/d-port.cc +++ b/gcc/d/d-port.cc @@ -1,5 +1,5 @@ /* d-port.cc -- D frontend interface to the gcc back-end. - Copyright (C) 2013-2024 Free Software Foundation, Inc. + Copyright (C) 2013-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -74,7 +74,7 @@ Port::strupr (char *s) bool Port::isFloat32LiteralOutOfRange (const char *buffer) { - real_t r; + real_t r = {}; real_from_string3 (&r.rv (), buffer, TYPE_MODE (float_type_node)); @@ -87,7 +87,7 @@ Port::isFloat32LiteralOutOfRange (const char *buffer) bool Port::isFloat64LiteralOutOfRange (const char *buffer) { - real_t r; + real_t r = {}; real_from_string3 (&r.rv (), buffer, TYPE_MODE (double_type_node)); diff --git a/gcc/d/d-spec.cc b/gcc/d/d-spec.cc index cf0d418..c788048 100644 --- a/gcc/d/d-spec.cc +++ b/gcc/d/d-spec.cc @@ -1,5 +1,5 @@ /* d-spec.c -- Specific flags and argument handling of the D front end. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -104,8 +104,8 @@ lang_specific_driver (cl_decoded_option **in_decoded_options, /* The total number of arguments with the new stuff. */ unsigned int num_args = 1; - /* "-fonly" if it appears on the command line. */ - const char *only_source_option = 0; + /* "-fonly=" if it appears on the command line. */ + const char *only_source_arg = 0; /* Whether the -o option was used. */ bool saw_opt_o = false; @@ -280,13 +280,13 @@ lang_specific_driver (cl_decoded_option **in_decoded_options, case OPT_fonly_: args[i] |= SKIPOPT; - only_source_option = decoded_options[i].orig_option_with_args_text; + only_source_arg = arg; if (arg != NULL) { - const char *suffix = strrchr (only_source_option, '.'); + const char *suffix = strrchr (only_source_arg, '.'); if (suffix == NULL || strcmp (suffix, ".d") != 0) - only_source_option = concat (only_source_option, ".d", NULL); + only_source_arg = concat (only_source_arg, ".d", NULL); } break; @@ -335,48 +335,52 @@ lang_specific_driver (cl_decoded_option **in_decoded_options, + (phobos_library != PHOBOS_NOLINK) * 4 + 2; new_decoded_options = XNEWVEC (cl_decoded_option, num_args); - i = 0; j = 0; /* Copy the 0th argument, i.e., the name of the program itself. */ - new_decoded_options[j++] = decoded_options[i++]; + new_decoded_options[j++] = decoded_options[0]; /* NOTE: We start at 1 now, not 0. */ - while (i < argc) + for (i = 1; i < argc; i++) { if (args[i] & SKIPOPT) - { - ++i; - continue; - } - - new_decoded_options[j] = decoded_options[i]; + continue; if (!saw_libcxx && (args[i] & WITHLIBCXX)) { - --j; saw_libcxx = &decoded_options[i]; + continue; } - if (args[i] & DSOURCE) + if (only_source_arg && (args[i] & DSOURCE)) { - if (only_source_option) - --j; + if (!endswith (decoded_options[i].arg, only_source_arg)) + continue; } - i++; + new_decoded_options[j] = decoded_options[i]; j++; } - if (only_source_option) + if (only_source_arg) { - const char *only_source_arg = only_source_option + 7; + /* Generate -fonly= option, then copy D input sources that were initially + skipped in first pass over all decoded_options. */ generate_option (OPT_fonly_, only_source_arg, 1, CL_DRIVER, &new_decoded_options[j]); j++; - generate_option_input_file (only_source_arg, - &new_decoded_options[j++]); + for (i = 1; i < argc; i++) + { + if (!(args[i] & DSOURCE)) + continue; + + if (endswith (decoded_options[i].arg, only_source_arg)) + continue; + + new_decoded_options[j] = decoded_options[i]; + j++; + } } /* If no reason to link against libphobos library, then don't add it. */ diff --git a/gcc/d/d-system.h b/gcc/d/d-system.h index 426e3f8..7df5bdc 100644 --- a/gcc/d/d-system.h +++ b/gcc/d/d-system.h @@ -1,5 +1,5 @@ /* d-system.h -- DMD frontend inclusion of gcc header files. - * Copyright (C) 2018-2024 Free Software Foundation, Inc. + * Copyright (C) 2018-2025 Free Software Foundation, Inc. * * GCC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/gcc/d/d-target-def.h b/gcc/d/d-target-def.h index 5eaa1d7..39a7f06 100644 --- a/gcc/d/d-target-def.h +++ b/gcc/d/d-target-def.h @@ -1,5 +1,5 @@ /* d-target-def.h -- Default initializers for D target hooks. - Copyright (C) 2017-2024 Free Software Foundation, Inc. + Copyright (C) 2017-2025 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc index dd46e53..ce25bf8 100644 --- a/gcc/d/d-target.cc +++ b/gcc/d/d-target.cc @@ -1,5 +1,5 @@ /* d-target.cc -- Target interface for the D front end. - Copyright (C) 2013-2024 Free Software Foundation, Inc. + Copyright (C) 2013-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -241,7 +241,7 @@ Target::fieldalign (Type *type) /* Returns a Type for the va_list type of the target. */ Type * -Target::va_listType (const Loc &, Scope *) +Target::va_listType (Loc, Scope *) { if (this->tvalist) return this->tvalist; @@ -274,13 +274,13 @@ Target::isVectorTypeSupported (int sz, Type *type) type = Type::tuns8; /* No support for non-trivial types, complex types, or booleans. */ - if (!type->isTypeBasic () || type->iscomplex () || type->ty == TY::Tbool) + if (!type->isTypeBasic () || type->isComplex () || type->ty == TY::Tbool) return 2; /* In [simd/vector extensions], which vector types are supported depends on the target. The implementation is expected to only support the vector types that are implemented in the target's hardware. */ - unsigned HOST_WIDE_INT nunits = sz / type->size (); + unsigned HOST_WIDE_INT nunits = sz / dmd::size (type); tree ctype = build_vector_type (build_ctype (type), nunits); if (!targetm.vector_mode_supported_p (TYPE_MODE (ctype))) @@ -300,7 +300,7 @@ Target::isVectorOpSupported (Type *type, EXP op, Type *) return true; /* Don't support if type is non-scalar, such as __vector(void[]). */ - if (!type->isscalar ()) + if (!type->isScalar ()) return false; /* Don't support if expression cannot be represented. */ @@ -314,7 +314,7 @@ Target::isVectorOpSupported (Type *type, EXP op, Type *) case EXP::mod: case EXP::modAssign: /* fmod() is lowered as a function call. */ - if (type->isfloating ()) + if (type->isFloating ()) return false; break; @@ -449,11 +449,11 @@ Target::isReturnOnStack (TypeFunction *tf, bool) /* Need the back-end type to determine this, but this is called from the frontend before semantic processing is finished. An accurate value is not currently needed anyway. */ - if (tf->isref ()) + if (tf->isRef ()) return false; Type *tn = tf->next->toBasetype (); - if (tn->size () == SIZE_INVALID) + if (dmd::size (tn) == SIZE_INVALID) return false; return (tn->ty == TY::Tstruct || tn->ty == TY::Tsarray); @@ -517,7 +517,7 @@ d_handle_target_object_format (void) LOC is the location to use for the returned expression. */ Expression * -Target::getTargetInfo (const char *key, const Loc &loc) +Target::getTargetInfo (const char *key, Loc loc) { unsigned ix; d_target_info_spec *spec; @@ -583,8 +583,28 @@ Target::preferPassByRef (Type *param_type) { /* See note in Target::isReturnOnStack. */ Type *tb = param_type->toBasetype (); - if (tb->size () == SIZE_INVALID) + if (dmd::size (tb) == SIZE_INVALID) return false; return (tb->ty == TY::Tstruct || tb->ty == TY::Tsarray); } + +/* Returns true if the specified bit-field FIELD contributes to the alignment + of the containing aggregate. */ + +bool +TargetC::contributesToAggregateAlignment(BitFieldDeclaration *field) +{ + if (this->bitFieldStyle == TargetC::BitFieldStyle::MS) + return true; + + if (PCC_BITFIELD_TYPE_MATTERS) + { + /* Named bit-fields contribute to alignment. Some targets also apply the + same rules to unnamed bit-fields too. */ + if (!field->isAnonymous () || targetm.align_anon_bitfield ()) + return true; + } + + return false; +} diff --git a/gcc/d/d-target.def b/gcc/d/d-target.def index 0b0332d..c5a6e96 100644 --- a/gcc/d/d-target.def +++ b/gcc/d/d-target.def @@ -1,5 +1,5 @@ /* d-target.def -- Target hook definitions for the D front end. - Copyright (C) 2017-2024 Free Software Foundation, Inc. + Copyright (C) 2017-2025 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the diff --git a/gcc/d/d-target.h b/gcc/d/d-target.h index 1958ec5..b781419 100644 --- a/gcc/d/d-target.h +++ b/gcc/d/d-target.h @@ -1,5 +1,5 @@ /* d-target.h -- Data structure definitions for target-specific D behavior. - Copyright (C) 2017-2024 Free Software Foundation, Inc. + Copyright (C) 2017-2025 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the diff --git a/gcc/d/d-tree.def b/gcc/d/d-tree.def index b076c63..2ca0c13 100644 --- a/gcc/d/d-tree.def +++ b/gcc/d/d-tree.def @@ -1,6 +1,6 @@ /* d-tree.def -- Definitions and documentation for additional tree codes used in the D compiler (see tree.def for standard codes). - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h index 492c669..9d576e2 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -1,5 +1,5 @@ /* d-tree.h -- Definitions and declarations for code generation. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -534,7 +534,8 @@ extern Expression *d_eval_constant_expression (const Loc &, tree); extern void d_init_versions (void); /* In d-codegen.cc. */ -extern location_t make_location_t (const Loc &); +extern location_t make_location_t (const SourceLoc &); +extern location_t make_location_t (const Loc); extern tree d_decl_context (Dsymbol *); extern tree copy_aggregate_type (tree); extern bool declaration_reference_p (Declaration *); @@ -567,6 +568,7 @@ extern tree d_mark_read (tree); extern tree build_memcmp_call (tree, tree, tree); extern tree build_memcpy_call (tree, tree, tree); extern tree build_memset_call (tree, tree = NULL_TREE); +extern tree build_clear_padding_call (tree); extern bool identity_compare_p (StructDeclaration *); extern tree build_float_identity (tree_code, tree, tree); extern tree build_struct_comparison (tree_code, StructDeclaration *, @@ -574,6 +576,7 @@ extern tree build_struct_comparison (tree_code, StructDeclaration *, extern tree build_array_struct_comparison (tree_code, StructDeclaration *, tree, tree, tree); extern tree build_struct_literal (tree, vec <constructor_elt, va_gc> *); +extern tree build_padded_constructor (tree, vec <constructor_elt, va_gc> *); extern tree component_ref (tree, tree); extern tree build_assign (tree_code, tree, tree); extern tree modify_expr (tree, tree); @@ -703,11 +706,10 @@ extern tree get_classinfo_decl (ClassDeclaration *); extern void check_typeinfo_type (const Loc &, Scope *, Expression * = NULL); extern tree build_typeinfo (const Loc &, Type *, Expression * = NULL); extern tree build_typeinfo (Expression *, Type *); -extern void create_typeinfo (Type *, Module *, bool = true); +extern void create_typeinfo (Type *, Scope *); extern void create_tinfo_types (Module *); extern void layout_cpp_typeinfo (ClassDeclaration *); extern tree get_cpp_typeinfo_decl (ClassDeclaration *); -extern bool speculative_type_p (Type *); /* In toir.cc. */ extern void push_binding_level (level_kind); diff --git a/gcc/d/decl.cc b/gcc/d/decl.cc index 0a87c85..9ddf7cf 100644 --- a/gcc/d/decl.cc +++ b/gcc/d/decl.cc @@ -1,5 +1,5 @@ /* decl.cc -- Lower D frontend declarations to GCC trees. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see #include "dmd/nspace.h" #include "dmd/target.h" #include "dmd/template.h" +#include "dmd/typinf.h" #include "tree.h" #include "tree-iterator.h" @@ -255,18 +256,18 @@ public: void visit (Module *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; build_module_tree (d); - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Write the imported symbol to debug. */ void visit (Import *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; /* Implements import declarations by telling the debug back-end we are @@ -319,7 +320,7 @@ public: false, false); } - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Finish a top-level `asm` definition. */ @@ -350,7 +351,7 @@ public: void visit (AttribDeclaration *d) final override { - Dsymbols *ds = d->include (NULL); + Dsymbols *ds = dmd::include (d, NULL); if (!ds) return; @@ -471,7 +472,7 @@ public: void visit (StructDeclaration *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; if (d->type->ty == TY::Terror) @@ -519,7 +520,7 @@ public: if (d->xhash) this->build_dsymbol (d->xhash); - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Finish semantic analysis of functions in vtbl for class CD. */ @@ -541,7 +542,7 @@ public: has_errors = true; /* No name hiding to check for. */ - if (!d->isFuncHidden (fd) || fd->isFuture ()) + if (!dmd::isFuncHidden (d, fd) || fd->isFuture ()) continue; /* The function fd is hidden from the view of the class. @@ -561,10 +562,8 @@ public: if (fd2->isFuture ()) continue; - if (FuncDeclaration::leastAsSpecialized (fd, fd2, NULL) - == MATCH::nomatch - && FuncDeclaration::leastAsSpecialized (fd2, fd, NULL) - == MATCH::nomatch) + if (dmd::leastAsSpecialized (fd, fd2, NULL) == MATCH::nomatch + && dmd::leastAsSpecialized (fd2, fd, NULL) == MATCH::nomatch) continue; /* Hiding detected; same name, overlapping specializations. */ @@ -589,7 +588,7 @@ public: void visit (ClassDeclaration *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; if (d->type->ty == TY::Terror) @@ -618,7 +617,7 @@ public: /* Generate C symbols. */ d->csym = get_classinfo_decl (d); - Dsymbol *vtblsym = d->vtblSymbol (); + Dsymbol *vtblsym = dmd::vtblSymbol (d); vtblsym->csym = get_vtable_decl (d); tree sinit = aggregate_initializer_decl (d); @@ -652,10 +651,10 @@ public: } DECL_INITIAL (vtblsym->csym) - = build_constructor (TREE_TYPE (vtblsym->csym), elms); + = build_padded_constructor (TREE_TYPE (vtblsym->csym), elms); d_finish_decl (vtblsym->csym); - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Write out compiler generated TypeInfo and vtables for the given interface @@ -663,7 +662,7 @@ public: void visit (InterfaceDeclaration *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; if (d->type->ty == TY::Terror) @@ -698,7 +697,7 @@ public: DECL_INITIAL (d->csym) = layout_classinfo (d); d_finish_decl (d->csym); - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Write out compiler generated TypeInfo and initializer for the given @@ -706,10 +705,10 @@ public: void visit (EnumDeclaration *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; - if (d->errors || d->type->ty == TY::Terror) + if (d->errors () || d->type->ty == TY::Terror) { error_at (make_location_t (d->loc), "had semantic errors when compiling"); @@ -729,7 +728,7 @@ public: create_typeinfo (d->type, NULL); TypeEnum *tc = d->type->isTypeEnum (); - if (tc->sym->members && !d->type->isZeroInit ()) + if (tc->sym->members && !dmd::isZeroInit (d->type)) { /* Generate static initializer. */ d->sinit = enum_initializer_decl (d); @@ -737,7 +736,7 @@ public: d_finish_decl (d->sinit); } - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Finish up a variable declaration and push it into the current scope. @@ -745,7 +744,7 @@ public: void visit (VarDeclaration *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; if (d->type->ty == TY::Terror) @@ -783,7 +782,7 @@ public: { /* Do not store variables we cannot take the address of, but keep the values for purposes of debugging. */ - if (d->type->isscalar () && !dmd::hasPointers (d->type)) + if (d->type->isScalar () && !dmd::hasPointers (d->type)) { tree decl = get_symbol_decl (d); d_pushdecl (decl); @@ -792,6 +791,12 @@ public: } else if (d->isDataseg ()) { + /* When the front-end type size is invalid, an error has already been + given for the declaration or type. */ + dinteger_t size = dmd::size (d->type, d->loc); + if (size == SIZE_INVALID) + return; + tree decl = get_symbol_decl (d); /* Only need to build the VAR_DECL for extern declarations. */ @@ -805,9 +810,7 @@ public: return; /* How big a symbol can be should depend on back-end. */ - tree size = build_integer_cst (d->type->size (d->loc), - build_ctype (Type::tsize_t)); - if (!valid_constant_size_p (size)) + if (!valid_constant_size_p (build_integer_cst (size, size_type_node))) { error_at (make_location_t (d->loc), "size is too large"); return; @@ -823,7 +826,7 @@ public: DECL_INITIAL (decl) = build_expr (e, true); } } - else if (!d->type->isZeroInit ()) + else if (!dmd::isZeroInit (d->type)) { /* Use default initializer for the type. */ if (TypeStruct *ts = d->type->isTypeStruct ()) @@ -836,8 +839,9 @@ public: } /* Frontend should have already caught this. */ - gcc_assert (!integer_zerop (size) - || d->type->toBasetype ()->isTypeSArray ()); + gcc_assert ((size != 0 && size != SIZE_INVALID) + || d->type->toBasetype ()->isTypeSArray () + || d->isCsymbol ()); d_finish_decl (decl); @@ -890,7 +894,7 @@ public: } } - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Generate and compile a static TypeInfo declaration, but only if it is @@ -898,16 +902,16 @@ public: void visit (TypeInfoDeclaration *d) final override { - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; - if (speculative_type_p (d->tinfo)) + if (dmd::isSpeculativeType (d->tinfo)) return; tree t = get_typeinfo_decl (d); DECL_INITIAL (t) = layout_typeinfo (d); d_finish_decl (t); - d->semanticRun = PASS::obj; + d->semanticRun (PASS::obj); } /* Finish up a function declaration and compile it all the way @@ -916,7 +920,7 @@ public: void visit (FuncDeclaration *d) final override { /* Already generated the function. */ - if (d->semanticRun >= PASS::obj) + if (d->semanticRun () >= PASS::obj) return; /* Don't emit any symbols from gcc.attributes module. */ @@ -958,7 +962,7 @@ public: } /* Ensure all semantic passes have run. */ - if (d->semanticRun < PASS::semantic3) + if (d->semanticRun () < PASS::semantic3) { gcc_assert (!doing_semantic_analysis_p); @@ -972,8 +976,8 @@ public: return; /* Start generating code for this function. */ - gcc_assert (d->semanticRun == PASS::semantic3done); - d->semanticRun = PASS::obj; + gcc_assert (d->semanticRun () == PASS::semantic3done); + d->semanticRun (PASS::obj); /* Duplicated FuncDeclarations map to the same symbol. Check if this is the one declaration which will be emitted. */ @@ -1116,7 +1120,7 @@ build_decl_tree (Dsymbol *d) if (d->loc.filename ()) input_location = make_location_t (d->loc); else - input_location = make_location_t (Loc ("<no_file>", 1, 0)); + input_location = make_location_t (Loc::singleFilename ("<no_file>")); DeclVisitor v = DeclVisitor (); v.build_dsymbol (d); @@ -1180,7 +1184,7 @@ maybe_build_decl_tree (Declaration *decl) /* Still running semantic analysis on declaration, or it has already had its code generated. */ - if (doing_semantic_analysis_p || decl->semanticRun >= PASS::obj) + if (doing_semantic_analysis_p || decl->semanticRun () >= PASS::obj) return decl->csym; if (error_operand_p (decl->csym)) @@ -1247,7 +1251,7 @@ get_symbol_decl (Declaration *decl) /* CONST_DECL was initially intended for enumerals and may be used for scalars in general, but not for aggregates. Here a non-constant value is generated anyway so as its value can be used. */ - if (!vd->canTakeAddressOf () && !vd->type->isscalar ()) + if (!vd->canTakeAddressOf () && !vd->type->isScalar ()) { gcc_assert (vd->_init && !vd->_init->isVoidInitializer ()); Expression *ie = dmd::initializerToExpression (vd->_init); @@ -1308,7 +1312,7 @@ get_symbol_decl (Declaration *decl) /* Cannot make an expression out of a void initializer. */ gcc_assert (vd->_init && !vd->_init->isVoidInitializer ()); /* Non-scalar manifest constants have already been dealt with. */ - gcc_assert (vd->type->isscalar ()); + gcc_assert (vd->type->isScalar ()); Expression *ie = dmd::initializerToExpression (vd->_init); DECL_INITIAL (decl->csym) = build_expr (ie, true); @@ -1325,7 +1329,7 @@ get_symbol_decl (Declaration *decl) /* `const` applies to data that cannot be changed by the const reference to that data. It may, however, be changed by another reference to that same data. */ - if (vd->isConst () && !vd->isDataseg ()) + if (vd->isConst () && !vd->isResult () && !vd->isDataseg ()) TREE_READONLY (decl->csym) = 1; } @@ -2213,7 +2217,7 @@ get_vtable_decl (ClassDeclaration *decl) will have a different type. However the back-end seems to accept this. */ tree type = build_ctype (dmd::sarrayOf (Type::tvoidptr, decl->vtbl.length)); - Dsymbol *vtblsym = decl->vtblSymbol (); + Dsymbol *vtblsym = dmd::vtblSymbol (decl); vtblsym->csym = declare_extern_var (ident, type); DECL_LANG_SPECIFIC (vtblsym->csym) = build_lang_decl (NULL); @@ -2394,6 +2398,12 @@ aggregate_initializer_decl (AggregateDeclaration *decl) SET_DECL_ALIGN (sinit, sd->alignment.get () * BITS_PER_UNIT); DECL_USER_ALIGN (sinit) = true; } + else if (sd == NULL) + { + /* Alignment of class is determined its biggest field alignment. */ + SET_DECL_ALIGN (sinit, decl->alignsize * BITS_PER_UNIT); + DECL_USER_ALIGN (sinit) = true; + } decl->sinit = sinit; return sinit; @@ -2404,7 +2414,7 @@ aggregate_initializer_decl (AggregateDeclaration *decl) tree layout_class_initializer (ClassDeclaration *cd) { - NewExp *ne = NewExp::create (cd->loc, NULL, cd->type, NULL); + NewExp *ne = NewExp::create (cd->loc, NULL, NULL, cd->type, NULL); ne->type = cd->type; Expression *e = dmd::ctfeInterpret (ne); @@ -2418,7 +2428,7 @@ layout_struct_initializer (StructDeclaration *sd) { StructLiteralExp *sle = StructLiteralExp::create (sd->loc, sd, NULL); - if (!sd->fill (sd->loc, *sle->elements, true)) + if (!dmd::fill (sd, sd->loc, *sle->elements, true)) gcc_unreachable (); sle->type = sd->type; diff --git a/gcc/d/dmd/MERGE b/gcc/d/dmd/MERGE index dc47db8..58d19b4 100644 --- a/gcc/d/dmd/MERGE +++ b/gcc/d/dmd/MERGE @@ -1,4 +1,4 @@ -b65767825f365dbc153457fc86e1054b03196c6d +956e73d64e532a68213970316c2590c572ec03f3 The first line of this file holds the git revision number of the last merge done from the dlang/dmd repository. diff --git a/gcc/d/dmd/README.md b/gcc/d/dmd/README.md index 282e818..2e93d26 100644 --- a/gcc/d/dmd/README.md +++ b/gcc/d/dmd/README.md @@ -41,8 +41,11 @@ Note that these groups have no strict meaning, the category assignments are a bi | [frontend.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/frontend.d) | An interface for using DMD as a library | | [errors.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d) | Error reporting implementation | | [errorsink.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d) | Error reporting interface | +| [sarif.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sarif.d) | Generates SARIF reports for errors and warnings. | | [target.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) | | [compiler.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions | +| [deps.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d) | Implement the `-deps` and `-makedeps` switches | +| [timetrace.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.d) | Build time profiling utility | ### Lexing / parsing @@ -96,19 +99,20 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| -| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parsetimevisitor.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | -| [permissivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | -| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/strictvisitor.d) | Visitor that forces derived classes to implement `visit` for every possible node | -| [visitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler | -| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST | -| [postordervisitor.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/postordervisitor.d) | Depth-first expression visitor | -| [sapply.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sapply.d) | Depth-first statement visitor | -| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [visitor/parsetime.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/parsetime.d) | General [visitor](https://en.wikipedia.org/wiki/Visitor_pattern) for AST nodes | +| [visitor/permissive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/permissive.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes | +| [visitor/strict.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/strict.d) | Visitor that forces derived classes to implement `visit` for every possible node | +| [visitor/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/package.d) | A visitor implementing `visit` for all nodes present in the compiler | +| [visitor/transitive.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/transitive.d) | Provide a mixin template with visit methods for the parse time AST | +| [visitor/postorder.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/postorder.d) | Depth-first expression & statement visitor | +| [visitor/statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node | +| [visitor/foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | **Semantic passes** | File | Purpose | |-------------------------------------------------------------------------------------------|-------------------------------------------------------------------| +| [attribsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/attribsem.d) | Attribute semantics | | [dsymbolsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbolsem.d) | Do semantic 1 pass (symbol identifiers/types) | | [enumsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/enumsem.d) | Enum semantics | | [funcsem.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/funcsem.d) | Function semantics | @@ -196,15 +200,13 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-------------------------------------------------------------------------------|------------------------------------------------------| -| [lib.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib.d) | Abstract library class | -| [libelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libelf.d) | Library in ELF format (Unix) | -| [libmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmach.d) | Library in Mach-O format (macOS) | -| [libmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) | -| [libomf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/libomf.d) | Library in OMF format (legacy 32-bit Windows) | -| [scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format | -| [scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format | -| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format | -| [scanomf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/scanomf.d) | Extract symbol names from a library in OMF format | +| [lib/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/package.d) | Abstract library class | +| [lib/elf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/elf.d) | Library in ELF format (Unix) | +| [lib/mach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mach.d) | Library in Mach-O format (macOS) | +| [lib/mscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/mscoff.d) | Library in COFF format (32/64-bit Windows) | +| [lib/scanelf.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanelf.d) | Extract symbol names from a library in ELF format | +| [lib/scanmach.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmach.d) | Extract symbol names from a library in Mach-O format | +| [lib/scanmscoff.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lib/scanmscoff.d) | Extract symbol names from a library in COFF format | ### Code generation / back-end interfacing @@ -232,10 +234,10 @@ Note that these groups have no strict meaning, the category assignments are a bi | File | Purpose | |-----------------------------------------------------------------------------------|------------------------------------------------------------------| -| [cppmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmangle.d) | C++ name mangling | -| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cppmanglewin.d) | C++ name mangling for Windows | -| [basicmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/basicmangle.d) | D name mangling for basic types | -| [dmangle.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | +| [mangle/cpp.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cpp.d) | C++ name mangling | +| [mangle/cppwin.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cppwin.d) | C++ name mangling for Windows | +| [mangle/basic.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/basic.d) | D name mangling for basic types | +| [mangle/package.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) | ### Linking @@ -252,6 +254,8 @@ Note that these groups have no strict meaning, the category assignments are a bi | [hdrgen.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d) | Convert an AST into D source code for `.di` header generation, as well as `-vcg-ast` and error messages | | [json.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag | | [dtoh.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d) | C++ header generation from D source files | +| [disasm86.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/x86/disasm86.d) | x86-64 disassembly generation +| [disasmarm.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/backend/arm/disasmarm.d) | AArch64 disassembly generation ### Utility @@ -267,4 +271,3 @@ Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/m |---------------------------------------------------------------------------------|---------------------------------------------------------------| | [asttypename.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) | | [printast.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d) | Print the AST data structure | -| [foreachvar.d](https://github.com/dlang/dmd/blob/master/compiler/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression | diff --git a/gcc/d/dmd/VERSION b/gcc/d/dmd/VERSION index 5868b87..e972d6e 100644 --- a/gcc/d/dmd/VERSION +++ b/gcc/d/dmd/VERSION @@ -1 +1 @@ -v2.108.0 +v2.111.0 diff --git a/gcc/d/dmd/access.d b/gcc/d/dmd/access.d index 8d02203..eaaa288 100644 --- a/gcc/d/dmd/access.d +++ b/gcc/d/dmd/access.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/access.d, _access.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/access.d, _access.d) * Documentation: https://dlang.org/phobos/dmd_access.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/access.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/access.d */ module dmd.access; @@ -22,6 +22,7 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.errors; import dmd.expression; +import dmd.funcsem : overloadApply; import dmd.location; import dmd.tokens; @@ -141,7 +142,7 @@ private bool hasPackageAccess(Module mod, Dsymbol s) /**************************************** * Determine if scope sc has protected level access to cd. */ -private bool hasProtectedAccess(Scope *sc, Dsymbol s) +private bool hasProtectedAccess(Scope* sc, Dsymbol s) { if (auto cd = s.isClassMember()) // also includes interfaces { @@ -163,7 +164,7 @@ private bool hasProtectedAccess(Scope *sc, Dsymbol s) */ bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d) { - if (sc.flags & SCOPE.noaccesscheck) + if (sc.noAccessCheck) return false; static if (LOG) { @@ -272,7 +273,7 @@ bool symbolIsVisible(Dsymbol origin, Dsymbol s) * s = symbol to check for visibility * Returns: true if s is visible by origin */ -bool symbolIsVisible(Scope *sc, Dsymbol s) +bool symbolIsVisible(Scope* sc, Dsymbol s) { s = mostVisibleOverload(s); return checkSymbolAccess(sc, s); @@ -287,7 +288,7 @@ bool symbolIsVisible(Scope *sc, Dsymbol s) * s = symbol to check for visibility * Returns: true if s is visible by origin */ -bool checkSymbolAccess(Scope *sc, Dsymbol s) +bool checkSymbolAccess(Scope* sc, Dsymbol s) { final switch (s.visible().kind) { diff --git a/gcc/d/dmd/aggregate.d b/gcc/d/dmd/aggregate.d index 2c7622a..51d6fc4 100644 --- a/gcc/d/dmd/aggregate.d +++ b/gcc/d/dmd/aggregate.d @@ -4,12 +4,12 @@ * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions), * $(LINK2 https://dlang.org/spec/class.html, Class). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d, _aggregate.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aggregate.d, _aggregate.d) * Documentation: https://dlang.org/phobos/dmd_aggregate.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aggregate.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/aggregate.d */ module dmd.aggregate; @@ -25,7 +25,7 @@ import dmd.declaration; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, determineFields, search, determineSize, include; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -37,7 +37,7 @@ import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.tokens; -import dmd.typesem : defaultInit, addMod; +import dmd.typesem : defaultInit, addMod, size; import dmd.visitor; /** @@ -96,10 +96,10 @@ struct MangleOverride extern (C++) abstract class AggregateDeclaration : ScopeDsymbol { Type type; /// - StorageClass storage_class; /// + STC storage_class; /// uint structsize; /// size of struct uint alignsize; /// size of struct for alignment purposes - VarDeclarations fields; /// VarDeclaration fields + VarDeclarations fields; /// VarDeclaration fields including flattened AnonDeclaration members Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol /// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface @@ -154,7 +154,7 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol bool disableNew; /// disallow allocations using `new` Sizeok sizeok = Sizeok.none; /// set when structsize contains valid data - final extern (D) this(const ref Loc loc, Identifier id) + final extern (D) this(Loc loc, Identifier id) { super(loc, id); visibility = Visibility(Visibility.Kind.public_); @@ -187,70 +187,12 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return fields.length - isNested() - (vthis2 !is null); } - /*************************************** - * Collect all instance fields, then determine instance size. - * Returns: - * false if failed to determine the size. - */ - extern (D) final bool determineSize(const ref Loc loc) - { - //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); - - // The previous instance size finalizing had: - if (type.ty == Terror || errors) - return false; // failed already - if (sizeok == Sizeok.done) - return true; // succeeded - - if (!members) - { - .error(loc, "%s `%s` unknown size", kind, toPrettyChars); - return false; - } - - if (_scope) - dsymbolSemantic(this, null); - - // Determine the instance size of base class first. - if (auto cd = isClassDeclaration()) - { - cd = cd.baseClass; - if (cd && !cd.determineSize(loc)) - goto Lfail; - } - - // Determine instance fields when sizeok == Sizeok.none - if (!this.determineFields()) - goto Lfail; - if (sizeok != Sizeok.done) - finalizeSize(); - - // this aggregate type has: - if (type.ty == Terror) - return false; // marked as invalid during the finalizing. - if (sizeok == Sizeok.done) - return true; // succeeded to calculate instance size. - - Lfail: - // There's unresolvable forward reference. - if (type != Type.terror) - error(loc, "%s `%s` no size because of forward reference", kind, toPrettyChars); - // Don't cache errors from speculative semantic, might be resolvable later. - // https://issues.dlang.org/show_bug.cgi?id=16574 - if (!global.gag) - { - type = Type.terror; - errors = true; - } - return false; - } - abstract void finalizeSize(); - override final uinteger_t size(const ref Loc loc) + override final uinteger_t size(Loc loc) { //printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); - bool ok = determineSize(loc); + bool ok = determineSize(this, loc); //printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok); return ok ? structsize : SIZE_INVALID; } @@ -336,161 +278,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol return errors; } - /*************************************** - * Fill out remainder of elements[] with default initializers for fields[]. - * Params: - * loc = location - * elements = explicit arguments which given to construct object. - * ctorinit = true if the elements will be used for default initialization. - * Returns: - * false if any errors occur. - * Otherwise, returns true and the missing arguments will be pushed in elements[]. - */ - final bool fill(const ref Loc loc, ref Expressions elements, bool ctorinit) - { - //printf("AggregateDeclaration::fill() %s\n", toChars()); - assert(sizeok == Sizeok.done); - const nfields = nonHiddenFields(); - bool errors = false; - - size_t dim = elements.length; - elements.setDim(nfields); - foreach (size_t i; dim .. nfields) - elements[i] = null; - - // Fill in missing any elements with default initializers - foreach (i; 0 .. nfields) - { - if (elements[i]) - continue; - - auto vd = fields[i]; - auto vx = vd; - if (vd._init && vd._init.isVoidInitializer()) - vx = null; - - // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. - size_t fieldi = i; - foreach (j; 0 .. nfields) - { - if (i == j) - continue; - auto v2 = fields[j]; - if (!vd.isOverlappedWith(v2)) - continue; - - if (elements[j]) - { - vx = null; - break; - } - if (v2._init && v2._init.isVoidInitializer()) - continue; - - version (all) - { - /* Prefer first found non-void-initialized field - * union U { int a; int b = 2; } - * U u; // Error: overlapping initialization for field a and b - */ - if (!vx) - { - vx = v2; - fieldi = j; - } - else if (v2._init) - { - .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); - errors = true; - } - } - else - { - // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always - - /* Prefer explicitly initialized field - * union U { int a; int b = 2; } - * U u; // OK (u.b == 2) - */ - if (!vx || !vx._init && v2._init) - { - vx = v2; - fieldi = j; - } - else if (vx != vd && !vx.isOverlappedWith(v2)) - { - // Both vx and v2 fills vd, but vx and v2 does not overlap - } - else if (vx._init && v2._init) - { - .error(loc, "overlapping default initialization for field `%s` and `%s`", - v2.toChars(), vd.toChars()); - errors = true; - } - else - assert(vx._init || !vx._init && !v2._init); - } - } - if (vx) - { - Expression e; - if (vx.type.size() == 0) - { - e = null; - } - else if (vx._init) - { - assert(!vx._init.isVoidInitializer()); - if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 - { - .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars()); - errors = true; - } - else - e = vx.getConstInitializer(false); - } - else - { - if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) - { - .error(loc, "field `%s.%s` must be initialized because it has no default constructor", - type.toChars(), vx.toChars()); - errors = true; - } - /* https://issues.dlang.org/show_bug.cgi?id=12509 - * Get the element of static array type. - */ - Type telem = vx.type; - if (telem.ty == Tsarray) - { - /* We cannot use Type::baseElemOf() here. - * If the bottom of the Tsarray is an enum type, baseElemOf() - * will return the base of the enum, and its default initializer - * would be different from the enum's. - */ - TypeSArray tsa; - while ((tsa = telem.toBasetype().isTypeSArray()) !is null) - telem = tsa.next; - if (telem.ty == Tvoid) - telem = Type.tuns8.addMod(telem.mod); - } - if (telem.needsNested() && ctorinit) - e = telem.defaultInit(loc); - else - e = telem.defaultInitLiteral(loc); - } - elements[fieldi] = e; - } - } - foreach (e; elements) - { - if (e && e.op == EXP.error) - return false; - } - - return !errors; - } - override final Type getType() { /* Apply storage classes to forward references. (Issue 22254) @@ -660,7 +447,8 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol s.isTemplateDeclaration() || s.isOverloadSet())) { - .error(s.loc, "%s `%s` is not a constructor; identifiers starting with `__` are reserved for the implementation", s.kind(), s.toPrettyChars()); + error(s.loc, "%s name `__ctor` is not allowed", s.kind); + errorSupplemental(s.loc, "identifiers starting with `__` are reserved for internal use"); errors = true; s = null; } @@ -707,11 +495,6 @@ extern (C++) abstract class AggregateDeclaration : ScopeDsymbol // Back end void* sinit; /// initializer symbol - override final inout(AggregateDeclaration) isAggregateDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -793,6 +576,7 @@ public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) /**************************************** * Place a field (mem) into an aggregate (agg), which can be a struct, union or class * Params: + * loc = source location for error messages * nextoffset = location just past the end of the previous field in the aggregate. * Updated to be just past the end of this field to be placed, i.e. the future nextoffset * memsize = size of field @@ -805,8 +589,8 @@ public uint alignmember(structalign_t alignment, uint memalignsize, uint offset) * aligned offset to place field at * */ -public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize, - structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @safe pure nothrow +public uint placeField(Loc loc, ref uint nextoffset, uint memsize, uint memalignsize, + structalign_t alignment, ref uint aggsize, ref uint aggalignsize, bool isunion) @trusted nothrow { static if (0) { @@ -829,7 +613,12 @@ public uint placeField(ref uint nextoffset, uint memsize, uint memalignsize, bool overflow; const sz = addu(memsize, actualAlignment, overflow); addu(ofs, sz, overflow); - if (overflow) assert(0); + if (overflow) + { + error(loc, "max object size %u exceeded from adding field size %u + alignment adjustment %u + field offset %u when placing field in aggregate", + uint.max, memsize, actualAlignment, ofs); + return 0; + } // Skip no-op for noreturn without custom aligment if (memalignsize != 0 || !alignment.isDefault()) diff --git a/gcc/d/dmd/aggregate.h b/gcc/d/dmd/aggregate.h index c972f0a..82f8aec 100644 --- a/gcc/d/dmd/aggregate.h +++ b/gcc/d/dmd/aggregate.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -45,6 +45,9 @@ namespace dmd { FuncDeclaration *search_toString(StructDeclaration *sd); void semanticTypeInfoMembers(StructDeclaration *sd); + bool fill(StructDeclaration* sd, Loc loc, Expressions &elements, bool ctorinit); + bool isFuncHidden(ClassDeclaration* cd, FuncDeclaration* fd); + Dsymbol* vtblSymbol(ClassDeclaration *cd); } enum class ClassKind : uint8_t @@ -118,8 +121,7 @@ public: virtual Scope *newScope(Scope *sc); virtual void finalizeSize() = 0; - uinteger_t size(const Loc &loc) override final; - bool fill(const Loc &loc, Expressions &elements, bool ctorinit); + uinteger_t size(Loc loc) override final; Type *getType() override final; bool isDeprecated() const override final; // is aggregate deprecated? bool isNested() const; @@ -135,7 +137,6 @@ public: // Back end void *sinit; - AggregateDeclaration *isAggregateDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -168,7 +169,7 @@ public: private: uint16_t bitFields; public: - static StructDeclaration *create(const Loc &loc, Identifier *id, bool inObject); + static StructDeclaration *create(Loc loc, Identifier *id, bool inObject); StructDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; void finalizeSize() override final; @@ -191,7 +192,6 @@ public: bool requestTypeInfo() const; bool requestTypeInfo(bool v); - StructDeclaration *isStructDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } unsigned numArgTypes() const; @@ -205,7 +205,6 @@ public: UnionDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - UnionDeclaration *isUnionDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -278,7 +277,7 @@ public: ObjcClassDeclaration objc; // Data for a class declaration that is needed for the Objective-C integration Symbol *cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr - static ClassDeclaration *create(const Loc &loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject); + static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject); const char *toPrettyChars(bool QualifyTypes = false) override; ClassDeclaration *syntaxCopy(Dsymbol *s) override; Scope *newScope(Scope *sc) override; @@ -290,7 +289,6 @@ public: bool isBaseInfoComplete(); void finalizeSize() override; bool hasMonitor(); - bool isFuncHidden(FuncDeclaration *fd); bool isCOMclass() const; virtual bool isCOMinterface() const; bool isCPPclass() const; @@ -303,9 +301,7 @@ public: // Back end Dsymbol *vtblsym; - Dsymbol *vtblSymbol(); - ClassDeclaration *isClassDeclaration() override final { return (ClassDeclaration *)this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -320,6 +316,5 @@ public: bool isCPPinterface() const override; bool isCOMinterface() const override; - InterfaceDeclaration *isInterfaceDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/aliasthis.d b/gcc/d/dmd/aliasthis.d index 0e063ca..632cf95 100644 --- a/gcc/d/dmd/aliasthis.d +++ b/gcc/d/dmd/aliasthis.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d, _aliasthis.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/aliasthis.d, _aliasthis.d) * Documentation: https://dlang.org/phobos/dmd_aliasthis.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/aliasthis.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/aliasthis.d */ module dmd.aliasthis; @@ -31,9 +31,9 @@ extern (C++) final class AliasThis : Dsymbol /// Whether this `alias this` is deprecated or not bool isDeprecated_; - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { - super(loc, null); // it's anonymous (no identifier) + super(DSYM.aliasThis, loc, null); // it's anonymous (no identifier) this.ident = ident; } diff --git a/gcc/d/dmd/aliasthis.h b/gcc/d/dmd/aliasthis.h index 88ba353..249da70 100644 --- a/gcc/d/dmd/aliasthis.h +++ b/gcc/d/dmd/aliasthis.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2009-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2009-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/arrayop.d b/gcc/d/dmd/arrayop.d index af3875e..7d25e18 100644 --- a/gcc/d/dmd/arrayop.d +++ b/gcc/d/dmd/arrayop.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/arrays.html#array-operations, Array Operations) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/arrayop.d, _arrayop.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/arrayop.d, _arrayop.d) * Documentation: https://dlang.org/phobos/dmd_arrayop.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/arrayop.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/arrayop.d */ module dmd.arrayop; @@ -16,6 +16,7 @@ module dmd.arrayop; import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; +import dmd.dcast : implicitConvTo; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; @@ -43,12 +44,12 @@ bool isArrayOpValid(Expression e) if (e.op == EXP.arrayLiteral) { Type t = e.type.toBasetype(); - while (t.ty == Tarray || t.ty == Tsarray) + while (t.isStaticOrDynamicArray()) t = t.nextOf().toBasetype(); return (t.ty != Tvoid); } Type tb = e.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (isUnaArrayOp(e.op)) { @@ -79,7 +80,7 @@ bool isNonAssignmentArrayOp(Expression e) return isNonAssignmentArrayOp(e.isSliceExp().e1); Type tb = e.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { return (isUnaArrayOp(e.op) || isBinArrayOp(e.op)); } @@ -118,7 +119,7 @@ Expression arrayOp(BinExp e, Scope* sc) { //printf("BinExp.arrayOp() %s\n", e.toChars()); Type tb = e.type.toBasetype(); - assert(tb.ty == Tarray || tb.ty == Tsarray); + assert(tb.isStaticOrDynamicArray()); Type tbn = tb.nextOf().toBasetype(); if (tbn.ty == Tvoid) { @@ -145,7 +146,10 @@ Expression arrayOp(BinExp e, Scope* sc) if (auto te = id.isTemplateExp()) arrayOp = te.td; else - ObjectNotFound(idArrayOp); // fatal error + { + ObjectNotFound(e.loc, idArrayOp); // fatal error + return ErrorExp.get(); + } } auto fd = resolveFuncCall(e.loc, sc, arrayOp, tiargs, null, ArgumentList(args), FuncResolveFlag.standard); @@ -283,9 +287,8 @@ bool isUnaArrayOp(EXP op) @safe case EXP.tilde: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -306,9 +309,8 @@ bool isBinArrayOp(EXP op) @safe case EXP.pow: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -329,9 +331,8 @@ bool isBinAssignArrayOp(EXP op) @safe case EXP.powAssign: return true; default: - break; + return false; } - return false; } /*********************************************** @@ -345,7 +346,7 @@ bool isArrayOpOperand(Expression e) if (e.op == EXP.arrayLiteral) { Type t = e.type.toBasetype(); - while (t.ty == Tarray || t.ty == Tsarray) + while (t.isStaticOrDynamicArray()) t = t.nextOf().toBasetype(); return (t.ty != Tvoid); } diff --git a/gcc/d/dmd/arraytypes.d b/gcc/d/dmd/arraytypes.d index feab9a4..2cd446e 100644 --- a/gcc/d/dmd/arraytypes.d +++ b/gcc/d/dmd/arraytypes.d @@ -1,12 +1,12 @@ /** * Provide aliases for arrays of certain declarations or statements. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/arraytypes.d, _arraytypes.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/arraytypes.d, _arraytypes.d) * Documentation: https://dlang.org/phobos/dmd_arraytypes.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/arraytypes.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/arraytypes.d */ module dmd.arraytypes; diff --git a/gcc/d/dmd/arraytypes.h b/gcc/d/dmd/arraytypes.h index 7796428..28165f3 100644 --- a/gcc/d/dmd/arraytypes.h +++ b/gcc/d/dmd/arraytypes.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2006-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2006-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/ast_node.d b/gcc/d/dmd/ast_node.d index c8c95fa..ad49169 100644 --- a/gcc/d/dmd/ast_node.d +++ b/gcc/d/dmd/ast_node.d @@ -1,12 +1,12 @@ /** * Defines the base class for all nodes which are part of the AST. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ast_node.d, _ast_node.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ast_node.d, _ast_node.d) * Documentation: https://dlang.org/phobos/dmd_ast_node.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ast_node.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ast_node.d */ module dmd.ast_node; diff --git a/gcc/d/dmd/ast_node.h b/gcc/d/dmd/ast_node.h index db8608e..0782d7e 100644 --- a/gcc/d/dmd/ast_node.h +++ b/gcc/d/dmd/ast_node.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/astenums.d b/gcc/d/dmd/astenums.d index 4fc1514..b71b6c4 100644 --- a/gcc/d/dmd/astenums.d +++ b/gcc/d/dmd/astenums.d @@ -1,11 +1,11 @@ /** * Defines enums common to dmd and dmd as parse library. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/astenums.d, _astenums.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/astenums.d, _astenums.d) * Documentation: https://dlang.org/phobos/dmd_astenums.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/astenums.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/astenums.d */ module dmd.astenums; @@ -18,6 +18,15 @@ enum Sizeok : ubyte done, /// size of aggregate is set correctly } +/// D Language version +enum Edition : ubyte +{ + none, + legacy, /// Before the introduction of editions + v2024, /// Experimental first new edition + latest = v2024 /// Newest edition that this compiler knows of +} + enum Baseok : ubyte { none, /// base classes not computed yet @@ -41,7 +50,7 @@ alias MOD = ubyte; enum STC : ulong // transfer changes to declaration.h { - undefined_ = 0, + none = 0, static_ = 1, /// `static` extern_ = 2, /// `extern` @@ -134,6 +143,9 @@ enum STC : ulong // transfer changes to declaration.h } +// Alias for C++ interface functions which use plain integer instead of enum class, +// since C++ enum class doesn't support | & and conversion to bool. Maybe this can +// be refactored to a struct with operator overloads or bit fields at some point. alias StorageClass = ulong; /******** @@ -293,7 +305,7 @@ enum ThreeState : ubyte enum TRUST : ubyte { default_ = 0, - system = 1, // @system (same as TRUST.default) + system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled) trusted = 2, // @trusted safe = 3, // @safe } @@ -441,6 +453,24 @@ enum FileType : ubyte c, /// C source file } +/// In which context checks for assertions, contracts, bounds checks etc. are enabled +enum CHECKENABLE : ubyte +{ + _default, /// initial value + off, /// never do checking + on, /// always do checking + safeonly, /// do checking only in @safe functions +} + +/// What should happend when an assertion fails +enum CHECKACTION : ubyte +{ + D, /// call D assert on failure + C, /// call C assert on failure + halt, /// cause program halt on failure + context, /// call D assert with the error context on failure +} + extern (C++) struct structalign_t { private: diff --git a/gcc/d/dmd/attrib.d b/gcc/d/dmd/attrib.d index d7d3eca6..1bfe790 100644 --- a/gcc/d/dmd/attrib.d +++ b/gcc/d/dmd/attrib.d @@ -14,12 +14,12 @@ * - Protection (`private`, `public`) * - Deprecated declarations (`@deprecated`) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/attrib.d, _attrib.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/attrib.d, _attrib.d) * Documentation: https://dlang.org/phobos/dmd_attrib.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/attrib.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/attrib.d */ module dmd.attrib; @@ -32,10 +32,8 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; -import dmd.errors; +import dmd.dsymbolsem : include; import dmd.expression; -import dmd.expressionsem; import dmd.func; import dmd.globals; import dmd.hdrgen : visibilityToBuffer; @@ -59,36 +57,29 @@ extern (C++) abstract class AttribDeclaration : Dsymbol extern (D) this(Dsymbols* decl) @safe { + super(DSYM.attribDeclaration); this.decl = decl; } - extern (D) this(const ref Loc loc, Dsymbols* decl) @safe + extern (D) this(Loc loc, Dsymbols* decl) @safe { - super(loc, null); + super(DSYM.attribDeclaration, loc, null); this.decl = decl; } - extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) @safe + extern (D) this(Loc loc, Identifier ident, Dsymbols* decl) @safe { - super(loc, ident); + super(DSYM.attribDeclaration, loc, ident); this.decl = decl; } - Dsymbols* include(Scope* sc) - { - if (errors) - return null; - - return decl; - } - /**************************************** * Create a new scope if one or more given attributes * are different from the sc's. * If the returned scope != sc, the caller should pop * the scope after it used. */ - extern (D) static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, + extern (D) static Scope* createNewScope(Scope* sc, STC stc, LINK linkage, CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility, AlignDeclaration aligndecl, PragmaDeclaration inlining) { @@ -114,48 +105,14 @@ extern (C++) abstract class AttribDeclaration : Dsymbol return sc2; } - /**************************************** - * A hook point to supply scope for members. - * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. - */ - Scope* newScope(Scope* sc) - { - return sc; - } - - override void addComment(const(char)* comment) - { - //printf("AttribDeclaration::addComment %s\n", comment); - if (comment) - { - include(null).foreachDsymbol( s => s.addComment(comment) ); - } - } - override const(char)* kind() const { return "attribute"; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - Dsymbols* d = include(null); - return Dsymbol.oneMembers(d, ps, ident); - } - override final bool hasPointers() { - return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; - } - - override final bool hasStaticCtorOrDtor() - { - return include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; - } - - override final void checkCtorConstInit() - { - include(null).foreachDsymbol( s => s.checkCtorConstInit() ); + return this.include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } /**************************************** @@ -165,11 +122,6 @@ extern (C++) abstract class AttribDeclaration : Dsymbol objc.addSymbols(this, classes, categories); } - override inout(AttribDeclaration) isAttribDeclaration() inout pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -183,18 +135,20 @@ extern (C++) abstract class AttribDeclaration : Dsymbol */ extern (C++) class StorageClassDeclaration : AttribDeclaration { - StorageClass stc; + STC stc; - extern (D) this(StorageClass stc, Dsymbols* decl) @safe + extern (D) this(STC stc, Dsymbols* decl) @safe { super(decl); this.stc = stc; + this.dsym = DSYM.storageClassDeclaration; } - extern (D) this(const ref Loc loc, StorageClass stc, Dsymbols* decl) @safe + extern (D) this(Loc loc, STC stc, Dsymbols* decl) @safe { super(loc, decl); this.stc = stc; + this.dsym = DSYM.storageClassDeclaration; } override StorageClassDeclaration syntaxCopy(Dsymbol s) @@ -203,60 +157,6 @@ extern (C++) class StorageClassDeclaration : AttribDeclaration return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - StorageClass scstc = sc.stc; - /* These sets of storage classes are mutually exclusive, - * so choose the innermost or most recent one. - */ - if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) - scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); - if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) - scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); - if (stc & (STC.const_ | STC.immutable_ | STC.manifest)) - scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); - if (stc & (STC.gshared | STC.shared_)) - scstc &= ~(STC.gshared | STC.shared_); - if (stc & (STC.safe | STC.trusted | STC.system)) - scstc &= ~(STC.safe | STC.trusted | STC.system); - scstc |= stc; - //printf("scstc = x%llx\n", scstc); - return createNewScope(sc, scstc, sc.linkage, sc.cppmangle, - sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); - } - - override final bool oneMember(out Dsymbol ps, Identifier ident) - { - bool t = Dsymbol.oneMembers(decl, ps, ident); - if (t && ps) - { - /* This is to deal with the following case: - * struct Tick { - * template to(T) { const T to() { ... } } - * } - * For eponymous function templates, the 'const' needs to get attached to 'to' - * before the semantic analysis of 'to', so that template overloading based on the - * 'this' pointer can be successful. - */ - FuncDeclaration fd = ps.isFuncDeclaration(); - if (fd) - { - /* Use storage_class2 instead of storage_class otherwise when we do .di generation - * we'll wind up with 'const const' rather than 'const'. - */ - /* Don't think we need to worry about mutually exclusive storage classes here - */ - fd.storage_class2 |= stc; - } - } - return t; - } - - override inout(StorageClassDeclaration) isStorageClassDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -288,25 +188,6 @@ extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); } - /** - * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set - * - * Calls `StorageClassDeclaration.newScope` (as it must be called or copied - * in any function overriding `newScope`), then set the `Scope`'s depdecl. - * - * Returns: - * Always a new scope, to use for this `DeprecatedDeclaration`'s members. - */ - override Scope* newScope(Scope* sc) - { - auto scx = super.newScope(sc); - // The enclosing scope is deprecated as well - if (scx == sc) - scx = sc.push(); - scx.depdecl = this; - return scx; - } - override void accept(Visitor v) { v.visit(this); @@ -323,14 +204,15 @@ extern (C++) final class LinkDeclaration : AttribDeclaration { LINK linkage; /// either explicitly set or `default_` - extern (D) this(const ref Loc loc, LINK linkage, Dsymbols* decl) @safe + extern (D) this(Loc loc, LINK linkage, Dsymbols* decl) @safe { super(loc, null, decl); //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl); this.linkage = linkage; + this.dsym = DSYM.linkDeclaration; } - static LinkDeclaration create(const ref Loc loc, LINK p, Dsymbols* decl) @safe + static LinkDeclaration create(Loc loc, LINK p, Dsymbols* decl) @safe { return new LinkDeclaration(loc, p, decl); } @@ -341,22 +223,6 @@ extern (C++) final class LinkDeclaration : AttribDeclaration return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, - sc.aligndecl, sc.inlining); - } - - override const(char)* toChars() const - { - return toString().ptr; - } - - extern(D) override const(char)[] toString() const - { - return "extern ()"; - } - override void accept(Visitor v) { v.visit(this); @@ -375,11 +241,12 @@ extern (C++) final class CPPMangleDeclaration : AttribDeclaration { CPPMANGLE cppmangle; - extern (D) this(const ref Loc loc, CPPMANGLE cppmangle, Dsymbols* decl) @safe + extern (D) this(Loc loc, CPPMANGLE cppmangle, Dsymbols* decl) @safe { super(loc, null, decl); //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", cppmangle, decl); this.cppmangle = cppmangle; + this.dsym = DSYM.cppMangleDeclaration; } override CPPMangleDeclaration syntaxCopy(Dsymbol s) @@ -388,22 +255,6 @@ extern (C++) final class CPPMangleDeclaration : AttribDeclaration return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, LINK.cpp, cppmangle, sc.visibility, sc.explicitVisibility, - sc.aligndecl, sc.inlining); - } - - override const(char)* toChars() const - { - return toString().ptr; - } - - extern(D) override const(char)[] toString() const - { - return "extern ()"; - } - override void accept(Visitor v) { v.visit(this); @@ -438,21 +289,24 @@ extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration /// CTFE-able expression, resolving to `TupleExp` or `StringExp` Expression exp; - extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) @safe + extern (D) this(Loc loc, Identifier ident, Dsymbols* decl) @safe { super(loc, ident, decl); + this.dsym = DSYM.cppNamespaceDeclaration; } - extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl) @safe + extern (D) this(Loc loc, Expression exp, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.cppNamespaceDeclaration; this.exp = exp; } - extern (D) this(const ref Loc loc, Identifier ident, Expression exp, Dsymbols* decl, + extern (D) this(Loc loc, Identifier ident, Expression exp, Dsymbols* decl, CPPNamespaceDeclaration parent) @safe { super(loc, ident, decl); + this.dsym = DSYM.cppNamespaceDeclaration; this.exp = exp; this.cppnamespace = parent; } @@ -464,34 +318,10 @@ extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace); } - /** - * Returns: - * A copy of the parent scope, with `this` as `namespace` and C++ linkage - */ - override Scope* newScope(Scope* sc) - { - auto scx = sc.copy(); - scx.linkage = LINK.cpp; - scx.namespace = this; - return scx; - } - - override const(char)* toChars() const - { - return toString().ptr; - } - - extern(D) override const(char)[] toString() const - { - return "extern (C++, `namespace`)"; - } - override void accept(Visitor v) { v.visit(this); } - - override inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return this; } } /*********************************************************** @@ -511,9 +341,10 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration * visibility = visibility attribute data * decl = declarations which are affected by this visibility attribute */ - extern (D) this(const ref Loc loc, Visibility visibility, Dsymbols* decl) @safe + extern (D) this(Loc loc, Visibility visibility, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.visibilityDeclaration; this.visibility = visibility; //printf("decl = %p\n", decl); } @@ -524,9 +355,10 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration * pkg_identifiers = list of identifiers for a qualified package name * decl = declarations which are affected by this visibility attribute */ - extern (D) this(const ref Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl) + extern (D) this(Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl) { super(loc, null, decl); + this.dsym = DSYM.visibilityDeclaration; this.visibility.kind = Visibility.Kind.package_; this.pkg_identifiers = pkg_identifiers; if (pkg_identifiers.length > 0) @@ -547,13 +379,6 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - if (pkg_identifiers) - dsymbolSemantic(this, sc); - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.visibility, 1, sc.aligndecl, sc.inlining); - } - override const(char)* kind() const { return "visibility attribute"; @@ -567,11 +392,6 @@ extern (C++) final class VisibilityDeclaration : AttribDeclaration return buf.extractChars(); } - override inout(VisibilityDeclaration) isVisibilityDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -593,9 +413,10 @@ extern (C++) final class AlignDeclaration : AttribDeclaration structalign_t salign; - extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl) + extern (D) this(Loc loc, Expression exp, Dsymbols* decl) { super(loc, null, decl); + this.dsym = DSYM.alignDeclaration; if (exp) { exps = new Expressions(); @@ -603,15 +424,17 @@ extern (C++) final class AlignDeclaration : AttribDeclaration } } - extern (D) this(const ref Loc loc, Expressions* exps, Dsymbols* decl) @safe + extern (D) this(Loc loc, Expressions* exps, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.alignDeclaration; this.exps = exps; } - extern (D) this(const ref Loc loc, structalign_t salign, Dsymbols* decl) @safe + extern (D) this(Loc loc, structalign_t salign, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.alignDeclaration; this.salign = salign; } @@ -623,11 +446,6 @@ extern (C++) final class AlignDeclaration : AttribDeclaration Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, this, sc.inlining); - } - override void accept(Visitor v) { v.visit(this); @@ -645,9 +463,10 @@ extern (C++) final class AnonDeclaration : AttribDeclaration uint anonstructsize; /// size of anonymous struct uint anonalignsize; /// size of anonymous struct for alignment purposes - extern (D) this(const ref Loc loc, bool isunion, Dsymbols* decl) @safe + extern (D) this(Loc loc, bool isunion, Dsymbols* decl) @safe { super(loc, null, decl); + this.dsym = DSYM.anonDeclaration; this.isunion = isunion; } @@ -662,11 +481,6 @@ extern (C++) final class AnonDeclaration : AttribDeclaration return (isunion ? "anonymous union" : "anonymous struct"); } - override inout(AnonDeclaration) isAnonDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -683,9 +497,10 @@ extern (C++) final class PragmaDeclaration : AttribDeclaration { Expressions* args; /// parameters of this pragma - extern (D) this(const ref Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) @safe + extern (D) this(Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) @safe { super(loc, ident, decl); + this.dsym = DSYM.pragmaDeclaration; this.args = args; } @@ -696,17 +511,6 @@ extern (C++) final class PragmaDeclaration : AttribDeclaration return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - if (ident == Id.Pinline) - { - // We keep track of this pragma inside scopes, - // then it's evaluated on demand in function semantic - return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, this); - } - return sc; - } - override const(char)* kind() const { return "pragma"; @@ -729,9 +533,10 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration Condition condition; /// condition deciding whether decl or elsedecl applies Dsymbols* elsedecl; /// array of Dsymbol's for else block - extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe + extern (D) this(Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe { super(loc, null, decl); + this.dsym = DSYM.conditionalDeclaration; //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); this.condition = condition; this.elsedecl = elsedecl; @@ -743,48 +548,6 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration return new ConditionalDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } - override final bool oneMember(out Dsymbol ps, Identifier ident) - { - //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc); - if (condition.inc != Include.notComputed) - { - Dsymbols* d = condition.include(null) ? decl : elsedecl; - return Dsymbol.oneMembers(d, ps, ident); - } - else - { - bool res = (Dsymbol.oneMembers(decl, ps, ident) && ps is null && Dsymbol.oneMembers(elsedecl, ps, ident) && ps is null); - ps = null; - return res; - } - } - - // Decide if 'then' or 'else' code should be included - override Dsymbols* include(Scope* sc) - { - //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); - - if (errors) - return null; - - assert(condition); - return condition.include(_scope ? _scope : sc) ? decl : elsedecl; - } - - override final void addComment(const(char)* comment) - { - /* Because addComment is called by the parser, if we called - * include() it would define a version before it was used. - * But it's no problem to drill down to both decl and elsedecl, - * so that's the workaround. - */ - if (comment) - { - decl .foreachDsymbol( s => s.addComment(comment) ); - elsedecl.foreachDsymbol( s => s.addComment(comment) ); - } - } - override void accept(Visitor v) { v.visit(this); @@ -799,12 +562,13 @@ extern (C++) class ConditionalDeclaration : AttribDeclaration extern (C++) final class StaticIfDeclaration : ConditionalDeclaration { ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted - private bool addisdone = false; /// true if members have been added to scope - private bool onStack = false; /// true if a call to `include` is currently active + bool addisdone = false; /// true if members have been added to scope + bool onStack = false; /// true if a call to `include` is currently active - extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe + extern (D) this(Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) @safe { super(loc, condition, decl, elsedecl); + this.dsym = DSYM.staticIfDeclaration; //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); } @@ -814,52 +578,11 @@ extern (C++) final class StaticIfDeclaration : ConditionalDeclaration return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); } - /**************************************** - * Different from other AttribDeclaration subclasses, include() call requires - * the completion of addMember and setScope phases. - */ - override Dsymbols* include(Scope* sc) - { - //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); - - if (errors || onStack) - return null; - onStack = true; - scope(exit) onStack = false; - - if (sc && condition.inc == Include.notComputed) - { - assert(scopesym); // addMember is already done - assert(_scope); // setScope is already done - Dsymbols* d = ConditionalDeclaration.include(_scope); - if (d && !addisdone) - { - // Add members lazily. - d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); - - // Set the member scopes lazily. - d.foreachDsymbol( s => s.setScope(_scope) ); - - addisdone = true; - } - return d; - } - else - { - return ConditionalDeclaration.include(sc); - } - } - override const(char)* kind() const { return "static if"; } - override inout(StaticIfDeclaration) isStaticIfDeclaration() inout pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -890,6 +613,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration extern (D) this(StaticForeach sfe, Dsymbols* decl) @safe { super(sfe.loc, null, decl); + this.dsym = DSYM.staticForeachDeclaration; this.sfe = sfe; } @@ -901,64 +625,6 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration Dsymbol.arraySyntaxCopy(decl)); } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - // Required to support IFTI on a template that contains a - // `static foreach` declaration. `super.oneMember` calls - // include with a `null` scope. As `static foreach` requires - // the scope for expansion, `oneMember` can only return a - // precise result once `static foreach` has been expanded. - if (cached) - { - return super.oneMember(ps, ident); - } - ps = null; // a `static foreach` declaration may in general expand to multiple symbols - return false; - } - - override Dsymbols* include(Scope* sc) - { - if (errors || onStack) - return null; - if (cached) - { - assert(!onStack); - return cache; - } - onStack = true; - scope(exit) onStack = false; - - if (_scope) - { - sfe.prepare(_scope); // lower static foreach aggregate - } - if (!sfe.ready()) - { - return null; // TODO: ok? - } - - // expand static foreach - import dmd.statementsem: makeTupleForeach; - Dsymbols* d = makeTupleForeach(_scope, true, true, sfe.aggrfe, decl, sfe.needExpansion).decl; - if (d) // process generated declarations - { - // Add members lazily. - d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); - - // Set the member scopes lazily. - d.foreachDsymbol( s => s.setScope(_scope) ); - } - cached = true; - cache = d; - return d; - } - - override void addComment(const(char)* comment) - { - // do nothing - // change this to give semantics to documentation comments on static foreach declarations - } - override const(char)* kind() const { return "static foreach"; @@ -985,7 +651,7 @@ extern (C++) final class StaticForeachDeclaration : AttribDeclaration * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope * } * - * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop + * static assert(!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop * * A StaticForeachDeclaration generates one * ForwardingAttribDeclaration for each expansion of its body. The @@ -1003,30 +669,17 @@ extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration this(Dsymbols* decl) @safe { super(decl); + this.dsym = DSYM.forwardingAttribDeclaration; sym = new ForwardingScopeDsymbol(); sym.symtab = new DsymbolTable(); } - /************************************** - * Use the ForwardingScopeDsymbol as the parent symbol for members. - */ - override Scope* newScope(Scope* sc) - { - return sc.push(sym); - } - - override inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); } } - /*********************************************************** * Mixin declarations, like: * mixin("int x"); @@ -1039,10 +692,11 @@ extern (C++) final class MixinDeclaration : AttribDeclaration ScopeDsymbol scopesym; bool compiled; - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, null, null); //printf("MixinDeclaration(loc = %d)\n", loc.linnum); + this.dsym = DSYM.mixinDeclaration; this.exps = exps; } @@ -1057,11 +711,6 @@ extern (C++) final class MixinDeclaration : AttribDeclaration return "mixin"; } - override inout(MixinDeclaration) isMixinDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1080,6 +729,7 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration extern (D) this(Expressions* atts, Dsymbols* decl) @safe { super(decl); + this.dsym = DSYM.userAttributeDeclaration; this.atts = atts; } @@ -1090,18 +740,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl)); } - override Scope* newScope(Scope* sc) - { - Scope* sc2 = sc; - if (atts && atts.length) - { - // create new one for changes - sc2 = sc.copy(); - sc2.userAttribDecl = this; - } - return sc2; - } - extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2) { Expressions* udas; @@ -1121,21 +759,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration return udas; } - Expressions* getAttributes() - { - if (auto sc = _scope) - { - _scope = null; - arrayExpressionSemantic(atts.peekSlice(), sc); - } - auto exps = new Expressions(); - if (userAttribDecl && userAttribDecl !is this) - exps.push(new TupleExp(Loc.initial, userAttribDecl.getAttributes())); - if (atts && atts.length) - exps.push(new TupleExp(Loc.initial, atts)); - return exps; - } - override const(char)* kind() const { return "UserAttribute"; @@ -1145,70 +768,6 @@ extern (C++) final class UserAttributeDeclaration : AttribDeclaration { v.visit(this); } - - /** - * Check if the provided expression references `core.attribute.gnuAbiTag` - * - * This should be called after semantic has been run on the expression. - * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). - * - * Params: - * e = Expression to check (usually from `UserAttributeDeclaration.atts`) - * - * Returns: - * `true` if the expression references the compiler-recognized `gnuAbiTag` - */ - static bool isGNUABITag(Expression e) - { - if (global.params.cplusplus < CppStdRevision.cpp11) - return false; - - auto ts = e.type ? e.type.isTypeStruct() : null; - if (!ts) - return false; - if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) - return false; - // Can only be defined in druntime - Module m = ts.sym.parent.isModule(); - if (!m || !m.isCoreModule(Id.attribute)) - return false; - return true; - } - - /** - * Called from a symbol's semantic to check if `gnuAbiTag` UDA - * can be applied to them - * - * Directly emits an error if the UDA doesn't work with this symbol - * - * Params: - * sym = symbol to check for `gnuAbiTag` - * linkage = Linkage of the symbol (Declaration.link or sc.link) - */ - static void checkGNUABITag(Dsymbol sym, LINK linkage) - { - if (global.params.cplusplus < CppStdRevision.cpp11) - return; - - foreachUdaNoSemantic(sym, (exp) { - if (isGNUABITag(exp)) - { - if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) - { - .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); - sym.errors = true; - } - else if (linkage != LINK.cpp) - { - .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); - sym.errors = true; - } - // Only one `@gnuAbiTag` is allowed by semantic2 - return 1; // break - } - return 0; // continue - }); - } } /** @@ -1232,43 +791,6 @@ bool isCoreUda(Dsymbol sym, Identifier ident) } /** - * Iterates the UDAs attached to the given symbol. - * - * Params: - * sym = the symbol to get the UDAs from - * sc = scope to use for semantic analysis of UDAs - * dg = called once for each UDA - * - * Returns: - * If `dg` returns `!= 0`, stops the iteration and returns that value. - * Otherwise, returns 0. - */ -int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg) -{ - if (!sym.userAttribDecl) - return 0; - - auto udas = sym.userAttribDecl.getAttributes(); - arrayExpressionSemantic(udas.peekSlice(), sc, true); - - return udas.each!((uda) { - if (!uda.isTupleExp()) - return 0; - - auto exps = uda.isTupleExp().exps; - - return exps.each!((e) { - assert(e); - - if (auto result = dg(e)) - return result; - - return 0; - }); - }); -} - -/** * Iterates the UDAs attached to the given symbol, without performing semantic * analysis. * @@ -1297,7 +819,6 @@ int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg) return 0; } - /** * Returns: true if the given expression is an enum from `core.attribute` named `id` */ diff --git a/gcc/d/dmd/attrib.h b/gcc/d/dmd/attrib.h index 344a7e9..ef37e0a 100644 --- a/gcc/d/dmd/attrib.h +++ b/gcc/d/dmd/attrib.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -17,23 +17,19 @@ class Expression; class Condition; class StaticForeach; +namespace dmd +{ + Expressions *getAttributes(UserAttributeDeclaration *a); +} + /**************************************************************/ class AttribDeclaration : public Dsymbol { public: Dsymbols *decl; // array of Dsymbol's - - virtual Dsymbols *include(Scope *sc); - virtual Scope *newScope(Scope *sc); - void addComment(const utf8_t *comment) override; const char *kind() const override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; bool hasPointers() override final; - bool hasStaticCtorOrDtor() override final; - void checkCtorConstInit() override final; - AttribDeclaration *isAttribDeclaration() override { return this; } - void accept(Visitor *v) override { v->visit(this); } }; @@ -43,9 +39,6 @@ public: StorageClass stc; StorageClassDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override final; - StorageClassDeclaration *isStorageClassDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -57,7 +50,6 @@ public: const char *msgstr; DeprecatedDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -66,10 +58,8 @@ class LinkDeclaration final : public AttribDeclaration public: LINK linkage; - static LinkDeclaration *create(const Loc &loc, LINK p, Dsymbols *decl); + static LinkDeclaration *create(Loc loc, LINK p, Dsymbols *decl); LinkDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -79,8 +69,6 @@ public: CPPMANGLE cppmangle; CPPMangleDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -90,8 +78,6 @@ public: Expression *exp; CPPNamespaceDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -102,10 +88,8 @@ public: DArray<Identifier*> pkg_identifiers; VisibilityDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; const char *toPrettyChars(bool unused) override; - VisibilityDeclaration *isVisibilityDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -116,7 +100,6 @@ public: structalign_t salign; AlignDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -131,7 +114,6 @@ public: AnonDeclaration *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - AnonDeclaration *isAnonDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -141,7 +123,6 @@ public: Expressions *args; // array of Expression's PragmaDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -153,9 +134,6 @@ public: Dsymbols *elsedecl; // array of Dsymbol's for else block ConditionalDeclaration *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override final; - Dsymbols *include(Scope *sc) override; - void addComment(const utf8_t *comment) override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -167,8 +145,6 @@ public: d_bool onStack; StaticIfDeclaration *syntaxCopy(Dsymbol *s) override; - Dsymbols *include(Scope *sc) override; - StaticIfDeclaration *isStaticIfDeclaration() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -183,9 +159,6 @@ public: Dsymbols *cache; StaticForeachDeclaration *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; - Dsymbols *include(Scope *sc) override; - void addComment(const utf8_t *comment) override; const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -195,8 +168,6 @@ class ForwardingAttribDeclaration final : public AttribDeclaration public: ForwardingScopeDsymbol *sym; - Scope *newScope(Scope *sc) override; - ForwardingAttribDeclaration *isForwardingAttribDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -225,8 +196,6 @@ public: Expressions *atts; UserAttributeDeclaration *syntaxCopy(Dsymbol *s) override; - Scope *newScope(Scope *sc) override; - Expressions *getAttributes(); const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/attribsem.d b/gcc/d/dmd/attribsem.d new file mode 100644 index 0000000..bc966bb --- /dev/null +++ b/gcc/d/dmd/attribsem.d @@ -0,0 +1,87 @@ +/** + * Does semantic analysis for attributes. + * + * The term 'attribute' refers to things that can apply to a larger scope than a single declaration. + * Among them are: + * - Alignment (`align(8)`) + * - User defined attributes (`@UDA`) + * - Function Attributes (`@safe`) + * - Storage classes (`static`, `__gshared`) + * - Mixin declarations (`mixin("int x;")`) + * - Conditional compilation (`static if`, `static foreach`) + * - Linkage (`extern(C)`) + * - Anonymous structs / unions + * - Protection (`private`, `public`) + * - Deprecated declarations (`@deprecated`) + * + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/attribsem.d, _attrib.d) + * Documentation: https://dlang.org/phobos/dmd_attribsem.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/attribsem.d + */ + +module dmd.attribsem; + +import dmd.arraytypes; +import dmd.attrib; +import dmd.dscope; +import dmd.dsymbol; +import dmd.expression; +import dmd.expressionsem; +import dmd.location; +import dmd.root.array; // for each + + +Expressions* getAttributes(UserAttributeDeclaration a) +{ + if (auto sc = a._scope) + { + a._scope = null; + arrayExpressionSemantic(a.atts.peekSlice(), sc); + } + auto exps = new Expressions(); + if (a.userAttribDecl && a.userAttribDecl !is a) + exps.push(new TupleExp(Loc.initial, a.userAttribDecl.getAttributes())); + if (a.atts && a.atts.length) + exps.push(new TupleExp(Loc.initial, a.atts)); + return exps; +} + +/** + * Iterates the UDAs attached to the given symbol. + * + * Params: + * sym = the symbol to get the UDAs from + * sc = scope to use for semantic analysis of UDAs + * dg = called once for each UDA + * + * Returns: + * If `dg` returns `!= 0`, stops the iteration and returns that value. + * Otherwise, returns 0. + */ +int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg) +{ + if (!sym.userAttribDecl) + return 0; + + auto udas = sym.userAttribDecl.getAttributes(); + arrayExpressionSemantic(udas.peekSlice(), sc, true); + + return udas.each!((uda) { + if (!uda.isTupleExp()) + return 0; + + auto exps = uda.isTupleExp().exps; + + return exps.each!((e) { + assert(e); + + if (auto result = dg(e)) + return result; + + return 0; + }); + }); +} diff --git a/gcc/d/dmd/blockexit.d b/gcc/d/dmd/blockexit.d index d77af7e..ae2f12f 100644 --- a/gcc/d/dmd/blockexit.d +++ b/gcc/d/dmd/blockexit.d @@ -1,12 +1,12 @@ /** * Find out in what ways control flow can exit a statement block. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/blockexit.d, _blockexit.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/blockexit.d, _blockexit.d) * Documentation: https://dlang.org/phobos/dmd_blockexit.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/blockexit.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/blockexit.d */ module dmd.blockexit; @@ -80,26 +80,25 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) void visitExp(ExpStatement s) { result = BE.fallthru; - if (s.exp) + if (!s.exp) + return; + + if (s.exp.op == EXP.halt) { - if (s.exp.op == EXP.halt) + result = BE.halt; + return; + } + if (AssertExp a = s.exp.isAssertExp()) + { + if (a.e1.toBool().hasValue(false)) // if it's an assert(0) { result = BE.halt; return; } - if (AssertExp a = s.exp.isAssertExp()) - { - if (a.e1.toBool().hasValue(false)) // if it's an assert(0) - { - result = BE.halt; - return; - } - } - if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn()) - result = BE.halt; - - result |= canThrow(s.exp, func, eSink); } + if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn()) + result = BE.halt; + result |= canThrow(s.exp, func, eSink); } void visitDtorExp(DtorExpStatement s) @@ -120,44 +119,39 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) Statement slast = null; foreach (s; *cs.statements) { - if (s) + if (!s) + continue; + + //printf("result = x%x\n", result); + //printf("s: %s\n", s.toChars()); + if (result & BE.fallthru && slast) { - //printf("result = x%x\n", result); - //printf("s: %s\n", s.toChars()); - if (result & BE.fallthru && slast) + slast = slast.last(); + if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement())) { - slast = slast.last(); - if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement())) + // Allow if last case/default was empty + CaseStatement sc = slast.isCaseStatement(); + DefaultStatement sd = slast.isDefaultStatement(); + auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); + + if (sl && (!sl.hasCode() || sl.isErrorStatement())) { - // Allow if last case/default was empty - CaseStatement sc = slast.isCaseStatement(); - DefaultStatement sd = slast.isDefaultStatement(); - auto sl = (sc ? sc.statement : (sd ? sd.statement : null)); - - if (sl && (!sl.hasCode() || sl.isErrorStatement())) - { - } - else if (func.getModule().filetype != FileType.c) - { - const(char)* gototype = s.isCaseStatement() ? "case" : "default"; - // @@@DEPRECATED_2.110@@@ https://issues.dlang.org/show_bug.cgi?id=22999 - // Deprecated in 2.100 - // Make an error in 2.110 - if (sl && sl.isCaseStatement()) - global.errorSink.deprecation(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); - else - global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); - } + } + else if (func.getModule().filetype != FileType.c) + { + const(char)* gototype = s.isCaseStatement() ? "case" : "default"; + // https://issues.dlang.org/show_bug.cgi?id=22999 + global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype); } } + } - if ((result & BE.fallthru) || s.comeFrom()) - { - result &= ~BE.fallthru; - result |= blockExit(s, func, eSink); - } - slast = s; + if ((result & BE.fallthru) || s.comeFrom()) + { + result &= ~BE.fallthru; + result |= blockExit(s, func, eSink); } + slast = s; } } @@ -166,13 +160,12 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) result = BE.fallthru; foreach (s; *uls.statements) { - if (s) - { - int r = blockExit(s, func, eSink); - result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru); - if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0) - result &= ~BE.fallthru; - } + if (!s) + continue; + int r = blockExit(s, func, eSink); + result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru); + if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0) + result &= ~BE.fallthru; } } @@ -485,7 +478,7 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) if (!(s.stc & STC.nothrow_)) { if(func) - func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); + func.setThrow(s.loc, "executing an `asm` statement without a `nothrow` annotation"); if (eSink) eSink.error(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO else @@ -517,7 +510,7 @@ int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink) + + Returns: `BE.[err]throw` depending on the type of `exp` +/ -BE checkThrow(ref const Loc loc, Expression exp, FuncDeclaration func, ErrorSink eSink) +BE checkThrow(Loc loc, Expression exp, FuncDeclaration func, ErrorSink eSink) { Type t = exp.type.toBasetype(); ClassDeclaration cd = t.isClassHandle(); @@ -530,7 +523,7 @@ BE checkThrow(ref const Loc loc, Expression exp, FuncDeclaration func, ErrorSink if (eSink) eSink.error(loc, "`%s` is thrown but not caught", exp.type.toChars()); else if (func) - func.setThrow(loc, "`%s` is thrown but not caught", exp.type); + func.setThrow(loc, "`%s` being thrown but not caught", exp.type); return BE.throw_; } diff --git a/gcc/d/dmd/builtin.d b/gcc/d/dmd/builtin.d index d29092b..53307fc 100644 --- a/gcc/d/dmd/builtin.d +++ b/gcc/d/dmd/builtin.d @@ -3,12 +3,12 @@ * * Currently includes functions from `std.math`, `core.math` and `core.bitop`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/builtin.d, _builtin.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/builtin.d, _builtin.d) * Documentation: https://dlang.org/phobos/dmd_builtin.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/builtin.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/builtin.d */ module dmd.builtin; @@ -28,4 +28,4 @@ public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd); * Evaluate builtin function. * Return result; NULL if cannot evaluate it. */ -public extern (C++) Expression eval_builtin(const ref Loc loc, FuncDeclaration fd, Expressions* arguments); +public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments); diff --git a/gcc/d/dmd/canthrow.d b/gcc/d/dmd/canthrow.d index 31155f1..96acf05 100644 --- a/gcc/d/dmd/canthrow.d +++ b/gcc/d/dmd/canthrow.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d, _canthrow.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/canthrow.d, _canthrow.d) * Documentation: https://dlang.org/phobos/dmd_canthrow.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/canthrow.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/canthrow.d */ module dmd.canthrow; @@ -20,15 +20,17 @@ import dmd.astenums; import dmd.blockexit : BE, checkThrow; import dmd.declaration; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.errorsink; import dmd.expression; +import dmd.expressionsem : errorSupplementalInferredAttr; import dmd.func; import dmd.globals; import dmd.init; import dmd.mtype; -import dmd.postordervisitor; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /** * Status indicating what kind of throwable might be caused by an expression. @@ -72,16 +74,16 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) void checkFuncThrows(Expression e, FuncDeclaration f) { auto tf = f.type.toBasetype().isTypeFunction(); - if (tf && !tf.isnothrow) + if (tf && !tf.isNothrow) { if (eSink) { eSink.error(e.loc, "%s `%s` is not `nothrow`", f.kind(), f.toPrettyChars()); if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, 10, false, STC.nothrow_); + errorSupplementalInferredAttr(f, 10, false, STC.nothrow_, eSink); import dmd.expressionsem : checkOverriddenDtor; - f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isnothrow, "not nothrow"); + f.checkOverriddenDtor(null, e.loc, dd => dd.type.toTypeFunction().isNothrow, "not nothrow"); } else if (func) { @@ -111,11 +113,9 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) if (ce.f && ce.arguments.length > 0) { Type tb = (*ce.arguments)[0].type.toBasetype(); - auto tbNext = tb.nextOf(); - if (tbNext) + if (auto tbNext = tb.nextOf()) { - auto ts = tbNext.baseElemOf().isTypeStruct(); - if (ts) + if (auto ts = tbNext.baseElemOf().isTypeStruct()) { auto sd = ts.sym; const id = ce.f.ident; @@ -135,7 +135,7 @@ CT canThrow(Expression e, FuncDeclaration func, ErrorSink eSink) if (ce.f && ce.f == func) return; const tf = ce.calledFunctionType(); - if (tf && tf.isnothrow) + if (tf && tf.isNothrow) return; if (ce.f) diff --git a/gcc/d/dmd/chkformat.d b/gcc/d/dmd/chkformat.d index 5024f9b..8b2d5b4 100644 --- a/gcc/d/dmd/chkformat.d +++ b/gcc/d/dmd/chkformat.d @@ -1,12 +1,12 @@ /** * Check the arguments to `printf` and `scanf` against the `format` string. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/chkformat.d, _chkformat.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/chkformat.d, _chkformat.d) * Documentation: https://dlang.org/phobos/dmd_chkformat.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/chkformat.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/chkformat.d */ module dmd.chkformat; @@ -21,6 +21,7 @@ import dmd.globals; import dmd.identifier; import dmd.location; import dmd.mtype; +import dmd.typesem; import dmd.target; @@ -62,7 +63,7 @@ import dmd.target; * https://www.cplusplus.com/reference/cstdio/printf/ */ public -bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) +bool checkPrintfFormat(Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) { //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr); size_t n; // index in args @@ -173,13 +174,13 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre case Format.lu: // unsigned long int case Format.ld: // long int - if (!(t.isintegral() && t.size() == c_longsize)) + if (!(t.isIntegral() && t.size() == c_longsize)) { if (fmt == Format.lu) errorMsg(null, e, (c_longsize == 4 ? "uint" : "ulong"), t); else errorMsg(null, e, (c_longsize == 4 ? "int" : "long"), t); - if (t.isintegral() && t.size() != c_longsize) + if (t.isIntegral() && t.size() != c_longsize) eSink.errorSupplemental(e.loc, "C `long` is %d bytes on your system", c_longsize); } break; @@ -202,12 +203,12 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.zd: // size_t - if (!(t.isintegral() && t.size() == ptrsize)) + if (!(t.isIntegral() && t.size() == ptrsize)) errorMsg(null, e, "size_t", t); break; case Format.td: // ptrdiff_t - if (!(t.isintegral() && t.size() == ptrsize)) + if (!(t.isIntegral() && t.size() == ptrsize)) errorMsg(null, e, "ptrdiff_t", t); break; @@ -233,7 +234,7 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.ln: // pointer to long int - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.size() == c_longsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.size() == c_longsize)) errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t); break; @@ -258,12 +259,12 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre break; case Format.zn: // pointer to size_t - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "size_t*", t); break; case Format.tn: // pointer to ptrdiff_t - if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && !tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "ptrdiff_t*", t); break; @@ -338,7 +339,7 @@ bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre * https://www.cplusplus.com/reference/cstdio/scanf/ */ public -bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) +bool checkScanfFormat(Loc loc, scope const char[] format, scope Expression[] args, bool isVa_list, ErrorSink eSink) { size_t n = 0; for (size_t i = 0; i < format.length;) @@ -413,7 +414,7 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres case Format.ln: case Format.ld: // pointer to long int - if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == c_longsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && !tnext.isUnsigned() && tnext.size() == c_longsize)) errorMsg(null, e, (c_longsize == 4 ? "int*" : "long*"), t); break; @@ -431,13 +432,13 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres case Format.zn: case Format.zd: // pointer to size_t - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "size_t*", t); break; case Format.tn: case Format.td: // pointer to ptrdiff_t - if (!(t.ty == Tpointer && tnext.isintegral() && !tnext.isunsigned() && tnext.size() == ptrsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && !tnext.isUnsigned() && tnext.size() == ptrsize)) errorMsg(null, e, "ptrdiff_t*", t); break; @@ -457,7 +458,7 @@ bool checkScanfFormat(ref const Loc loc, scope const char[] format, scope Expres break; case Format.lu: // pointer to unsigned long int - if (!(t.ty == Tpointer && tnext.isintegral() && tnext.isunsigned() && tnext.size() == c_longsize)) + if (!(t.ty == Tpointer && tnext.isIntegral() && tnext.isUnsigned() && tnext.size() == c_longsize)) errorMsg(null, e, (c_longsize == 4 ? "uint*" : "ulong*"), t); break; diff --git a/gcc/d/dmd/clone.d b/gcc/d/dmd/clone.d index 2e4833e..a21f5a0 100644 --- a/gcc/d/dmd/clone.d +++ b/gcc/d/dmd/clone.d @@ -2,12 +2,12 @@ * Builds struct member functions if needed and not defined by the user. * Includes `opEquals`, `opAssign`, post blit, copy constructor and destructor. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/clone.d, _clone.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/clone.d, _clone.d) * Documentation: https://dlang.org/phobos/dmd_clone.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/clone.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/clone.d */ module dmd.clone; @@ -51,13 +51,13 @@ import dmd.tokens; * Returns: * merged storage class */ -StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure +STC mergeFuncAttrs(STC s1, const FuncDeclaration f) pure @safe { if (!f) return s1; - StorageClass s2 = (f.storage_class & STC.disable); + STC s2 = (f.storage_class & STC.disable); - TypeFunction tf = cast(TypeFunction)f.type; + auto tf = f.type.isTypeFunction(); if (tf.trust == TRUST.safe) s2 |= STC.safe; else if (tf.trust == TRUST.system) @@ -67,15 +67,15 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure if (tf.purity != PURE.impure) s2 |= STC.pure_; - if (tf.isnothrow) + if (tf.isNothrow) s2 |= STC.nothrow_; - if (tf.isnogc) + if (tf.isNogc) s2 |= STC.nogc; const sa = s1 & s2; const so = s1 | s2; - StorageClass stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable); + STC stc = (sa & (STC.pure_ | STC.nothrow_ | STC.nogc)) | (so & STC.disable); if (so & STC.system) stc |= STC.system; @@ -96,56 +96,60 @@ StorageClass mergeFuncAttrs(StorageClass s1, const FuncDeclaration f) pure * sc = current scope * Returns: * if found, returns FuncDeclaration of opAssign, otherwise null + * References: + * https://dlang.org/spec/operatoroverloading.html#assignment */ FuncDeclaration hasIdentityOpAssign(AggregateDeclaration ad, Scope* sc) { - Dsymbol assign = search_function(ad, Id.assign); - if (assign) - { - /* check identity opAssign exists - */ - scope er = new NullExp(ad.loc, ad.type); // dummy rvalue - scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue - el.type = ad.type; - auto a = new Expressions(1); - const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. - sc = sc.push(); - sc.tinst = null; - sc.minst = null; + Dsymbol assign = search_function(ad, Id.opAssign); + if (!assign) + return null; - (*a)[0] = er; - auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); - if (!f) - { - (*a)[0] = el; - f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); - } + /* check identity opAssign exists + */ + scope er = new NullExp(ad.loc, ad.type); // dummy rvalue + scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + el.type = ad.type; + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. + sc = sc.push(); + sc.tinst = null; + sc.minst = null; - sc = sc.pop(); - global.endGagging(errors); - if (f) - { - if (f.errors) - return null; - auto fparams = f.getParameterList(); - if (fparams.length) - { - auto fparam0 = fparams[0]; - if (fparam0.type.toDsymbol(null) != ad) - f = null; - } - } - // BUGS: This detection mechanism cannot find some opAssign-s like follows: - // struct S { void opAssign(ref immutable S) const; } - return f; + auto a = new Expressions(1); + (*a)[0] = er; + auto f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); + if (!f) + { + (*a)[0] = el; + f = resolveFuncCall(ad.loc, sc, assign, null, ad.type, ArgumentList(a), FuncResolveFlag.quiet); } - return null; + + sc = sc.pop(); + global.endGagging(errors); + if (!f) + return null; + if (f.errors) + return null; + auto fparams = f.getParameterList(); + if (fparams.length) + { + auto fparam0 = fparams[0]; + if (fparam0.type.toDsymbol(null) != ad) + f = null; + } + // BUGS: This detection mechanism cannot find some opAssign-s like follows: + // struct S { void opAssign(ref immutable S) const; } + return f; } /******************************************* * We need an opAssign for the struct if * it has a destructor or a postblit. - * We need to generate one if a user-specified one does not exist. + * (We will later generate one if a user-specified one does not exist) + * Params: + * sd = struct to check + * Returns: + * true if an opAssign is needed */ private bool needOpAssign(StructDeclaration sd) { @@ -175,9 +179,8 @@ private bool needOpAssign(StructDeclaration sd) if (v.overlapped) // if field of a union continue; // user must handle it themselves Type tv = v.type.baseElemOf(); - if (tv.ty == Tstruct) + if (auto ts = tv.isTypeStruct()) { - TypeStruct ts = cast(TypeStruct)tv; if (ts.sym.isUnionDeclaration()) continue; if (needOpAssign(ts.sym)) @@ -251,7 +254,7 @@ private bool needOpAssign(StructDeclaration sd) * sd = struct to generate opAssign for * sc = context * Returns: - * generated `opAssign` function + * generated `opAssign` function, or null if it is not needed */ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) { @@ -266,7 +269,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) return null; //printf("StructDeclaration::buildOpAssign() %s\n", sd.toChars()); - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stc = STC.safe; Loc declLoc = sd.loc; Loc loc; // internal code should have no loc to prevent coverage @@ -281,10 +284,10 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) if (v.overlapped) continue; Type tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - StructDeclaration sdv = (cast(TypeStruct)tv).sym; - stc = mergeFuncAttrs(stc, hasIdentityOpAssign(sdv, sc)); + if (auto tvs = tv.isTypeStruct()) + { + stc = mergeFuncAttrs(stc, hasIdentityOpAssign(tvs.sym, sc)); + } } if (sd.dtor || sd.postblit) @@ -300,7 +303,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) auto fparams = new Parameters(); fparams.push(new Parameter(loc, STC.nodtor, sd.type, Id.p, null, null)); auto tf = new TypeFunction(ParameterList(fparams), sd.handleType(), LINK.d, stc | STC.ref_); - auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.assign, stc, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, Id.opAssign, stc, tf); fop.storage_class |= STC.inference; fop.isGenerated = true; Expression e; @@ -314,8 +317,7 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) else if (sd.dtor) { //printf("\tswap copy\n"); - TypeFunction tdtor = cast(TypeFunction)sd.dtor.type; - assert(tdtor.ty == Tfunction); + auto tdtor = sd.dtor.type.isTypeFunction(); auto idswap = Identifier.generateId("__swap"); auto swap = new VarDeclaration(loc, sd.type, idswap, new VoidInitializer(loc)); @@ -378,14 +380,14 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) auto er = new ThisExp(loc); Statement s2 = new ReturnStatement(loc, er); fop.fbody = new CompoundStatement(loc, s1, s2); - tf.isreturn = true; + tf.isReturn = true; } sd.members.push(fop); fop.addMember(sc, sd); sd.hasIdentityAssign = true; // temporary mark identity assignable const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it. Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -407,21 +409,34 @@ FuncDeclaration buildOpAssign(StructDeclaration sd, Scope* sc) /******************************************* * We need an opEquals for the struct if - * any fields has an opEquals. - * Generate one if a user-specified one does not exist. + * any field has an opEquals and a user-specified one does not exist. + * Params: + * sd = struct to check + * Returns: + * true if need to generate one */ bool needOpEquals(StructDeclaration sd) { + bool dontneed() + { + //printf("\tdontneed\n"); + return false; + } + bool need() + { + //printf("\tneed\n"); + return true; + } //printf("StructDeclaration::needOpEquals() %s\n", sd.toChars()); if (sd.isUnionDeclaration()) { /* If a union has only one field, treat it like a struct */ if (sd.fields.length != 1) - goto Ldontneed; + return dontneed(); } if (sd.hasIdentityEquals) - goto Lneed; + return need(); /* If any of the fields has an opEquals, then we * need it too. */ @@ -433,84 +448,84 @@ bool needOpEquals(StructDeclaration sd) continue; Type tv = v.type.toBasetype(); auto tvbase = tv.baseElemOf(); - if (tvbase.ty == Tstruct) + if (auto ts = tvbase.isTypeStruct()) { - TypeStruct ts = cast(TypeStruct)tvbase; if (ts.sym.isUnionDeclaration() && ts.sym.fields.length != 1) continue; if (needOpEquals(ts.sym)) - goto Lneed; + return need(); } - if (tvbase.isfloating()) + if (tvbase.isFloating()) { // This is necessray for: // 1. comparison of +0.0 and -0.0 should be true. // 2. comparison of NANs should be false always. - goto Lneed; + return need(); } if (tvbase.ty == Tarray) - goto Lneed; + return need(); if (tvbase.ty == Taarray) - goto Lneed; + return need(); if (tvbase.ty == Tclass) - goto Lneed; + return need(); } -Ldontneed: - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return dontneed(); } /******************************************* * Check given aggregate actually has an identity opEquals or not. + * ad = aggregate to check + * sc = context + * Returns: + * identity opEquals if it is there, null if not */ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) { FuncDeclaration f; - if (Dsymbol eq = search_function(ad, Id.eq)) + Dsymbol eq = search_function(ad, Id.opEquals); + if (!eq) + return null; + + /* check identity opEquals exists + */ + scope er = new NullExp(ad.loc, null); // dummy rvalue + scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue + auto a = new Expressions(1); + + bool hasIt(Type tthis) { - /* check identity opEquals exists - */ - scope er = new NullExp(ad.loc, null); // dummy rvalue - scope el = new IdentifierExp(ad.loc, Id.p); // dummy lvalue - auto a = new Expressions(1); + const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it + sc = sc.push(); + sc.tinst = null; + sc.minst = null; - bool hasIt(Type tthis) + FuncDeclaration rfc(Expression e) { - const errors = global.startGagging(); // Do not report errors, even if the template opAssign fbody makes it - sc = sc.push(); - sc.tinst = null; - sc.minst = null; - - FuncDeclaration rfc(Expression e) - { - (*a)[0] = e; - (*a)[0].type = tthis; - return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet); - } + (*a)[0] = e; + (*a)[0].type = tthis; + return resolveFuncCall(ad.loc, sc, eq, null, tthis, ArgumentList(a), FuncResolveFlag.quiet); + } - f = rfc(er); - if (!f) - f = rfc(el); + f = rfc(er); + if (!f) + f = rfc(el); - sc = sc.pop(); - global.endGagging(errors); + sc = sc.pop(); + global.endGagging(errors); - return f !is null; - } + return f !is null; + } - if (hasIt(ad.type) || - hasIt(ad.type.constOf()) || - hasIt(ad.type.immutableOf()) || - hasIt(ad.type.sharedOf()) || - hasIt(ad.type.sharedConstOf())) - { - if (f.errors) - return null; - } + if (hasIt(ad.type) || + hasIt(ad.type.constOf()) || + hasIt(ad.type.immutableOf()) || + hasIt(ad.type.sharedOf()) || + hasIt(ad.type.sharedConstOf())) + { + if (f.errors) + return null; } + return f; } @@ -522,7 +537,7 @@ private FuncDeclaration hasIdentityOpEquals(AggregateDeclaration ad, Scope* sc) * opEquals is changed to be never implicitly generated. * Now, struct objects comparison s1 == s2 is translated to: * s1.tupleof == s2.tupleof - * to calculate structural equality. See EqualExp.op_overload. + * to calculate structural equality. See `opOverloadEquals`. */ FuncDeclaration buildOpEquals(StructDeclaration sd, Scope* sc) { @@ -549,7 +564,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) return null; // bitwise comparison would work //printf("StructDeclaration::buildXopEquals() %s\n", sd.toChars()); - if (Dsymbol eq = search_function(sd, Id.eq)) + if (Dsymbol eq = search_function(sd, Id.opEquals)) { if (FuncDeclaration fd = eq.isFuncDeclaration()) { @@ -563,7 +578,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); tfeqptr = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d); tfeqptr.mod = MODFlags.const_; - tfeqptr = cast(TypeFunction)tfeqptr.typeSemantic(Loc.initial, &scx); + tfeqptr = tfeqptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); } fd = fd.overloadExactMatch(tfeqptr); if (fd) @@ -592,16 +607,16 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) auto tf = new TypeFunction(ParameterList(parameters), Type.tbool, LINK.d, STC.const_); tf = tf.addSTC(STC.const_).toTypeFunction(); Identifier id = Id.xopEquals; - auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.none, tf); fop.isGenerated = true; fop.parent = sd; Expression e1 = new IdentifierExp(loc, Id.This); Expression e2 = new IdentifierExp(loc, Id.p); Expression e = new EqualExp(EXP.equal, loc, e1, e2); fop.fbody = new ReturnStatement(loc, e); - uint errors = global.startGagging(); // Do not report errors + const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -624,7 +639,7 @@ FuncDeclaration buildXopEquals(StructDeclaration sd, Scope* sc) FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) { //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); - if (Dsymbol cmp = search_function(sd, Id.cmp)) + if (Dsymbol cmp = search_function(sd, Id.opCmp)) { if (FuncDeclaration fd = cmp.isFuncDeclaration()) { @@ -638,11 +653,10 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) parameters.push(new Parameter(Loc.initial, STC.ref_ | STC.const_, sd.type, null, null, null)); tfcmpptr = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d); tfcmpptr.mod = MODFlags.const_; - tfcmpptr = cast(TypeFunction)tfcmpptr.typeSemantic(Loc.initial, &scx); + tfcmpptr = tfcmpptr.typeSemantic(Loc.initial, &scx).isTypeFunction(); } - fd = fd.overloadExactMatch(tfcmpptr); - if (fd) - return fd; + if (auto fdo = fd.overloadExactMatch(tfcmpptr)) + return fdo; } } else @@ -653,7 +667,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) * Consider 'alias this', but except opDispatch. */ Expression e = new DsymbolExp(sd.loc, sd); - e = new DotIdExp(sd.loc, e, Id.cmp); + e = new DotIdExp(sd.loc, e, Id.opCmp); Scope* sc2 = sc.push(); e = e.trySemantic(sc2); sc2.pop(); @@ -674,7 +688,7 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) default: break; } - if (!s || s.ident != Id.cmp) + if (!s || s.ident != Id.opCmp) e = null; // there's no valid member 'opCmp' } if (!e) @@ -717,16 +731,16 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) auto tf = new TypeFunction(ParameterList(parameters), Type.tint32, LINK.d, STC.const_); tf = tf.addSTC(STC.const_).toTypeFunction(); Identifier id = Id.xopCmp; - auto fop = new FuncDeclaration(declLoc, Loc.initial, id, 0, tf); + auto fop = new FuncDeclaration(declLoc, Loc.initial, id, STC.none, tf); fop.isGenerated = true; fop.parent = sd; Expression e1 = new IdentifierExp(loc, Id.This); Expression e2 = new IdentifierExp(loc, Id.p); - Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.cmp), e2); + Expression e = new CallExp(loc, new DotIdExp(loc, e1, Id.opCmp), e2); fop.fbody = new ReturnStatement(loc, e); - uint errors = global.startGagging(); // Do not report errors + const errors = global.startGagging(); // Do not report errors Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -739,15 +753,31 @@ FuncDeclaration buildXopCmp(StructDeclaration sd, Scope* sc) /******************************************* * We need a toHash for the struct if * any fields has a toHash. - * Generate one if a user-specified one does not exist. + * (will generate one if a user-specified one does not exist) + * Params: + * sd = struct to check + * Returns: + * need to generate toHash() + * References: + * https://dlang.org/spec/hash-map.html#using_struct_as_key */ private bool needToHash(StructDeclaration sd) { + bool dontneed() + { + //printf("\tdontneed\n"); + return false; + } + bool need() + { + //printf("\tneed\n"); + return true; + } //printf("StructDeclaration::needToHash() %s\n", sd.toChars()); if (sd.isUnionDeclaration()) - goto Ldontneed; + return dontneed(); if (sd.xhash) - goto Lneed; + return need(); /* If any of the fields has an toHash, then we * need it too. @@ -760,34 +790,28 @@ private bool needToHash(StructDeclaration sd) continue; Type tv = v.type.toBasetype(); auto tvbase = tv.baseElemOf(); - if (tvbase.ty == Tstruct) + if (auto ts = tvbase.isTypeStruct()) { - TypeStruct ts = cast(TypeStruct)tvbase; if (ts.sym.isUnionDeclaration()) continue; if (needToHash(ts.sym)) - goto Lneed; + return need(); } - if (tvbase.isfloating()) + if (tvbase.isFloating()) { /* This is necessary because comparison of +0.0 and -0.0 should be true, * i.e. not a bit compare. */ - goto Lneed; + return need(); } if (tvbase.ty == Tarray) - goto Lneed; + return need(); if (tvbase.ty == Taarray) - goto Lneed; + return need(); if (tvbase.ty == Tclass) - goto Lneed; + return need(); } -Ldontneed: - //printf("\tdontneed\n"); - return false; -Lneed: - //printf("\tneed\n"); - return true; + return dontneed(); } /****************************************** @@ -803,13 +827,12 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) { tftohash = new TypeFunction(ParameterList(), Type.thash_t, LINK.d); tftohash.mod = MODFlags.const_; - tftohash = cast(TypeFunction)tftohash.merge(); + tftohash = tftohash.merge().isTypeFunction(); } if (FuncDeclaration fd = s.isFuncDeclaration()) { - fd = fd.overloadExactMatch(tftohash); - if (fd) - return fd; + if (auto fdo = fd.overloadExactMatch(tftohash)) + return fdo; } } if (!needToHash(sd)) @@ -825,7 +848,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) * Note that it would only be necessary if it has floating point fields. * For now, we'll just not generate a toHash() for C files. */ - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return null; //printf("StructDeclaration::buildXtoHash() %s\n", sd.toPrettyChars()); @@ -854,7 +877,7 @@ FuncDeclaration buildXtoHash(StructDeclaration sd, Scope* sc) "return h;"; fop.fbody = new MixinStatement(loc, new StringExp(loc, code)); Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; fop.dsymbolSemantic(sc2); fop.semantic2(sc2); @@ -882,7 +905,7 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) if (ad.isUnionDeclaration()) return; // unions don't have destructors - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; Loc declLoc = ad.userDtors.length ? ad.userDtors[0].loc : ad.loc; Loc loc; // internal code should have no loc to prevent coverage FuncDeclaration xdtor_fwd = null; @@ -900,10 +923,10 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) continue; if (v.overlapped) continue; - auto tv = v.type.baseElemOf(); - if (tv.ty != Tstruct) + auto tvs = v.type.baseElemOf().isTypeStruct(); + if (!tvs) continue; - auto sdv = (cast(TypeStruct)tv).sym; + auto sdv = tvs.sym; if (!sdv.dtor) continue; @@ -925,8 +948,8 @@ void buildDtors(AggregateDeclaration ad, Scope* sc) } Expression ex; - tv = v.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = v.type.toBasetype(); + if (tv.isTypeStruct()) { // this.v.__xdtor() @@ -1086,7 +1109,7 @@ private DtorDeclaration buildWindowsCppDtor(AggregateDeclaration ad, DtorDeclara // // TODO: if (del) delete (char*)this; // return (void*) this; // } - Parameter delparam = new Parameter(Loc.initial, STC.undefined_, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); + Parameter delparam = new Parameter(Loc.initial, STC.none, Type.tuns32, Identifier.idPool("del"), new IntegerExp(dtor.loc, 0, Type.tuns32), null); Parameters* params = new Parameters; params.push(delparam); const stc = dtor.storage_class & ~STC.scope_; // because we add the `return this;` later @@ -1179,6 +1202,14 @@ private DtorDeclaration buildExternDDtor(AggregateDeclaration ad, Scope* sc) * invs[0](), invs[1](), ...; * } * --- + * Params: + * ad = aggregate for creating invariant + * sc = context + * Returns: + * generated invariant, null if not needed + * References: + * https://dlang.org/spec/class.html#invariants + * https://dlang.org/spec/struct.html#Invariant */ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) { @@ -1193,8 +1224,8 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) default: Expression e = null; - StorageClass stcx = 0; - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stcx = STC.none; + STC stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; foreach (i, inv; ad.invs) { stc = mergeFuncAttrs(stc, inv); @@ -1203,7 +1234,7 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) // What should do? } const stcy = (inv.storage_class & STC.synchronized_) | - (inv.type.mod & MODFlags.shared_ ? STC.shared_ : 0); + (inv.type.mod & MODFlags.shared_ ? STC.shared_ : STC.none); if (i == 0) stcx = stcy; else if (stcx ^ stcy) @@ -1232,6 +1263,11 @@ FuncDeclaration buildInv(AggregateDeclaration ad, Scope* sc) * all the members. * Note the close similarity with AggregateDeclaration::buildDtor(), * and the ordering changes (runs forward instead of backwards). + * Params: + * sd = struct to create postblit for + * sc = context + * Returns: + * generated postblit, or null if not */ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) { @@ -1242,7 +1278,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) const hasUserDefinedPosblit = sd.postblits.length && !sd.postblits[0].isDisabled ? true : false; // by default, the storage class of the created postblit - StorageClass stc = STC.safe | STC.nothrow_ | STC.pure_ | STC.nogc; + STC stc = STC.safe; Loc declLoc = sd.postblits.length ? sd.postblits[0].loc : sd.loc; Loc loc; // internal code should have no loc to prevent coverage @@ -1262,10 +1298,10 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) if (structField.overlapped) continue; // if it's a struct declaration or an array of structs - Type tv = structField.type.baseElemOf(); - if (tv.ty != Tstruct) + TypeStruct tvs = structField.type.baseElemOf().isTypeStruct(); + if (!tvs) continue; - auto sdv = (cast(TypeStruct)tv).sym; + auto sdv = tvs.sym; // which has a postblit declaration if (!sdv.postblit) continue; @@ -1275,15 +1311,15 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // block to destroy any prior successfully postblitted fields should // this field's postblit fail. // Don't generate it for betterC code since it cannot throw exceptions. - if (fieldsToDestroy.length > 0 && !(cast(TypeFunction)sdv.postblit.type).isnothrow && global.params.useExceptions) + if (fieldsToDestroy.length > 0 && !sdv.postblit.type.isTypeFunction().isNothrow && global.params.useExceptions) { // create a list of destructors that need to be called Expression[] dtorCalls; foreach(sf; fieldsToDestroy) { Expression ex; - tv = sf.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = sf.type.toBasetype(); + if (auto tvs2 = tv.isTypeStruct()) { // this.v.__xdtor() @@ -1297,9 +1333,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) if (stc & STC.safe) stc = (stc & ~STC.safe) | STC.trusted; - auto sfv = (cast(TypeStruct)sf.type.baseElemOf()).sym; - - ex = new DotVarExp(loc, ex, sfv.dtor, false); + ex = new DotVarExp(loc, ex, tvs2.sym.dtor, false); ex = new CallExp(loc, ex); dtorCalls ~= ex; @@ -1346,6 +1380,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) // perform semantic on the member postblit in order to // be able to aggregate it later on with the rest of the // postblits + sdv.postblit.isGenerated = true; functionSemantic(sdv.postblit); stc = mergeFuncAttrs(stc, sdv.postblit); @@ -1361,8 +1396,8 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) } Expression ex; - tv = structField.type.toBasetype(); - if (tv.ty == Tstruct) + Type tv = structField.type.toBasetype(); + if (tv.isTypeStruct()) { // this.v.__xpostblit() @@ -1411,6 +1446,7 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) */ if (sdv.dtor) { + sdv.dtor.isGenerated = true; functionSemantic(sdv.dtor); // keep a list of fields that need to be destroyed in case @@ -1511,25 +1547,27 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc) } /** - * Generates a copy constructor declaration with the specified storage + * Generates a copy or move constructor declaration with the specified storage * class for the parameter and the function. * * Params: - * sd = the `struct` that contains the copy constructor - * paramStc = the storage class of the copy constructor parameter - * funcStc = the storage class for the copy constructor declaration + * sd = the `struct` that contains the constructor + * paramStc = the storage class of the constructor parameter + * funcStc = the storage class for the constructor declaration + * move = true for move constructor, false for copy constructor * * Returns: * The copy constructor declaration for struct `sd`. */ -private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc) +private CtorDeclaration generateCtorDeclaration(StructDeclaration sd, const STC paramStc, const STC funcStc, bool move) { auto fparams = new Parameters(); auto structType = sd.type; - fparams.push(new Parameter(Loc.initial, paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null)); + STC stc = move ? STC.none : STC.ref_; // the only difference between copy or move + fparams.push(new Parameter(Loc.initial, paramStc | stc, structType, Id.p, null, null)); ParameterList pList = ParameterList(fparams); auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_); - auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true); + auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf); ccd.storage_class |= funcStc; ccd.storage_class |= STC.inference; ccd.isGenerated = true; @@ -1537,28 +1575,37 @@ private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const } /** - * Generates a trivial copy constructor body that simply does memberwise - * initialization: + * Generates a trivial copy or move constructor body that simply does memberwise + * initialization. * + * for copy construction: * this.field1 = rhs.field1; * this.field2 = rhs.field2; * ... + * for move construction: + * this.field1 = __rvalue(rhs.field1); + * this.field2 = __rvalue(rhs.field2); + * ... * * Params: - * sd = the `struct` declaration that contains the copy constructor + * sd = the `struct` declaration that contains the constructor + * move = true for move constructor, false for copy constructor * * Returns: - * A `CompoundStatement` containing the body of the copy constructor. + * A `CompoundStatement` containing the body of the constructor. */ -private Statement generateCopyCtorBody(StructDeclaration sd) +private Statement generateCtorBody(StructDeclaration sd, bool move) { Loc loc; Expression e; foreach (v; sd.fields) { + Expression rhs = new DotVarExp(loc, new IdentifierExp(loc, Id.p), v); + if (move) + rhs.rvalue = true; auto ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v), - new DotVarExp(loc, new IdentifierExp(loc, Id.p), v)); + rhs); e = Expression.combine(e, ec); //printf("e.toChars = %s\n", e.toChars()); } @@ -1566,27 +1613,18 @@ private Statement generateCopyCtorBody(StructDeclaration sd) return new CompoundStatement(loc, s1); } -/** - * Determine if a copy constructor is needed for struct sd, - * if the following conditions are met: - * - * 1. sd does not define a copy constructor - * 2. at least one field of sd defines a copy constructor - * +/****************************************** + * Find root `this` constructor for struct sd. + * (root is starting position for overloaded constructors) * Params: - * sd = the `struct` for which the copy constructor is generated - * hasCpCtor = set to true if a copy constructor is already present - * - * Returns: - * `true` if one needs to be generated - * `false` otherwise + * sd = the `struct` to be searched + * ctor = `this` if found, otherwise null + * Result: + * false means `this` found in overload set */ -bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) +private bool findStructConstructorRoot(StructDeclaration sd, out Dsymbol ctor) { - if (global.errors) - return false; - - auto ctor = sd.search(sd.loc, Id.ctor); + ctor = sd.search(sd.loc, Id.ctor); // Aggregate.searchCtor() ? if (ctor) { if (ctor.isOverloadSet()) @@ -1594,13 +1632,18 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) if (auto td = ctor.isTemplateDeclaration()) ctor = td.funcroot; } + return true; +} - CtorDeclaration cpCtor; - CtorDeclaration rvalueCtor; - - if (!ctor) - goto LcheckFields; - +/*********************************************** + * Find move and copy constructors (if any) starting at `ctor` + * Params: + * ctor = `this` constructor root + * copyCtor = set to first copy constructor found, or null + * moveCtor = set to first move constructor found, or null + */ +private void findMoveAndCopyConstructors(Dsymbol ctor, out CtorDeclaration copyCtor, out CtorDeclaration moveCtor) +{ overloadApply(ctor, (Dsymbol s) { if (s.isTemplateDeclaration()) @@ -1609,38 +1652,63 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor) assert(ctorDecl); if (ctorDecl.isCpCtor) { - if (!cpCtor) - cpCtor = ctorDecl; - return 0; + if (!copyCtor) + copyCtor = ctorDecl; } - - auto tf = ctorDecl.type.toTypeFunction(); - const dim = tf.parameterList.length; - if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) + else if (ctorDecl.isMoveCtor) { - auto param = tf.parameterList[0]; - if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) - { - rvalueCtor = ctorDecl; - } + if (!moveCtor) + moveCtor = ctorDecl; } return 0; }); +} - if (cpCtor) - { - if (rvalueCtor) - { - .error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars()); - errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); - errorSupplemental(cpCtor.loc, "copy constructor defined here"); - } - hasCpCtor = true; - return false; - } +/** + * Determine if a copy constructor is needed for struct sd, + * if the following conditions are met: + * + * 1. sd does not define a copy constructor + * 2. at least one field of sd defines a copy constructor + * + * Params: + * sd = the `struct` for which the copy constructor is generated + * hasCopyCtor = set to true if a copy constructor is already present + * hasMoveCtor = set to true if a move constructor is already present + * needCopyCtor = set to true if a copy constructor is not present, but needed + * needMoveCtor = set to true if a move constructor is not present, but needed + * + * Returns: + * `true` if one needs to be generated + * `false` otherwise + */ +void needCopyOrMoveCtor(StructDeclaration sd, out bool hasCopyCtor, out bool hasMoveCtor, out bool needCopyCtor, out bool needMoveCtor) +{ + //printf("needCopyOrMoveCtor() %s\n", sd.toChars()); + if (global.errors) + return; + + Dsymbol ctor; + if (!findStructConstructorRoot(sd, ctor)) + return; + + CtorDeclaration copyCtor; + CtorDeclaration moveCtor; + + if (ctor) + findMoveAndCopyConstructors(ctor, copyCtor, moveCtor); + + if (moveCtor) + hasMoveCtor = true; + + if (copyCtor) + hasCopyCtor = true; + + if (hasMoveCtor && hasCopyCtor) + return; -LcheckFields: VarDeclaration fieldWithCpCtor; + VarDeclaration fieldWithMoveCtor; // see if any struct members define a copy constructor foreach (v; sd.fields) { @@ -1655,20 +1723,25 @@ LcheckFields: if (ts.sym.hasCopyCtor) { fieldWithCpCtor = v; - break; + } + if (ts.sym.hasMoveCtor) + { + fieldWithMoveCtor = v; } } - if (fieldWithCpCtor && rvalueCtor) + if (0 && fieldWithCpCtor && moveCtor) { .error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with copy constructors", sd.toChars()); - errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here"); + errorSupplemental(moveCtor.loc,"rvalue constructor defined here"); errorSupplemental(fieldWithCpCtor.loc, "field with copy constructor defined here"); - return false; + return; } - else if (!fieldWithCpCtor) - return false; - return true; + + if (fieldWithCpCtor && !hasCopyCtor) + needCopyCtor = true; + if (fieldWithMoveCtor && !hasMoveCtor) + needMoveCtor = true; } /** @@ -1682,30 +1755,25 @@ LcheckFields: * } * * Params: - * sd = the `struct` for which the copy constructor is generated - * sc = the scope where the copy constructor is generated - * - * Returns: - * `true` if `struct` sd defines a copy constructor (explicitly or generated), - * `false` otherwise. + * sd = the `struct` for which the constructor is generated + * sc = the scope where the constructor is generated + * move = true means generate the move constructor, otherwise copy constructor + * References: + * https://dlang.org/spec/struct.html#struct-copy-constructor */ -bool buildCopyCtor(StructDeclaration sd, Scope* sc) +void buildCopyOrMoveCtor(StructDeclaration sd, Scope* sc, bool move) { - bool hasCpCtor; - if (!needCopyCtor(sd, hasCpCtor)) - return hasCpCtor; - - //printf("generating copy constructor for %s\n", sd.toChars()); + //printf("buildCopyOrMoveCtor() generating %s constructor for %s\n", move ? "move".ptr : "copy".ptr, sd.toChars()); const MOD paramMod = MODFlags.wild; const MOD funcMod = MODFlags.wild; - auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod)); - auto copyCtorBody = generateCopyCtorBody(sd); - ccd.fbody = copyCtorBody; + auto ccd = generateCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod), move); + auto ctorBody = generateCtorBody(sd, move); + ccd.fbody = ctorBody; sd.members.push(ccd); ccd.addMember(sc, sd); const errors = global.startGagging(); Scope* sc2 = sc.push(); - sc2.stc = 0; + sc2.stc = STC.none; sc2.linkage = LINK.d; ccd.dsymbolSemantic(sc2); ccd.semantic2(sc2); @@ -1717,5 +1785,4 @@ bool buildCopyCtor(StructDeclaration sd, Scope* sc) ccd.storage_class |= STC.disable; ccd.fbody = null; } - return true; } diff --git a/gcc/d/dmd/common/bitfields.d b/gcc/d/dmd/common/bitfields.d index 01aa56d..72cb3e7 100644 --- a/gcc/d/dmd/common/bitfields.d +++ b/gcc/d/dmd/common/bitfields.d @@ -1,12 +1,12 @@ /** * A library bitfields utility * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Dennis Korpel * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/bitfields.d, common/bitfields.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/bitfields.d, common/bitfields.d) * Documentation: https://dlang.org/phobos/dmd_common_bitfields.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/bitfields.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/bitfields.d */ module dmd.common.bitfields; @@ -20,43 +20,79 @@ module dmd.common.bitfields; extern (D) string generateBitFields(S, T)() if (__traits(isUnsigned, T)) { + import core.bitop: bsr; + string result = "extern (C++) pure nothrow @nogc @safe final {"; - enum structName = __traits(identifier, S); - string initialValue = ""; + struct BitInfo + { + int[] offset; + int[] size; + T initialValue; + int totalSize; + } + + // Iterate over members to compute bit offset and bit size for each of them + enum BitInfo bitInfo = () { + BitInfo result; + int bitOffset = 0; + foreach (size_t i, mem; __traits(allMembers, S)) + { + alias memType = typeof(__traits(getMember, S, mem)); + enum int bitSize = bsr(memType.max | 1) + 1; + result.offset ~= bitOffset; + result.size ~= bitSize; + result.initialValue |= cast(T) __traits(getMember, S.init, mem) << bitOffset; + bitOffset += bitSize; + } + result.totalSize = bitOffset; + return result; + } (); + + alias TP = typeof(T.init + 0u); // type that `T` gets promoted to, uint or ulong + enum string toString(TP i) = i.stringof; // compile time 'integer to string' + + static assert(bitInfo.totalSize <= T.sizeof * 8, + "sum of bit field size "~toString!(bitInfo.totalSize)~" exceeds storage type `"~T.stringof~"`"); + foreach (size_t i, mem; __traits(allMembers, S)) { - static assert(is(typeof(__traits(getMember, S, mem)) == bool)); - static assert(i < T.sizeof * 8, "too many fields for bit field storage of type `"~T.stringof~"`"); - enum mask = "(1 << "~i.stringof~")"; + enum typeName = typeof(__traits(getMember, S, mem)).stringof; + enum shift = toString!(bitInfo.offset[i]); + enum sizeMask = toString!((1 << bitInfo.size[i]) - 1); // 0x01 for bool, 0xFF for ubyte etc. result ~= " - /// set or get the corresponding "~structName~" member - bool "~mem~"() const scope { return !!(bitFields & "~mask~"); } - /// ditto - bool "~mem~"(bool v) + "~typeName~" "~mem~"() const scope { return cast("~typeName~") ((bitFields >>> "~shift~") & "~sizeMask~"); } + "~typeName~" "~mem~"("~typeName~" v) scope { - v ? (bitFields |= "~mask~") : (bitFields &= ~"~mask~"); + bitFields &= ~("~sizeMask~" << "~shift~"); + bitFields |= v << "~shift~"; return v; }"; - - initialValue = (__traits(getMember, S.init, mem) ? "1" : "0") ~ initialValue; } - return result ~ "}\n private "~T.stringof~" bitFields = 0b" ~ initialValue ~ ";\n"; + enum TP initVal = bitInfo.initialValue; + return result ~ "\n}\n private "~T.stringof~" bitFields = " ~ toString!(initVal) ~ ";\n"; } /// unittest { + enum E + { + a, b, c, + } + static struct B { bool x; bool y; + E e = E.c; bool z = 1; + private ubyte w = 77; } static struct S { - mixin(generateBitFields!(B, ubyte)); + mixin(generateBitFields!(B, ushort)); } S s; @@ -69,5 +105,13 @@ unittest s.y = true; assert(s.y); assert(!s.x); + + assert(s.e == E.c); + s.e = E.a; + assert(s.e == E.a); + assert(s.z); + assert(s.w == 77); + s.w = 3; + assert(s.w == 3); } diff --git a/gcc/d/dmd/common/charactertables.d b/gcc/d/dmd/common/charactertables.d new file mode 100644 index 0000000..7df5234 --- /dev/null +++ b/gcc/d/dmd/common/charactertables.d @@ -0,0 +1,268 @@ +/** + * Character tables related to identifiers. + * + * Supports UAX31, C99, C11 and least restrictive (All). + * + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://cattermole.co.nz, Richard (Rikki) Andrew Cattermole) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/charactertables.d, common/charactertables.d) + * Documentation: https://dlang.org/phobos/dmd_common_charactertables.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/charactertables.d + */ +module dmd.common.charactertables; + +@safe nothrow @nogc pure: + +extern(C++): + +/// +enum IdentifierTable { + UAX31, /// + C99, /// + C11, /// + LR, /// Least Restrictive aka All +} + +/// +struct IdentifierCharLookup +{ + @safe nothrow @nogc pure: + + /// + extern(C++) bool function(dchar) isStart; + /// + extern(C++) bool function(dchar) isContinue; + + /// Lookup the table given the table name + extern(C++) static IdentifierCharLookup forTable(IdentifierTable table) + { + import dmd.common.identifiertables; + + // Awful solution to require these lambdas. + // However without them the extern(C++) ABI issues crop up for isInRange, + // and then it can't access the tables. + final switch(table) + { + case IdentifierTable.UAX31: + return IdentifierCharLookup( + (c) => isInRange!UAX31_Start(c), + (c) => isInRange!UAX31_Continue(c)); + case IdentifierTable.C99: + return IdentifierCharLookup( + (c) => isInRange!FixedTable_C99_Start(c), + (c) => isInRange!FixedTable_C99_Continue(c)); + case IdentifierTable.C11: + return IdentifierCharLookup( + (c) => isInRange!FixedTable_C11_Start(c), + (c) => isInRange!FixedTable_C11_Continue(c)); + case IdentifierTable.LR: + return IdentifierCharLookup( + (c) => isInRange!LeastRestrictive_Start(c), + (c) => isInRange!LeastRestrictive_Continue(c)); + } + } +} + +/** +Convenience function for use in places where we just don't care, +what the identifier ranges are, or if it is start/continue. + +Returns: is character a member of least restrictive of all. +*/ +bool isAnyIdentifierCharacter(dchar c) +{ + import dmd.common.identifiertables; + return isInRange!LeastRestrictive_OfAll(c); +} + +/// +unittest +{ + assert(isAnyIdentifierCharacter('ÄŸ')); +} + +/** +Convenience function for use in places where we just don't care, +what the identifier ranges are. + +Returns: is character a member of restrictive Start +*/ +bool isAnyStart(dchar c) +{ + import dmd.common.identifiertables; + return isInRange!LeastRestrictive_Start(c); +} + +/// +unittest +{ + assert(isAnyStart('ÄŸ')); +} + +/** +Convenience function for use in places where we just don't care, +what the identifier ranges are. + +Returns: is character a member of least restrictive Continue +*/ +bool isAnyContinue(dchar c) +{ + import dmd.common.identifiertables; + return isInRange!LeastRestrictive_Continue(c); +} + +/// +unittest +{ + assert(isAnyContinue('ÄŸ')); +} + +/// UTF line separator +enum LS = 0x2028; +/// UTF paragraph separator +enum PS = 0x2029; + +private +{ + enum CMoctal = 0x1; + enum CMhex = 0x2; + enum CMidchar = 0x4; + enum CMzerosecond = 0x8; + enum CMdigitsecond = 0x10; + enum CMsinglechar = 0x20; +} + +/// +bool isoctal(const char c) +{ + return (cmtable[c] & CMoctal) != 0; +} + +/// +bool ishex(const char c) +{ + return (cmtable[c] & CMhex) != 0; +} + +/// +bool isidchar(const char c) +{ + return (cmtable[c] & CMidchar) != 0; +} + +/// +bool isZeroSecond(const char c) +{ + return (cmtable[c] & CMzerosecond) != 0; +} + +/// +bool isDigitSecond(const char c) +{ + return (cmtable[c] & CMdigitsecond) != 0; +} + +/// +bool issinglechar(const char c) +{ + return (cmtable[c] & CMsinglechar) != 0; +} + +/// +bool c_isxdigit(const int c) +{ + return (( c >= '0' && c <= '9') || + ( c >= 'a' && c <= 'f') || + ( c >= 'A' && c <= 'F')); +} + +/// +bool c_isalnum(const int c) +{ + return (( c >= '0' && c <= '9') || + ( c >= 'a' && c <= 'z') || + ( c >= 'A' && c <= 'Z')); +} + +extern(D) private: + +// originally from dmd.root.utf +bool isInRange(alias Ranges)(dchar c) +{ + size_t high = Ranges.length - 1; + // Shortcut search if c is out of range + size_t low = (c < Ranges[0][0] || Ranges[high][1] < c) ? high + 1 : 0; + // Binary search + while (low <= high) + { + const size_t mid = low + ((high - low) >> 1); + if (c < Ranges[mid][0]) + high = mid - 1; + else if (Ranges[mid][1] < c) + low = mid + 1; + else + { + assert(Ranges[mid][0] <= c && c <= Ranges[mid][1]); + return true; + } + } + return false; +} + +/******************************************** + * Do our own char maps + */ +// originally from dmd.lexer (was private) +static immutable cmtable = () +{ + ubyte[256] table; + foreach (const c; 0 .. table.length) + { + if ('0' <= c && c <= '7') + table[c] |= CMoctal; + if (c_isxdigit(c)) + table[c] |= CMhex; + if (c_isalnum(c) || c == '_') + table[c] |= CMidchar; + + switch (c) + { + case 'x': case 'X': + case 'b': case 'B': + table[c] |= CMzerosecond; + break; + + case '0': .. case '9': + case 'e': case 'E': + case 'f': case 'F': + case 'l': case 'L': + case 'p': case 'P': + case 'u': case 'U': + case 'i': + case '.': + case '_': + table[c] |= CMzerosecond | CMdigitsecond; + break; + + default: + break; + } + + switch (c) + { + case '\\': + case '\n': + case '\r': + case 0: + case 0x1A: + case '\'': + break; + default: + if (!(c & 0x80)) + table[c] |= CMsinglechar; + break; + } + } + return table; +}(); diff --git a/gcc/d/dmd/common/charactertables.h b/gcc/d/dmd/common/charactertables.h new file mode 100644 index 0000000..b38409e --- /dev/null +++ b/gcc/d/dmd/common/charactertables.h @@ -0,0 +1,29 @@ +/** + * Character tables related to identifiers. + * + * Supports UAX31, C99, C11 and least restrictive (All). + * + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://cattermole.co.nz, Richard (Rikki) Andrew Cattermole) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/charactertables.h, common/charactertables.d) + */ + +#pragma once + +enum class IdentifierTable +{ + UAX31, + C99, + C11, + LR, // Least Restrictive aka All +}; + +struct IdentifierCharLookup final +{ + bool(*isStart)(char32_t); + bool(*isContinue)(char32_t); + + // constructor not provided here. + static IdentifierCharLookup forTable(IdentifierTable table); +}; diff --git a/gcc/d/dmd/common/file.d b/gcc/d/dmd/common/file.d index 80677f6..1b01a28 100644 --- a/gcc/d/dmd/common/file.d +++ b/gcc/d/dmd/common/file.d @@ -4,25 +4,33 @@ * Functions and objects dedicated to file I/O and management. TODO: Move here artifacts * from places such as root/ so both the frontend and the backend have access to them. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/file.d, common/_file.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/file.d, common/_file.d) * Documentation: https://dlang.org/phobos/dmd_common_file.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/file.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/file.d */ module dmd.common.file; +import core.stdc.stdio; +import core.stdc.stdlib; +import core.stdc.string; +import core.stdc.limits; + import core.stdc.errno : errno; import core.stdc.stdio : fprintf, remove, rename, stderr; import core.stdc.stdlib; import core.stdc.string : strerror, strlen, memcpy; import dmd.common.smallbuffer; +import dmd.root.filename; +import dmd.root.rmem; version (Windows) { + import core.stdc.wchar_; import core.sys.windows.winbase; import core.sys.windows.winnls : CP_ACP; import core.sys.windows.winnt; @@ -32,6 +40,7 @@ version (Windows) } else version (Posix) { + import core.sys.posix.dirent; import core.sys.posix.fcntl; import core.sys.posix.sys.mman; import core.sys.posix.sys.stat; @@ -566,7 +575,7 @@ else version (Windows) private ulong fileSize(HANDLE fd) { ulong result; - if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result) == 0) + if (GetFileSizeEx(fd, cast(LARGE_INTEGER*) &result)) return result; return ulong.max; } @@ -587,3 +596,163 @@ private auto ref fakePure(F)(scope F fun) pure mixin("alias PureFun = " ~ F.stringof ~ " pure;"); return (cast(PureFun) fun)(); } + +/*********************************** + * Recursively search all the directories and files under dir_path + * for files that match one of the extensions in exts[]. + * Pass the matches to sink. + * Params: + * dir_path = root of directories to search + * exts = array of filename extensions to match + * recurse = go into subdirectories + * filenameSink = accepts the resulting matches + * Returns: + * true for failed to open the directory + */ +bool findFiles(const char* dir_path, const char[][] exts, bool recurse, void delegate(const(char)[]) nothrow filenameSink) +{ + enum log = false; + if (log) printf("findFiles() dir_path: %s\n", dir_path); + version (Windows) + { + debug + enum BufLength = 10; // trigger any reallocation bugs + else + enum BufLength = 100; + char[BufLength + 1] buf = void; + char* fullPath = buf.ptr; + size_t fullPathLength = BufLength; + + // fullPath = dir_path \ *.* + const dir_pathLength = strlen(dir_path); + auto count = dir_pathLength + 1 + 3; + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + memcpy(fullPath, dir_path, dir_pathLength); + strcpy(fullPath + dir_pathLength, "\\*.*".ptr); + + if (log) printf("fullPath: %s\n", fullPath); + + WIN32_FIND_DATAW ffd = void; + HANDLE hFind = fullPath[0 .. strlen(fullPath)].extendedPathThen!(p => FindFirstFileW(p.ptr, &ffd)); + if (hFind == INVALID_HANDLE_VALUE) + return true; + + do + { + if (log) wprintf("ffd.cFileName: %s\n", ffd.cFileName.ptr); + if (ffd.cFileName[0] == 0) + continue; // ignore + + if (ffd.cFileName[0] == '.') + continue; // ignore files that start with a ., also ignore . and .. directories + + const(char)[] name = toNarrowStringz(ffd.cFileName[0 .. wcslen(ffd.cFileName.ptr)], null); + if (log) printf("name: %s\n", name.ptr); + + // fullPath = dir_path \ name.ptr + count = dir_pathLength + 1 + name.length; + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + strcpy(fullPath + dir_pathLength + 1, name.ptr); + + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (recurse) + findFiles(fullPath, exts, recurse, filenameSink); + } + else + { + const(char)[] nameExt = FileName.ext(name); + foreach (ext; exts[]) + { + if (nameExt == ext) + { + if (log) printf("adding %s\n", fullPath); + filenameSink(fullPath[0 .. count]); + } + } + } + mem.xfree(cast(void*)name.ptr); + + } while (FindNextFileW(hFind, &ffd) != 0); + + if (fullPath != buf.ptr) + mem.xfree(fullPath); + FindClose(hFind); + if (log) printf("findFiles() exit\n"); + return false; + } + else version (Posix) + { + DIR* dir = opendir(dir_path); + if (!dir) + return true; + + debug + enum BufLength = 10; // trigger any reallocation bugs + else + enum BufLength = 100; + char[BufLength + 1] buf = void; + char* fullPath = buf.ptr; + size_t fullPathLength = BufLength; + + dirent* entry; + while ((entry = readdir(dir)) != null) + { + //printf("entry: %s\n", entry.d_name.ptr); + if (entry.d_name[0] == '.') + continue; // ignore files that start with a . + + // fullPath = dir_path / entry.d_name.ptr + const dir_pathLength = strlen(dir_path); + const count = dir_pathLength + 1 + strlen(entry.d_name.ptr); + if (count > fullPathLength) + { + fullPathLength = count; + fullPath = cast(char*)Mem.xrealloc_noscan(fullPath == buf.ptr ? null : fullPath, fullPathLength + 1); + } + memcpy(fullPath, dir_path, dir_pathLength); + fullPath[dir_pathLength] = '/'; + strcpy(fullPath + dir_pathLength + 1, entry.d_name.ptr); + + stat_t statbuf; + if (lstat(fullPath, &statbuf) == -1) + continue; + + const(char)[] name = entry.d_name.ptr[0 .. strlen(entry.d_name.ptr)]; // convert to D string + if (!name.length) + continue; // ignore + + if (S_ISDIR(statbuf.st_mode)) + { + if (recurse && !(name == "." || name == "..")) + findFiles(fullPath, exts, recurse, filenameSink); + } + else if (S_ISREG(statbuf.st_mode)) + { + foreach (ext; exts) + { + if (FileName.ext(name) == ext) + { + //printf("%s\n", fullPath); + filenameSink(fullPath[0 .. count]); + } + } + } + } + + if (fullPath != buf.ptr) + mem.xfree(fullPath); + closedir(dir); + return false; + } + else + static assert(0); +} diff --git a/gcc/d/dmd/common/identifiertables.d b/gcc/d/dmd/common/identifiertables.d new file mode 100644 index 0000000..b87e9fa --- /dev/null +++ b/gcc/d/dmd/common/identifiertables.d @@ -0,0 +1,4241 @@ +// Generated by compiler/tools/unicode_tables.d DO NOT MODIFY!!! +module dmd.common.identifiertables; + +/** +UAX31 profile Start +Entries: 136892 +*/ +static immutable dchar[2][] UAX31_Start = [ + [0xAA, 0xAA], + [0xB5, 0xB5], + [0xBA, 0xBA], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x370, 0x374], + [0x376, 0x377], + [0x37B, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3F5], + [0x3F7, 0x481], + [0x48A, 0x52F], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x588], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x620, 0x64A], + [0x66E, 0x66F], + [0x671, 0x6D3], + [0x6D5, 0x6D5], + [0x6E5, 0x6E6], + [0x6EE, 0x6EF], + [0x6FA, 0x6FC], + [0x6FF, 0x6FF], + [0x710, 0x710], + [0x712, 0x72F], + [0x74D, 0x7A5], + [0x7B1, 0x7B1], + [0x7CA, 0x7EA], + [0x7F4, 0x7F5], + [0x7FA, 0x7FA], + [0x800, 0x815], + [0x81A, 0x81A], + [0x824, 0x824], + [0x828, 0x828], + [0x840, 0x858], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x8A0, 0x8C9], + [0x904, 0x939], + [0x93D, 0x93D], + [0x950, 0x950], + [0x958, 0x961], + [0x971, 0x980], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BD, 0x9BD], + [0x9CE, 0x9CE], + [0x9DC, 0x9DD], + [0x9DF, 0x9E1], + [0x9F0, 0x9F1], + [0x9FC, 0x9FC], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA72, 0xA74], + [0xA85, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABD, 0xABD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE1], + [0xAF9, 0xAF9], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3D, 0xB3D], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB71, 0xB71], + [0xB83, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB9], + [0xBD0, 0xBD0], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC39], + [0xC3D, 0xC3D], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC80, 0xC80], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBD, 0xCBD], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCF1, 0xCF2], + [0xD04, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD3A], + [0xD3D, 0xD3D], + [0xD4E, 0xD4E], + [0xD54, 0xD56], + [0xD5F, 0xD61], + [0xD7A, 0xD7F], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xE01, 0xE30], + [0xE32, 0xE32], + [0xE40, 0xE46], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE8A], + [0xE8C, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEB0], + [0xEB2, 0xEB2], + [0xEBD, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF40, 0xF47], + [0xF49, 0xF6C], + [0xF88, 0xF8C], + [0x1000, 0x102A], + [0x103F, 0x103F], + [0x1050, 0x1055], + [0x105A, 0x105D], + [0x1061, 0x1061], + [0x1065, 0x1066], + [0x106E, 0x1070], + [0x1075, 0x1081], + [0x108E, 0x108E], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1711], + [0x171F, 0x1731], + [0x1740, 0x1751], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1780, 0x17B3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DC], + [0x1820, 0x1878], + [0x1880, 0x18A8], + [0x18AA, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1950, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x1A00, 0x1A16], + [0x1A20, 0x1A54], + [0x1AA7, 0x1AA7], + [0x1B05, 0x1B33], + [0x1B45, 0x1B4C], + [0x1B83, 0x1BA0], + [0x1BAE, 0x1BAF], + [0x1BBA, 0x1BE5], + [0x1C00, 0x1C23], + [0x1C4D, 0x1C4F], + [0x1C5A, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CE9, 0x1CEC], + [0x1CEE, 0x1CF3], + [0x1CF5, 0x1CF6], + [0x1CFA, 0x1CFA], + [0x1D00, 0x1DBF], + [0x1E00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x2071, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2139], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CEE], + [0x2CF2, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D80, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x3005, 0x3007], + [0x3021, 0x3029], + [0x3031, 0x3035], + [0x3038, 0x303C], + [0x3041, 0x3096], + [0x309D, 0x309F], + [0x30A1, 0x30FA], + [0x30FC, 0x30FF], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA61F], + [0xA62A, 0xA62B], + [0xA640, 0xA66E], + [0xA67F, 0xA69D], + [0xA6A0, 0xA6EF], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA801], + [0xA803, 0xA805], + [0xA807, 0xA80A], + [0xA80C, 0xA822], + [0xA840, 0xA873], + [0xA882, 0xA8B3], + [0xA8F2, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA8FE], + [0xA90A, 0xA925], + [0xA930, 0xA946], + [0xA960, 0xA97C], + [0xA984, 0xA9B2], + [0xA9CF, 0xA9CF], + [0xA9E0, 0xA9E4], + [0xA9E6, 0xA9EF], + [0xA9FA, 0xA9FE], + [0xAA00, 0xAA28], + [0xAA40, 0xAA42], + [0xAA44, 0xAA4B], + [0xAA60, 0xAA76], + [0xAA7A, 0xAA7A], + [0xAA7E, 0xAAAF], + [0xAAB1, 0xAAB1], + [0xAAB5, 0xAAB6], + [0xAAB9, 0xAABD], + [0xAAC0, 0xAAC0], + [0xAAC2, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEA], + [0xAAF2, 0xAAF4], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABE2], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB1D], + [0xFB1F, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD50, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE71, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF21, 0xFF3A], + [0xFF41, 0xFF5A], + [0xFF66, 0xFF9D], + [0xFFA0, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x10375], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A00], + [0x10A10, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE4], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D23], + [0x10E80, 0x10EA9], + [0x10EB0, 0x10EB1], + [0x10F00, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F45], + [0x10F70, 0x10F81], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11003, 0x11037], + [0x11071, 0x11072], + [0x11075, 0x11075], + [0x11083, 0x110AF], + [0x110D0, 0x110E8], + [0x11103, 0x11126], + [0x11144, 0x11144], + [0x11147, 0x11147], + [0x11150, 0x11172], + [0x11176, 0x11176], + [0x11183, 0x111B2], + [0x111C1, 0x111C4], + [0x111DA, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x1122B], + [0x1123F, 0x11240], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112DE], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133D, 0x1133D], + [0x11350, 0x11350], + [0x1135D, 0x11361], + [0x11400, 0x11434], + [0x11447, 0x1144A], + [0x1145F, 0x11461], + [0x11480, 0x114AF], + [0x114C4, 0x114C5], + [0x114C7, 0x114C7], + [0x11580, 0x115AE], + [0x115D8, 0x115DB], + [0x11600, 0x1162F], + [0x11644, 0x11644], + [0x11680, 0x116AA], + [0x116B8, 0x116B8], + [0x11700, 0x1171A], + [0x11740, 0x11746], + [0x11800, 0x1182B], + [0x118A0, 0x118DF], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x1192F], + [0x1193F, 0x1193F], + [0x11941, 0x11941], + [0x119A0, 0x119A7], + [0x119AA, 0x119D0], + [0x119E1, 0x119E1], + [0x119E3, 0x119E3], + [0x11A00, 0x11A00], + [0x11A0B, 0x11A32], + [0x11A3A, 0x11A3A], + [0x11A50, 0x11A50], + [0x11A5C, 0x11A89], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C2E], + [0x11C40, 0x11C40], + [0x11C72, 0x11C8F], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D30], + [0x11D46, 0x11D46], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D89], + [0x11D98, 0x11D98], + [0x11EE0, 0x11EF2], + [0x11F02, 0x11F02], + [0x11F04, 0x11F10], + [0x11F12, 0x11F33], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13441, 0x13446], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A70, 0x16ABE], + [0x16AD0, 0x16AED], + [0x16B00, 0x16B2F], + [0x16B40, 0x16B43], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F50, 0x16F50], + [0x16F93, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE3], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E030, 0x1E06D], + [0x1E100, 0x1E12C], + [0x1E137, 0x1E13D], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AD], + [0x1E2C0, 0x1E2EB], + [0x1E4D0, 0x1E4EB], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E900, 0x1E943], + [0x1E94B, 0x1E94B], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x20000, 0x2A6DF], + [0x2A700, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x3134A], + [0x31350, 0x323AF], +]; + +/** +UAX31 profile Continue +Entries: 140026 +*/ +static immutable dchar[2][] UAX31_Continue = [ + [0xAA, 0xAA], + [0xB5, 0xB5], + [0xB7, 0xB7], + [0xBA, 0xBA], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x300, 0x374], + [0x376, 0x377], + [0x37B, 0x37D], + [0x37F, 0x37F], + [0x386, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3F5], + [0x3F7, 0x481], + [0x483, 0x487], + [0x48A, 0x52F], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x588], + [0x591, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5C4, 0x5C5], + [0x5C7, 0x5C7], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x610, 0x61A], + [0x620, 0x669], + [0x66E, 0x6D3], + [0x6D5, 0x6DC], + [0x6DF, 0x6E8], + [0x6EA, 0x6FC], + [0x6FF, 0x6FF], + [0x710, 0x74A], + [0x74D, 0x7B1], + [0x7C0, 0x7F5], + [0x7FA, 0x7FA], + [0x7FD, 0x7FD], + [0x800, 0x82D], + [0x840, 0x85B], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x898, 0x8E1], + [0x8E3, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BC, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CE], + [0x9D7, 0x9D7], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0x9FE, 0x9FE], + [0xA01, 0xA03], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3C, 0xA3C], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA51, 0xA51], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA75], + [0xA81, 0xA83], + [0xA85, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABC, 0xAC5], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE3], + [0xAE6, 0xAEF], + [0xAF9, 0xAFF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3C, 0xB44], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB55, 0xB57], + [0xB5C, 0xB5D], + [0xB5F, 0xB63], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBD7, 0xBD7], + [0xBE6, 0xBEF], + [0xC00, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC39], + [0xC3C, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC55, 0xC56], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC63], + [0xC66, 0xC6F], + [0xC80, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBC, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCD5, 0xCD6], + [0xCDD, 0xCDE], + [0xCE0, 0xCE3], + [0xCE6, 0xCEF], + [0xCF1, 0xCF3], + [0xD00, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD44], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD57], + [0xD5F, 0xD63], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD81, 0xD83], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xDCA, 0xDCA], + [0xDCF, 0xDD4], + [0xDD6, 0xDD6], + [0xDD8, 0xDDF], + [0xDE6, 0xDEF], + [0xDF2, 0xDF3], + [0xE01, 0xE3A], + [0xE40, 0xE4E], + [0xE50, 0xE59], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE8A], + [0xE8C, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECE], + [0xED0, 0xED9], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF29], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF6C], + [0xF71, 0xF84], + [0xF86, 0xF97], + [0xF99, 0xFBC], + [0xFC6, 0xFC6], + [0x1000, 0x1049], + [0x1050, 0x109D], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x135D, 0x135F], + [0x1369, 0x1371], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1715], + [0x171F, 0x1734], + [0x1740, 0x1753], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1772, 0x1773], + [0x1780, 0x17D3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DD], + [0x17E0, 0x17E9], + [0x180B, 0x180D], + [0x180F, 0x1819], + [0x1820, 0x1878], + [0x1880, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1920, 0x192B], + [0x1930, 0x193B], + [0x1946, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x19D0, 0x19DA], + [0x1A00, 0x1A1B], + [0x1A20, 0x1A5E], + [0x1A60, 0x1A7C], + [0x1A7F, 0x1A89], + [0x1A90, 0x1A99], + [0x1AA7, 0x1AA7], + [0x1AB0, 0x1ABD], + [0x1ABF, 0x1ACE], + [0x1B00, 0x1B4C], + [0x1B50, 0x1B59], + [0x1B6B, 0x1B73], + [0x1B80, 0x1BF3], + [0x1C00, 0x1C37], + [0x1C40, 0x1C49], + [0x1C4D, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CD0, 0x1CD2], + [0x1CD4, 0x1CFA], + [0x1D00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200C, 0x200D], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2071, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x20D0, 0x20DC], + [0x20E1, 0x20E1], + [0x20E5, 0x20F0], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2139], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D7F, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2DE0, 0x2DFF], + [0x3005, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x3035], + [0x3038, 0x303C], + [0x3041, 0x3096], + [0x3099, 0x309A], + [0x309D, 0x309F], + [0x30A1, 0x30FF], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA62B], + [0xA640, 0xA66F], + [0xA674, 0xA67D], + [0xA67F, 0xA6F1], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA827], + [0xA82C, 0xA82C], + [0xA840, 0xA873], + [0xA880, 0xA8C5], + [0xA8D0, 0xA8D9], + [0xA8E0, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA92D], + [0xA930, 0xA953], + [0xA960, 0xA97C], + [0xA980, 0xA9C0], + [0xA9CF, 0xA9D9], + [0xA9E0, 0xA9FE], + [0xAA00, 0xAA36], + [0xAA40, 0xAA4D], + [0xAA50, 0xAA59], + [0xAA60, 0xAA76], + [0xAA7A, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEF], + [0xAAF2, 0xAAF6], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABEA], + [0xABEC, 0xABED], + [0xABF0, 0xABF9], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD50, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE00, 0xFE0F], + [0xFE20, 0xFE2F], + [0xFE33, 0xFE34], + [0xFE4D, 0xFE4F], + [0xFE71, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF10, 0xFF19], + [0xFF21, 0xFF3A], + [0xFF3F, 0xFF3F], + [0xFF41, 0xFF5A], + [0xFF65, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x101FD, 0x101FD], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x102E0, 0x102E0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x1037A], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104A0, 0x104A9], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A03], + [0x10A05, 0x10A06], + [0x10A0C, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A38, 0x10A3A], + [0x10A3F, 0x10A3F], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE6], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D27], + [0x10D30, 0x10D39], + [0x10E80, 0x10EA9], + [0x10EAB, 0x10EAC], + [0x10EB0, 0x10EB1], + [0x10EFD, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F50], + [0x10F70, 0x10F85], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11000, 0x11046], + [0x11066, 0x11075], + [0x1107F, 0x110BA], + [0x110C2, 0x110C2], + [0x110D0, 0x110E8], + [0x110F0, 0x110F9], + [0x11100, 0x11134], + [0x11136, 0x1113F], + [0x11144, 0x11147], + [0x11150, 0x11173], + [0x11176, 0x11176], + [0x11180, 0x111C4], + [0x111C9, 0x111CC], + [0x111CE, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x11237], + [0x1123E, 0x11241], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112EA], + [0x112F0, 0x112F9], + [0x11300, 0x11303], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133B, 0x11344], + [0x11347, 0x11348], + [0x1134B, 0x1134D], + [0x11350, 0x11350], + [0x11357, 0x11357], + [0x1135D, 0x11363], + [0x11366, 0x1136C], + [0x11370, 0x11374], + [0x11400, 0x1144A], + [0x11450, 0x11459], + [0x1145E, 0x11461], + [0x11480, 0x114C5], + [0x114C7, 0x114C7], + [0x114D0, 0x114D9], + [0x11580, 0x115B5], + [0x115B8, 0x115C0], + [0x115D8, 0x115DD], + [0x11600, 0x11640], + [0x11644, 0x11644], + [0x11650, 0x11659], + [0x11680, 0x116B8], + [0x116C0, 0x116C9], + [0x11700, 0x1171A], + [0x1171D, 0x1172B], + [0x11730, 0x11739], + [0x11740, 0x11746], + [0x11800, 0x1183A], + [0x118A0, 0x118E9], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x11935], + [0x11937, 0x11938], + [0x1193B, 0x11943], + [0x11950, 0x11959], + [0x119A0, 0x119A7], + [0x119AA, 0x119D7], + [0x119DA, 0x119E1], + [0x119E3, 0x119E4], + [0x11A00, 0x11A3E], + [0x11A47, 0x11A47], + [0x11A50, 0x11A99], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C36], + [0x11C38, 0x11C40], + [0x11C50, 0x11C59], + [0x11C72, 0x11C8F], + [0x11C92, 0x11CA7], + [0x11CA9, 0x11CB6], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D36], + [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], + [0x11D3F, 0x11D47], + [0x11D50, 0x11D59], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D8E], + [0x11D90, 0x11D91], + [0x11D93, 0x11D98], + [0x11DA0, 0x11DA9], + [0x11EE0, 0x11EF6], + [0x11F00, 0x11F10], + [0x11F12, 0x11F3A], + [0x11F3E, 0x11F42], + [0x11F50, 0x11F59], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13440, 0x13455], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A60, 0x16A69], + [0x16A70, 0x16ABE], + [0x16AC0, 0x16AC9], + [0x16AD0, 0x16AED], + [0x16AF0, 0x16AF4], + [0x16B00, 0x16B36], + [0x16B40, 0x16B43], + [0x16B50, 0x16B59], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F4F, 0x16F87], + [0x16F8F, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE4], + [0x16FF0, 0x16FF1], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1BC9D, 0x1BC9E], + [0x1CF00, 0x1CF2D], + [0x1CF30, 0x1CF46], + [0x1D165, 0x1D169], + [0x1D16D, 0x1D172], + [0x1D17B, 0x1D182], + [0x1D185, 0x1D18B], + [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1D7CE, 0x1D7FF], + [0x1DA00, 0x1DA36], + [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], + [0x1DA84, 0x1DA84], + [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E000, 0x1E006], + [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], + [0x1E023, 0x1E024], + [0x1E026, 0x1E02A], + [0x1E030, 0x1E06D], + [0x1E08F, 0x1E08F], + [0x1E100, 0x1E12C], + [0x1E130, 0x1E13D], + [0x1E140, 0x1E149], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AE], + [0x1E2C0, 0x1E2F9], + [0x1E4D0, 0x1E4F9], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E8D0, 0x1E8D6], + [0x1E900, 0x1E94B], + [0x1E950, 0x1E959], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x1FBF0, 0x1FBF9], + [0x20000, 0x2A6DF], + [0x2A700, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x3134A], + [0x31350, 0x323AF], + [0xE0100, 0xE01EF], +]; + +/** +C99 Start +Entries: 34958 +*/ +alias FixedTable_C99_Start = FixedTable_C99_Continue; + +/** +C99 Continue +Entries: 34958 +*/ +static immutable dchar[2][] FixedTable_C99_Continue = [ + [0xAA, 0xAA], + [0xB5, 0xB5], + [0xB7, 0xB7], + [0xBA, 0xBA], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x1F5], + [0x1FA, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2D0, 0x2D1], + [0x2E0, 0x2E4], + [0x37A, 0x37A], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3CE], + [0x3D0, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x401, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x490, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x561, 0x587], + [0x5B0, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5D0, 0x5EA], + [0x5F0, 0x5F2], + [0x621, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x670, 0x6B7], + [0x6BA, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6DC], + [0x6E5, 0x6E8], + [0x6EA, 0x6ED], + [0x6F0, 0x6F9], + [0x901, 0x903], + [0x905, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x981, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BE, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CD], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0xA02, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA74, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABD, 0xAC5], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB36, 0xB39], + [0xB3D, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBE7, 0xBEF], + [0xC01, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3E, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC82, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBE, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCDE, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xD02, 0xD03], + [0xD05, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD28], + [0xD2A, 0xD39], + [0xD3E, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4D], + [0xD60, 0xD61], + [0xD66, 0xD6F], + [0xE01, 0xE3A], + [0xE40, 0xE5B], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE87, 0xE88], + [0xE8A, 0xE8A], + [0xE8D, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEA7], + [0xEAA, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB9], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECD], + [0xED0, 0xED9], + [0xEDC, 0xEDD], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF33], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF69], + [0xF71, 0xF84], + [0xF86, 0xF8B], + [0xF90, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFAD], + [0xFB1, 0xFB7], + [0xFB9, 0xFB9], + [0x10A0, 0x10C5], + [0x10D0, 0x10F6], + [0x1E00, 0x1E9B], + [0x1EA0, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x203F, 0x2040], + [0x207F, 0x207F], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2131], + [0x2133, 0x2138], + [0x2160, 0x2182], + [0x3005, 0x3007], + [0x3021, 0x3029], + [0x3041, 0x3093], + [0x309B, 0x309C], + [0x30A1, 0x30F6], + [0x30FB, 0x30FC], + [0x3105, 0x312C], + [0x4E00, 0x9FA5], + [0xAC00, 0xD7A3], +]; + +/** +C11 Start +Entries: 971620 +*/ +alias FixedTable_C11_Start = FixedTable_C11_Continue; + +/** +C11 Continue +Entries: 971620 +*/ +static immutable dchar[2][] FixedTable_C11_Continue = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0xFF], + [0x100, 0x167F], + [0x1681, 0x180D], + [0x180F, 0x1FFF], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x206F], + [0x2070, 0x218F], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2DFF], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x303F], + [0x3040, 0xD7FF], + [0xF900, 0xFD3D], + [0xFD40, 0xFDCF], + [0xFDF0, 0xFE44], + [0xFE47, 0xFFFD], + [0x10000, 0x1FFFD], + [0x20000, 0x2FFFD], + [0x30000, 0x3FFFD], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xEFFFD], +]; + +/** +Least restrictive with both Start and Continue +Entries: 860486 +*/ +static immutable dchar[2][] LeastRestrictive_OfAll = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x300, 0x374], + [0x376, 0x377], + [0x37A, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x3F7, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x483, 0x487], + [0x48A, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x587], + [0x591, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5C4, 0x5C5], + [0x5C7, 0x5C7], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x610, 0x61A], + [0x620, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x66E, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6D5], + [0x6DF, 0x6E8], + [0x6EA, 0x6F9], + [0x6FF, 0x6FF], + [0x710, 0x710], + [0x712, 0x72F], + [0x74D, 0x7A5], + [0x7B1, 0x7B1], + [0x7C0, 0x7EA], + [0x7F4, 0x7F5], + [0x7FA, 0x7FA], + [0x7FD, 0x7FD], + [0x800, 0x815], + [0x81A, 0x81A], + [0x824, 0x824], + [0x828, 0x828], + [0x840, 0x858], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x898, 0x8C9], + [0x8E3, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BC, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CE], + [0x9D7, 0x9D7], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0x9FE, 0x9FE], + [0xA01, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3C, 0xA3C], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA51, 0xA51], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA72, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABC, 0xABD], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xAF9, 0xAFF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3C, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB55, 0xB57], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBD7, 0xBD7], + [0xBE6, 0xBEF], + [0xC00, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3C, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC55, 0xC56], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC80, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBC, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCD5, 0xCD6], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xCF1, 0xCF2], + [0xD00, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD39], + [0xD3D, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD57], + [0xD5F, 0xD61], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD81, 0xD83], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xDCA, 0xDCA], + [0xDCF, 0xDD4], + [0xDD6, 0xDD6], + [0xDD8, 0xDDF], + [0xDE6, 0xDEF], + [0xDF2, 0xDF3], + [0xE01, 0xE32], + [0xE40, 0xE4E], + [0xE50, 0xE59], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE88], + [0xE8A, 0xE8A], + [0xE8C, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB9], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECE], + [0xED0, 0xED9], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF29], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF6C], + [0xF71, 0xF84], + [0xF86, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFB7], + [0xFB9, 0xFB9], + [0xFC6, 0xFC6], + [0x1000, 0x103F], + [0x1050, 0x105D], + [0x1061, 0x1061], + [0x1065, 0x1066], + [0x106E, 0x1070], + [0x1075, 0x1081], + [0x108E, 0x108E], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x135D, 0x135F], + [0x1369, 0x1371], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1711], + [0x171F, 0x1731], + [0x1740, 0x1751], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1772, 0x1773], + [0x1780, 0x17B3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DC], + [0x17E0, 0x17E9], + [0x180B, 0x180D], + [0x180F, 0x1878], + [0x1880, 0x18A8], + [0x18AA, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1920, 0x192B], + [0x1930, 0x193B], + [0x1946, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x19D0, 0x19DA], + [0x1A00, 0x1A16], + [0x1A20, 0x1A54], + [0x1A60, 0x1A7C], + [0x1A7F, 0x1A89], + [0x1A90, 0x1A99], + [0x1AA7, 0x1AA7], + [0x1AB0, 0x1ABD], + [0x1ABF, 0x1ACE], + [0x1B00, 0x1B33], + [0x1B45, 0x1B4C], + [0x1B50, 0x1B59], + [0x1B6B, 0x1B73], + [0x1B80, 0x1BA0], + [0x1BAE, 0x1BAF], + [0x1BBA, 0x1BE5], + [0x1C00, 0x1C37], + [0x1C40, 0x1C49], + [0x1C4D, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CD0, 0x1CD2], + [0x1CD4, 0x1CEC], + [0x1CEE, 0x1CF3], + [0x1CF5, 0x1CF6], + [0x1CFA, 0x1CFA], + [0x1D00, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x20D0, 0x20DC], + [0x20E1, 0x20E1], + [0x20E5, 0x20F0], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2138], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D7F, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2DE0, 0x2DFF], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x303C], + [0x3041, 0x3096], + [0x3099, 0x309F], + [0x30A1, 0x30FC], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA61F], + [0xA62A, 0xA62B], + [0xA640, 0xA66F], + [0xA674, 0xA67D], + [0xA67F, 0xA6EF], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA805], + [0xA807, 0xA80A], + [0xA80C, 0xA822], + [0xA82C, 0xA82C], + [0xA840, 0xA873], + [0xA880, 0xA8B3], + [0xA8D0, 0xA8D9], + [0xA8E0, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA8FE], + [0xA90A, 0xA925], + [0xA930, 0xA946], + [0xA960, 0xA97C], + [0xA980, 0xA9B2], + [0xA9CF, 0xA9CF], + [0xA9E0, 0xA9E4], + [0xA9E6, 0xA9EF], + [0xA9FA, 0xA9FE], + [0xAA00, 0xAA28], + [0xAA40, 0xAA42], + [0xAA44, 0xAA4B], + [0xAA50, 0xAA59], + [0xAA60, 0xAA76], + [0xAA7A, 0xAA7A], + [0xAA7E, 0xAAAF], + [0xAAB1, 0xAAB1], + [0xAAB5, 0xAAB6], + [0xAAB9, 0xAABD], + [0xAAC0, 0xAAC0], + [0xAAC2, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEA], + [0xAAF2, 0xAAF4], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABE2], + [0xABEC, 0xABED], + [0xABF0, 0xABF9], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB1D], + [0xFB1F, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD40, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE00, 0xFE0F], + [0xFE20, 0xFE2F], + [0xFE33, 0xFE34], + [0xFE47, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF10, 0xFF19], + [0xFF21, 0xFF3A], + [0xFF3F, 0xFF3F], + [0xFF41, 0xFF5A], + [0xFF65, 0xFF9D], + [0xFFA0, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x101FD, 0x101FD], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x102E0, 0x102E0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x10375], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104A0, 0x104A9], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A03], + [0x10A05, 0x10A06], + [0x10A0C, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A38, 0x10A3A], + [0x10A3F, 0x10A3F], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE6], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D23], + [0x10D30, 0x10D39], + [0x10E80, 0x10EA9], + [0x10EAB, 0x10EAC], + [0x10EB0, 0x10EB1], + [0x10EFD, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F45], + [0x10F70, 0x10F81], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11000, 0x11037], + [0x11066, 0x11072], + [0x11075, 0x11075], + [0x1107F, 0x110AF], + [0x110C2, 0x110C2], + [0x110D0, 0x110E8], + [0x110F0, 0x110F9], + [0x11100, 0x11126], + [0x11136, 0x1113F], + [0x11144, 0x11147], + [0x11150, 0x11173], + [0x11176, 0x11176], + [0x11180, 0x111B2], + [0x111C1, 0x111C4], + [0x111C9, 0x111CC], + [0x111CE, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x11237], + [0x1123E, 0x11240], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112EA], + [0x112F0, 0x112F9], + [0x11300, 0x11303], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133B, 0x1133D], + [0x11347, 0x11348], + [0x1134B, 0x1134D], + [0x11350, 0x11350], + [0x11357, 0x11357], + [0x1135D, 0x11363], + [0x11366, 0x1136C], + [0x11370, 0x11374], + [0x11400, 0x1144A], + [0x11450, 0x11459], + [0x1145E, 0x11461], + [0x11480, 0x114C5], + [0x114C7, 0x114C7], + [0x114D0, 0x114D9], + [0x11580, 0x115B5], + [0x115B8, 0x115C0], + [0x115D8, 0x115DD], + [0x11600, 0x11640], + [0x11644, 0x11644], + [0x11650, 0x11659], + [0x11680, 0x116B8], + [0x116C0, 0x116C9], + [0x11700, 0x1171A], + [0x1171D, 0x1172B], + [0x11730, 0x11739], + [0x11740, 0x11746], + [0x11800, 0x1183A], + [0x118A0, 0x118E9], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x11935], + [0x11937, 0x11938], + [0x1193B, 0x1193F], + [0x11941, 0x11941], + [0x11950, 0x11959], + [0x119A0, 0x119A7], + [0x119AA, 0x119D7], + [0x119DA, 0x119E1], + [0x119E3, 0x119E3], + [0x11A00, 0x11A32], + [0x11A3A, 0x11A3A], + [0x11A47, 0x11A47], + [0x11A50, 0x11A89], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C36], + [0x11C38, 0x11C40], + [0x11C50, 0x11C59], + [0x11C72, 0x11C8F], + [0x11C92, 0x11CA7], + [0x11CA9, 0x11CB6], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D36], + [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], + [0x11D3F, 0x11D46], + [0x11D50, 0x11D59], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D89], + [0x11D90, 0x11D91], + [0x11D93, 0x11D98], + [0x11DA0, 0x11DA9], + [0x11EE0, 0x11EF2], + [0x11F00, 0x11F02], + [0x11F04, 0x11F10], + [0x11F12, 0x11F33], + [0x11F3E, 0x11F42], + [0x11F50, 0x11F59], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13440, 0x13446], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A60, 0x16A69], + [0x16A70, 0x16ABE], + [0x16AC0, 0x16AC9], + [0x16AD0, 0x16AED], + [0x16AF0, 0x16AF4], + [0x16B00, 0x16B2F], + [0x16B40, 0x16B43], + [0x16B50, 0x16B59], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F4F, 0x16F50], + [0x16F8F, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE3], + [0x16FF0, 0x16FF1], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1BC9D, 0x1BC9E], + [0x1CF00, 0x1CF2D], + [0x1CF30, 0x1CF46], + [0x1D165, 0x1D169], + [0x1D16D, 0x1D172], + [0x1D17B, 0x1D182], + [0x1D185, 0x1D18B], + [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1D7CE, 0x1D7FF], + [0x1DA00, 0x1DA36], + [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], + [0x1DA84, 0x1DA84], + [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E000, 0x1E006], + [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], + [0x1E023, 0x1E024], + [0x1E026, 0x1E02A], + [0x1E030, 0x1E06D], + [0x1E08F, 0x1E08F], + [0x1E100, 0x1E12C], + [0x1E130, 0x1E13D], + [0x1E140, 0x1E149], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AE], + [0x1E2C0, 0x1E2F9], + [0x1E4D0, 0x1E4F9], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E8D0, 0x1E8D6], + [0x1E900, 0x1E94B], + [0x1E950, 0x1E959], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x1FBF0, 0x1FBF9], + [0x20000, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x323AF], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xEFFFD], +]; + +/** +Least restrictive Start +Entries: 858717 +*/ +static immutable dchar[2][] LeastRestrictive_Start = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x370, 0x374], + [0x376, 0x377], + [0x37A, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3CE], + [0x3D0, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x3F7, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x48A, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x587], + [0x5B0, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x620, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x66E, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6D5], + [0x6E5, 0x6E8], + [0x6EA, 0x6FC], + [0x6FF, 0x6FF], + [0x710, 0x710], + [0x712, 0x72F], + [0x74D, 0x7A5], + [0x7B1, 0x7B1], + [0x7CA, 0x7EA], + [0x7F4, 0x7F5], + [0x7FA, 0x7FA], + [0x800, 0x815], + [0x81A, 0x81A], + [0x824, 0x824], + [0x828, 0x828], + [0x840, 0x858], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x8A0, 0x8C9], + [0x901, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BD, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CE], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0xA02, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA72, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABD, 0xABD], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xAF9, 0xAF9], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3D, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBE7, 0xBEF], + [0xC01, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3D, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC80, 0xC80], + [0xC82, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBD, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xCF1, 0xCF2], + [0xD02, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD28], + [0xD2A, 0xD39], + [0xD3D, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD56], + [0xD5F, 0xD61], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xE01, 0xE30], + [0xE32, 0xE32], + [0xE40, 0xE5B], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE88], + [0xE8A, 0xE8A], + [0xE8C, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB2], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECD], + [0xED0, 0xED9], + [0xEDC, 0xEDD], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF33], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF69], + [0xF71, 0xF84], + [0xF86, 0xF8C], + [0xF90, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFAD], + [0xFB1, 0xFB7], + [0xFB9, 0xFB9], + [0x1000, 0x102A], + [0x103F, 0x103F], + [0x1050, 0x1055], + [0x105A, 0x105D], + [0x1061, 0x1061], + [0x1065, 0x1066], + [0x106E, 0x1070], + [0x1075, 0x1081], + [0x108E, 0x108E], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1711], + [0x171F, 0x1731], + [0x1740, 0x1751], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1780, 0x17B3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DC], + [0x180F, 0x1878], + [0x1880, 0x18A8], + [0x18AA, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1950, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x1A00, 0x1A16], + [0x1A20, 0x1A54], + [0x1AA7, 0x1AA7], + [0x1B05, 0x1B33], + [0x1B45, 0x1B4C], + [0x1B83, 0x1BA0], + [0x1BAE, 0x1BAF], + [0x1BBA, 0x1BE5], + [0x1C00, 0x1C23], + [0x1C4D, 0x1C4F], + [0x1C5A, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CE9, 0x1CEC], + [0x1CEE, 0x1CF3], + [0x1CF5, 0x1CF6], + [0x1CFA, 0x1CFA], + [0x1D00, 0x1DBF], + [0x1E00, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2138], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CEE], + [0x2CF2, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D80, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x3029], + [0x3031, 0x3035], + [0x3038, 0x303C], + [0x3041, 0x3096], + [0x309B, 0x309F], + [0x30A1, 0x30FF], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA61F], + [0xA62A, 0xA62B], + [0xA640, 0xA66E], + [0xA67F, 0xA69D], + [0xA6A0, 0xA6EF], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA801], + [0xA803, 0xA805], + [0xA807, 0xA80A], + [0xA80C, 0xA822], + [0xA840, 0xA873], + [0xA882, 0xA8B3], + [0xA8F2, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA8FE], + [0xA90A, 0xA925], + [0xA930, 0xA946], + [0xA960, 0xA97C], + [0xA984, 0xA9B2], + [0xA9CF, 0xA9CF], + [0xA9E0, 0xA9E4], + [0xA9E6, 0xA9EF], + [0xA9FA, 0xA9FE], + [0xAA00, 0xAA28], + [0xAA40, 0xAA42], + [0xAA44, 0xAA4B], + [0xAA60, 0xAA76], + [0xAA7A, 0xAA7A], + [0xAA7E, 0xAAAF], + [0xAAB1, 0xAAB1], + [0xAAB5, 0xAAB6], + [0xAAB9, 0xAABD], + [0xAAC0, 0xAAC0], + [0xAAC2, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEA], + [0xAAF2, 0xAAF4], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABE2], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB1D], + [0xFB1F, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD40, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE47, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF21, 0xFF3A], + [0xFF41, 0xFF5A], + [0xFF66, 0xFF9D], + [0xFFA0, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x10375], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A00], + [0x10A10, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE4], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D23], + [0x10E80, 0x10EA9], + [0x10EB0, 0x10EB1], + [0x10F00, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F45], + [0x10F70, 0x10F81], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11003, 0x11037], + [0x11071, 0x11072], + [0x11075, 0x11075], + [0x11083, 0x110AF], + [0x110D0, 0x110E8], + [0x11103, 0x11126], + [0x11144, 0x11144], + [0x11147, 0x11147], + [0x11150, 0x11172], + [0x11176, 0x11176], + [0x11183, 0x111B2], + [0x111C1, 0x111C4], + [0x111DA, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x1122B], + [0x1123F, 0x11240], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112DE], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133D, 0x1133D], + [0x11350, 0x11350], + [0x1135D, 0x11361], + [0x11400, 0x11434], + [0x11447, 0x1144A], + [0x1145F, 0x11461], + [0x11480, 0x114AF], + [0x114C4, 0x114C5], + [0x114C7, 0x114C7], + [0x11580, 0x115AE], + [0x115D8, 0x115DB], + [0x11600, 0x1162F], + [0x11644, 0x11644], + [0x11680, 0x116AA], + [0x116B8, 0x116B8], + [0x11700, 0x1171A], + [0x11740, 0x11746], + [0x11800, 0x1182B], + [0x118A0, 0x118DF], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x1192F], + [0x1193F, 0x1193F], + [0x11941, 0x11941], + [0x119A0, 0x119A7], + [0x119AA, 0x119D0], + [0x119E1, 0x119E1], + [0x119E3, 0x119E3], + [0x11A00, 0x11A00], + [0x11A0B, 0x11A32], + [0x11A3A, 0x11A3A], + [0x11A50, 0x11A50], + [0x11A5C, 0x11A89], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C2E], + [0x11C40, 0x11C40], + [0x11C72, 0x11C8F], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D30], + [0x11D46, 0x11D46], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D89], + [0x11D98, 0x11D98], + [0x11EE0, 0x11EF2], + [0x11F02, 0x11F02], + [0x11F04, 0x11F10], + [0x11F12, 0x11F33], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13441, 0x13446], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A70, 0x16ABE], + [0x16AD0, 0x16AED], + [0x16B00, 0x16B2F], + [0x16B40, 0x16B43], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F50, 0x16F50], + [0x16F93, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE3], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E030, 0x1E06D], + [0x1E100, 0x1E12C], + [0x1E137, 0x1E13D], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AD], + [0x1E2C0, 0x1E2EB], + [0x1E4D0, 0x1E4EB], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E900, 0x1E943], + [0x1E94B, 0x1E94B], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x20000, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x323AF], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xEFFFD], +]; + +/** +Least restrictive Continue +Entries: 796056 +*/ +static immutable dchar[2][] LeastRestrictive_Continue = [ + [0xA8, 0xA8], + [0xAA, 0xAA], + [0xAD, 0xAD], + [0xAF, 0xAF], + [0xB2, 0xB5], + [0xB7, 0xBA], + [0xBC, 0xBE], + [0xC0, 0xD6], + [0xD8, 0xF6], + [0xF8, 0x217], + [0x250, 0x2A8], + [0x2B0, 0x2B8], + [0x2BB, 0x2BB], + [0x2BD, 0x2C1], + [0x2C6, 0x2D1], + [0x2E0, 0x2E4], + [0x2EC, 0x2EC], + [0x2EE, 0x2EE], + [0x300, 0x374], + [0x376, 0x377], + [0x37A, 0x37D], + [0x37F, 0x37F], + [0x386, 0x386], + [0x388, 0x38A], + [0x38C, 0x38C], + [0x38E, 0x3A1], + [0x3A3, 0x3D6], + [0x3DA, 0x3DA], + [0x3DC, 0x3DC], + [0x3DE, 0x3DE], + [0x3E0, 0x3E0], + [0x3E2, 0x3F3], + [0x3F7, 0x40C], + [0x40E, 0x44F], + [0x451, 0x45C], + [0x45E, 0x481], + [0x483, 0x487], + [0x48A, 0x4C4], + [0x4C7, 0x4C8], + [0x4CB, 0x4CC], + [0x4D0, 0x4EB], + [0x4EE, 0x4F5], + [0x4F8, 0x4F9], + [0x531, 0x556], + [0x559, 0x559], + [0x560, 0x587], + [0x591, 0x5B9], + [0x5BB, 0x5BD], + [0x5BF, 0x5BF], + [0x5C1, 0x5C2], + [0x5C4, 0x5C5], + [0x5C7, 0x5C7], + [0x5D0, 0x5EA], + [0x5EF, 0x5F2], + [0x610, 0x61A], + [0x620, 0x63A], + [0x640, 0x652], + [0x660, 0x669], + [0x66E, 0x6B7], + [0x6BA, 0x6BE], + [0x6C0, 0x6CE], + [0x6D0, 0x6DC], + [0x6DF, 0x6E8], + [0x6EA, 0x6ED], + [0x6F0, 0x6F9], + [0x6FF, 0x6FF], + [0x710, 0x74A], + [0x74D, 0x7B1], + [0x7C0, 0x7F5], + [0x7FA, 0x7FA], + [0x7FD, 0x7FD], + [0x800, 0x82D], + [0x840, 0x85B], + [0x860, 0x86A], + [0x870, 0x887], + [0x889, 0x88E], + [0x898, 0x8E1], + [0x8E3, 0x903], + [0x905, 0x939], + [0x93D, 0x94D], + [0x950, 0x952], + [0x958, 0x963], + [0x966, 0x96F], + [0x971, 0x983], + [0x985, 0x98C], + [0x98F, 0x990], + [0x993, 0x9A8], + [0x9AA, 0x9B0], + [0x9B2, 0x9B2], + [0x9B6, 0x9B9], + [0x9BC, 0x9C4], + [0x9C7, 0x9C8], + [0x9CB, 0x9CD], + [0x9D7, 0x9D7], + [0x9DC, 0x9DD], + [0x9DF, 0x9E3], + [0x9E6, 0x9F1], + [0x9FC, 0x9FC], + [0x9FE, 0x9FE], + [0xA01, 0xA02], + [0xA05, 0xA0A], + [0xA0F, 0xA10], + [0xA13, 0xA28], + [0xA2A, 0xA30], + [0xA32, 0xA33], + [0xA35, 0xA36], + [0xA38, 0xA39], + [0xA3C, 0xA3C], + [0xA3E, 0xA42], + [0xA47, 0xA48], + [0xA4B, 0xA4D], + [0xA51, 0xA51], + [0xA59, 0xA5C], + [0xA5E, 0xA5E], + [0xA66, 0xA6F], + [0xA74, 0xA74], + [0xA81, 0xA83], + [0xA85, 0xA8B], + [0xA8D, 0xA8D], + [0xA8F, 0xA91], + [0xA93, 0xAA8], + [0xAAA, 0xAB0], + [0xAB2, 0xAB3], + [0xAB5, 0xAB9], + [0xABC, 0xAC5], + [0xAC7, 0xAC9], + [0xACB, 0xACD], + [0xAD0, 0xAD0], + [0xAE0, 0xAE0], + [0xAE6, 0xAEF], + [0xAF9, 0xAFF], + [0xB01, 0xB03], + [0xB05, 0xB0C], + [0xB0F, 0xB10], + [0xB13, 0xB28], + [0xB2A, 0xB30], + [0xB32, 0xB33], + [0xB35, 0xB39], + [0xB3C, 0xB43], + [0xB47, 0xB48], + [0xB4B, 0xB4D], + [0xB55, 0xB57], + [0xB5C, 0xB5D], + [0xB5F, 0xB61], + [0xB66, 0xB6F], + [0xB71, 0xB71], + [0xB82, 0xB83], + [0xB85, 0xB8A], + [0xB8E, 0xB90], + [0xB92, 0xB95], + [0xB99, 0xB9A], + [0xB9C, 0xB9C], + [0xB9E, 0xB9F], + [0xBA3, 0xBA4], + [0xBA8, 0xBAA], + [0xBAE, 0xBB5], + [0xBB7, 0xBB9], + [0xBBE, 0xBC2], + [0xBC6, 0xBC8], + [0xBCA, 0xBCD], + [0xBD0, 0xBD0], + [0xBD7, 0xBD7], + [0xBE6, 0xBEF], + [0xC00, 0xC03], + [0xC05, 0xC0C], + [0xC0E, 0xC10], + [0xC12, 0xC28], + [0xC2A, 0xC33], + [0xC35, 0xC39], + [0xC3C, 0xC44], + [0xC46, 0xC48], + [0xC4A, 0xC4D], + [0xC55, 0xC56], + [0xC58, 0xC5A], + [0xC5D, 0xC5D], + [0xC60, 0xC61], + [0xC66, 0xC6F], + [0xC80, 0xC83], + [0xC85, 0xC8C], + [0xC8E, 0xC90], + [0xC92, 0xCA8], + [0xCAA, 0xCB3], + [0xCB5, 0xCB9], + [0xCBC, 0xCC4], + [0xCC6, 0xCC8], + [0xCCA, 0xCCD], + [0xCD5, 0xCD6], + [0xCDD, 0xCDE], + [0xCE0, 0xCE1], + [0xCE6, 0xCEF], + [0xCF1, 0xCF3], + [0xD00, 0xD03], + [0xD05, 0xD0C], + [0xD0E, 0xD10], + [0xD12, 0xD39], + [0xD3E, 0xD43], + [0xD46, 0xD48], + [0xD4A, 0xD4E], + [0xD54, 0xD57], + [0xD5F, 0xD61], + [0xD66, 0xD6F], + [0xD7A, 0xD7F], + [0xD81, 0xD83], + [0xD85, 0xD96], + [0xD9A, 0xDB1], + [0xDB3, 0xDBB], + [0xDBD, 0xDBD], + [0xDC0, 0xDC6], + [0xDCA, 0xDCA], + [0xDCF, 0xDD4], + [0xDD6, 0xDD6], + [0xDD8, 0xDDF], + [0xDE6, 0xDEF], + [0xDF2, 0xDF3], + [0xE01, 0xE3A], + [0xE40, 0xE4E], + [0xE50, 0xE59], + [0xE81, 0xE82], + [0xE84, 0xE84], + [0xE86, 0xE88], + [0xE8A, 0xE8A], + [0xE8C, 0xE8D], + [0xE94, 0xE97], + [0xE99, 0xE9F], + [0xEA1, 0xEA3], + [0xEA5, 0xEA5], + [0xEA7, 0xEAB], + [0xEAD, 0xEAE], + [0xEB0, 0xEB9], + [0xEBB, 0xEBD], + [0xEC0, 0xEC4], + [0xEC6, 0xEC6], + [0xEC8, 0xECE], + [0xED0, 0xED9], + [0xEDC, 0xEDF], + [0xF00, 0xF00], + [0xF18, 0xF19], + [0xF20, 0xF29], + [0xF35, 0xF35], + [0xF37, 0xF37], + [0xF39, 0xF39], + [0xF3E, 0xF47], + [0xF49, 0xF6C], + [0xF71, 0xF84], + [0xF86, 0xF95], + [0xF97, 0xF97], + [0xF99, 0xFB7], + [0xFB9, 0xFB9], + [0xFC6, 0xFC6], + [0x1000, 0x1049], + [0x1050, 0x109D], + [0x10A0, 0x10C5], + [0x10C7, 0x10C7], + [0x10CD, 0x10CD], + [0x10D0, 0x10FA], + [0x10FC, 0x1248], + [0x124A, 0x124D], + [0x1250, 0x1256], + [0x1258, 0x1258], + [0x125A, 0x125D], + [0x1260, 0x1288], + [0x128A, 0x128D], + [0x1290, 0x12B0], + [0x12B2, 0x12B5], + [0x12B8, 0x12BE], + [0x12C0, 0x12C0], + [0x12C2, 0x12C5], + [0x12C8, 0x12D6], + [0x12D8, 0x1310], + [0x1312, 0x1315], + [0x1318, 0x135A], + [0x135D, 0x135F], + [0x1369, 0x1371], + [0x1380, 0x138F], + [0x13A0, 0x13F5], + [0x13F8, 0x13FD], + [0x1401, 0x166C], + [0x166F, 0x167F], + [0x1681, 0x169A], + [0x16A0, 0x16EA], + [0x16EE, 0x16F8], + [0x1700, 0x1715], + [0x171F, 0x1734], + [0x1740, 0x1753], + [0x1760, 0x176C], + [0x176E, 0x1770], + [0x1772, 0x1773], + [0x1780, 0x17D3], + [0x17D7, 0x17D7], + [0x17DC, 0x17DD], + [0x17E0, 0x17E9], + [0x180B, 0x180D], + [0x180F, 0x1819], + [0x1820, 0x1878], + [0x1880, 0x18AA], + [0x18B0, 0x18F5], + [0x1900, 0x191E], + [0x1920, 0x192B], + [0x1930, 0x193B], + [0x1946, 0x196D], + [0x1970, 0x1974], + [0x1980, 0x19AB], + [0x19B0, 0x19C9], + [0x19D0, 0x19DA], + [0x1A00, 0x1A1B], + [0x1A20, 0x1A5E], + [0x1A60, 0x1A7C], + [0x1A7F, 0x1A89], + [0x1A90, 0x1A99], + [0x1AA7, 0x1AA7], + [0x1AB0, 0x1ABD], + [0x1ABF, 0x1ACE], + [0x1B00, 0x1B4C], + [0x1B50, 0x1B59], + [0x1B6B, 0x1B73], + [0x1B80, 0x1BF3], + [0x1C00, 0x1C37], + [0x1C40, 0x1C49], + [0x1C4D, 0x1C7D], + [0x1C80, 0x1C88], + [0x1C90, 0x1CBA], + [0x1CBD, 0x1CBF], + [0x1CD0, 0x1CD2], + [0x1CD4, 0x1CFA], + [0x1D00, 0x1EF9], + [0x1F00, 0x1F15], + [0x1F18, 0x1F1D], + [0x1F20, 0x1F45], + [0x1F48, 0x1F4D], + [0x1F50, 0x1F57], + [0x1F59, 0x1F59], + [0x1F5B, 0x1F5B], + [0x1F5D, 0x1F5D], + [0x1F5F, 0x1F7D], + [0x1F80, 0x1FB4], + [0x1FB6, 0x1FBC], + [0x1FBE, 0x1FBE], + [0x1FC2, 0x1FC4], + [0x1FC6, 0x1FCC], + [0x1FD0, 0x1FD3], + [0x1FD6, 0x1FDB], + [0x1FE0, 0x1FEC], + [0x1FF2, 0x1FF4], + [0x1FF6, 0x1FFC], + [0x200B, 0x200D], + [0x202A, 0x202E], + [0x203F, 0x2040], + [0x2054, 0x2054], + [0x2060, 0x2071], + [0x207F, 0x207F], + [0x2090, 0x209C], + [0x20D0, 0x20DC], + [0x20E1, 0x20E1], + [0x20E5, 0x20F0], + [0x2102, 0x2102], + [0x2107, 0x2107], + [0x210A, 0x2113], + [0x2115, 0x2115], + [0x2118, 0x211D], + [0x2124, 0x2124], + [0x2126, 0x2126], + [0x2128, 0x2128], + [0x212A, 0x2138], + [0x213C, 0x213F], + [0x2145, 0x2149], + [0x214E, 0x214E], + [0x2160, 0x2188], + [0x2460, 0x24FF], + [0x2776, 0x2793], + [0x2C00, 0x2CE4], + [0x2CEB, 0x2CF3], + [0x2D00, 0x2D25], + [0x2D27, 0x2D27], + [0x2D2D, 0x2D2D], + [0x2D30, 0x2D67], + [0x2D6F, 0x2D6F], + [0x2D7F, 0x2D96], + [0x2DA0, 0x2DA6], + [0x2DA8, 0x2DAE], + [0x2DB0, 0x2DB6], + [0x2DB8, 0x2DBE], + [0x2DC0, 0x2DC6], + [0x2DC8, 0x2DCE], + [0x2DD0, 0x2DD6], + [0x2DD8, 0x2DDE], + [0x2DE0, 0x2DFF], + [0x2E80, 0x2FFF], + [0x3004, 0x3007], + [0x3021, 0x302F], + [0x3031, 0x303C], + [0x3041, 0x3096], + [0x3099, 0x309F], + [0x30A1, 0x30FC], + [0x3105, 0x312F], + [0x3131, 0x318E], + [0x31A0, 0x31BF], + [0x31F0, 0x31FF], + [0x3400, 0x4DBF], + [0x4E00, 0xA48C], + [0xA4D0, 0xA4FD], + [0xA500, 0xA60C], + [0xA610, 0xA62B], + [0xA640, 0xA66F], + [0xA674, 0xA67D], + [0xA67F, 0xA6F1], + [0xA717, 0xA71F], + [0xA722, 0xA788], + [0xA78B, 0xA7CA], + [0xA7D0, 0xA7D1], + [0xA7D3, 0xA7D3], + [0xA7D5, 0xA7D9], + [0xA7F2, 0xA827], + [0xA82C, 0xA82C], + [0xA840, 0xA873], + [0xA880, 0xA8C5], + [0xA8D0, 0xA8D9], + [0xA8E0, 0xA8F7], + [0xA8FB, 0xA8FB], + [0xA8FD, 0xA92D], + [0xA930, 0xA953], + [0xA960, 0xA97C], + [0xA980, 0xA9C0], + [0xA9CF, 0xA9D9], + [0xA9E0, 0xA9FE], + [0xAA00, 0xAA36], + [0xAA40, 0xAA4D], + [0xAA50, 0xAA59], + [0xAA60, 0xAA76], + [0xAA7A, 0xAAC2], + [0xAADB, 0xAADD], + [0xAAE0, 0xAAEF], + [0xAAF2, 0xAAF6], + [0xAB01, 0xAB06], + [0xAB09, 0xAB0E], + [0xAB11, 0xAB16], + [0xAB20, 0xAB26], + [0xAB28, 0xAB2E], + [0xAB30, 0xAB5A], + [0xAB5C, 0xAB69], + [0xAB70, 0xABEA], + [0xABEC, 0xABED], + [0xABF0, 0xABF9], + [0xAC00, 0xD7A3], + [0xD7B0, 0xD7C6], + [0xD7CB, 0xD7FB], + [0xF900, 0xFA6D], + [0xFA70, 0xFAD9], + [0xFB00, 0xFB06], + [0xFB13, 0xFB17], + [0xFB1D, 0xFB28], + [0xFB2A, 0xFB36], + [0xFB38, 0xFB3C], + [0xFB3E, 0xFB3E], + [0xFB40, 0xFB41], + [0xFB43, 0xFB44], + [0xFB46, 0xFBB1], + [0xFBD3, 0xFC5D], + [0xFC64, 0xFD3D], + [0xFD40, 0xFD8F], + [0xFD92, 0xFDC7], + [0xFDF0, 0xFDF9], + [0xFE00, 0xFE0F], + [0xFE20, 0xFE2F], + [0xFE33, 0xFE34], + [0xFE47, 0xFE4F], + [0xFE71, 0xFE71], + [0xFE73, 0xFE73], + [0xFE77, 0xFE77], + [0xFE79, 0xFE79], + [0xFE7B, 0xFE7B], + [0xFE7D, 0xFE7D], + [0xFE7F, 0xFEFC], + [0xFF10, 0xFF19], + [0xFF21, 0xFF3A], + [0xFF3F, 0xFF3F], + [0xFF41, 0xFF5A], + [0xFF65, 0xFFBE], + [0xFFC2, 0xFFC7], + [0xFFCA, 0xFFCF], + [0xFFD2, 0xFFD7], + [0xFFDA, 0xFFDC], + [0x10000, 0x1000B], + [0x1000D, 0x10026], + [0x10028, 0x1003A], + [0x1003C, 0x1003D], + [0x1003F, 0x1004D], + [0x10050, 0x1005D], + [0x10080, 0x100FA], + [0x10140, 0x10174], + [0x101FD, 0x101FD], + [0x10280, 0x1029C], + [0x102A0, 0x102D0], + [0x102E0, 0x102E0], + [0x10300, 0x1031F], + [0x1032D, 0x1034A], + [0x10350, 0x1037A], + [0x10380, 0x1039D], + [0x103A0, 0x103C3], + [0x103C8, 0x103CF], + [0x103D1, 0x103D5], + [0x10400, 0x1049D], + [0x104A0, 0x104A9], + [0x104B0, 0x104D3], + [0x104D8, 0x104FB], + [0x10500, 0x10527], + [0x10530, 0x10563], + [0x10570, 0x1057A], + [0x1057C, 0x1058A], + [0x1058C, 0x10592], + [0x10594, 0x10595], + [0x10597, 0x105A1], + [0x105A3, 0x105B1], + [0x105B3, 0x105B9], + [0x105BB, 0x105BC], + [0x10600, 0x10736], + [0x10740, 0x10755], + [0x10760, 0x10767], + [0x10780, 0x10785], + [0x10787, 0x107B0], + [0x107B2, 0x107BA], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080A, 0x10835], + [0x10837, 0x10838], + [0x1083C, 0x1083C], + [0x1083F, 0x10855], + [0x10860, 0x10876], + [0x10880, 0x1089E], + [0x108E0, 0x108F2], + [0x108F4, 0x108F5], + [0x10900, 0x10915], + [0x10920, 0x10939], + [0x10980, 0x109B7], + [0x109BE, 0x109BF], + [0x10A00, 0x10A03], + [0x10A05, 0x10A06], + [0x10A0C, 0x10A13], + [0x10A15, 0x10A17], + [0x10A19, 0x10A35], + [0x10A38, 0x10A3A], + [0x10A3F, 0x10A3F], + [0x10A60, 0x10A7C], + [0x10A80, 0x10A9C], + [0x10AC0, 0x10AC7], + [0x10AC9, 0x10AE6], + [0x10B00, 0x10B35], + [0x10B40, 0x10B55], + [0x10B60, 0x10B72], + [0x10B80, 0x10B91], + [0x10C00, 0x10C48], + [0x10C80, 0x10CB2], + [0x10CC0, 0x10CF2], + [0x10D00, 0x10D27], + [0x10D30, 0x10D39], + [0x10E80, 0x10EA9], + [0x10EAB, 0x10EAC], + [0x10EB0, 0x10EB1], + [0x10EFD, 0x10F1C], + [0x10F27, 0x10F27], + [0x10F30, 0x10F50], + [0x10F70, 0x10F85], + [0x10FB0, 0x10FC4], + [0x10FE0, 0x10FF6], + [0x11000, 0x11046], + [0x11066, 0x11075], + [0x1107F, 0x110BA], + [0x110C2, 0x110C2], + [0x110D0, 0x110E8], + [0x110F0, 0x110F9], + [0x11100, 0x11134], + [0x11136, 0x1113F], + [0x11144, 0x11147], + [0x11150, 0x11173], + [0x11176, 0x11176], + [0x11180, 0x111C4], + [0x111C9, 0x111CC], + [0x111CE, 0x111DA], + [0x111DC, 0x111DC], + [0x11200, 0x11211], + [0x11213, 0x11237], + [0x1123E, 0x11241], + [0x11280, 0x11286], + [0x11288, 0x11288], + [0x1128A, 0x1128D], + [0x1128F, 0x1129D], + [0x1129F, 0x112A8], + [0x112B0, 0x112EA], + [0x112F0, 0x112F9], + [0x11300, 0x11303], + [0x11305, 0x1130C], + [0x1130F, 0x11310], + [0x11313, 0x11328], + [0x1132A, 0x11330], + [0x11332, 0x11333], + [0x11335, 0x11339], + [0x1133B, 0x11344], + [0x11347, 0x11348], + [0x1134B, 0x1134D], + [0x11350, 0x11350], + [0x11357, 0x11357], + [0x1135D, 0x11363], + [0x11366, 0x1136C], + [0x11370, 0x11374], + [0x11400, 0x1144A], + [0x11450, 0x11459], + [0x1145E, 0x11461], + [0x11480, 0x114C5], + [0x114C7, 0x114C7], + [0x114D0, 0x114D9], + [0x11580, 0x115B5], + [0x115B8, 0x115C0], + [0x115D8, 0x115DD], + [0x11600, 0x11640], + [0x11644, 0x11644], + [0x11650, 0x11659], + [0x11680, 0x116B8], + [0x116C0, 0x116C9], + [0x11700, 0x1171A], + [0x1171D, 0x1172B], + [0x11730, 0x11739], + [0x11740, 0x11746], + [0x11800, 0x1183A], + [0x118A0, 0x118E9], + [0x118FF, 0x11906], + [0x11909, 0x11909], + [0x1190C, 0x11913], + [0x11915, 0x11916], + [0x11918, 0x11935], + [0x11937, 0x11938], + [0x1193B, 0x11943], + [0x11950, 0x11959], + [0x119A0, 0x119A7], + [0x119AA, 0x119D7], + [0x119DA, 0x119E1], + [0x119E3, 0x119E4], + [0x11A00, 0x11A3E], + [0x11A47, 0x11A47], + [0x11A50, 0x11A99], + [0x11A9D, 0x11A9D], + [0x11AB0, 0x11AF8], + [0x11C00, 0x11C08], + [0x11C0A, 0x11C36], + [0x11C38, 0x11C40], + [0x11C50, 0x11C59], + [0x11C72, 0x11C8F], + [0x11C92, 0x11CA7], + [0x11CA9, 0x11CB6], + [0x11D00, 0x11D06], + [0x11D08, 0x11D09], + [0x11D0B, 0x11D36], + [0x11D3A, 0x11D3A], + [0x11D3C, 0x11D3D], + [0x11D3F, 0x11D47], + [0x11D50, 0x11D59], + [0x11D60, 0x11D65], + [0x11D67, 0x11D68], + [0x11D6A, 0x11D8E], + [0x11D90, 0x11D91], + [0x11D93, 0x11D98], + [0x11DA0, 0x11DA9], + [0x11EE0, 0x11EF6], + [0x11F00, 0x11F10], + [0x11F12, 0x11F3A], + [0x11F3E, 0x11F42], + [0x11F50, 0x11F59], + [0x11FB0, 0x11FB0], + [0x12000, 0x12399], + [0x12400, 0x1246E], + [0x12480, 0x12543], + [0x12F90, 0x12FF0], + [0x13000, 0x1342F], + [0x13440, 0x13455], + [0x14400, 0x14646], + [0x16800, 0x16A38], + [0x16A40, 0x16A5E], + [0x16A60, 0x16A69], + [0x16A70, 0x16ABE], + [0x16AC0, 0x16AC9], + [0x16AD0, 0x16AED], + [0x16AF0, 0x16AF4], + [0x16B00, 0x16B36], + [0x16B40, 0x16B43], + [0x16B50, 0x16B59], + [0x16B63, 0x16B77], + [0x16B7D, 0x16B8F], + [0x16E40, 0x16E7F], + [0x16F00, 0x16F4A], + [0x16F4F, 0x16F87], + [0x16F8F, 0x16F9F], + [0x16FE0, 0x16FE1], + [0x16FE3, 0x16FE4], + [0x16FF0, 0x16FF1], + [0x17000, 0x187F7], + [0x18800, 0x18CD5], + [0x18D00, 0x18D08], + [0x1AFF0, 0x1AFF3], + [0x1AFF5, 0x1AFFB], + [0x1AFFD, 0x1AFFE], + [0x1B000, 0x1B122], + [0x1B132, 0x1B132], + [0x1B150, 0x1B152], + [0x1B155, 0x1B155], + [0x1B164, 0x1B167], + [0x1B170, 0x1B2FB], + [0x1BC00, 0x1BC6A], + [0x1BC70, 0x1BC7C], + [0x1BC80, 0x1BC88], + [0x1BC90, 0x1BC99], + [0x1BC9D, 0x1BC9E], + [0x1CF00, 0x1CF2D], + [0x1CF30, 0x1CF46], + [0x1D165, 0x1D169], + [0x1D16D, 0x1D172], + [0x1D17B, 0x1D182], + [0x1D185, 0x1D18B], + [0x1D1AA, 0x1D1AD], + [0x1D242, 0x1D244], + [0x1D400, 0x1D454], + [0x1D456, 0x1D49C], + [0x1D49E, 0x1D49F], + [0x1D4A2, 0x1D4A2], + [0x1D4A5, 0x1D4A6], + [0x1D4A9, 0x1D4AC], + [0x1D4AE, 0x1D4B9], + [0x1D4BB, 0x1D4BB], + [0x1D4BD, 0x1D4C3], + [0x1D4C5, 0x1D505], + [0x1D507, 0x1D50A], + [0x1D50D, 0x1D514], + [0x1D516, 0x1D51C], + [0x1D51E, 0x1D539], + [0x1D53B, 0x1D53E], + [0x1D540, 0x1D544], + [0x1D546, 0x1D546], + [0x1D54A, 0x1D550], + [0x1D552, 0x1D6A5], + [0x1D6A8, 0x1D6C0], + [0x1D6C2, 0x1D6DA], + [0x1D6DC, 0x1D6FA], + [0x1D6FC, 0x1D714], + [0x1D716, 0x1D734], + [0x1D736, 0x1D74E], + [0x1D750, 0x1D76E], + [0x1D770, 0x1D788], + [0x1D78A, 0x1D7A8], + [0x1D7AA, 0x1D7C2], + [0x1D7C4, 0x1D7CB], + [0x1D7CE, 0x1D7FF], + [0x1DA00, 0x1DA36], + [0x1DA3B, 0x1DA6C], + [0x1DA75, 0x1DA75], + [0x1DA84, 0x1DA84], + [0x1DA9B, 0x1DA9F], + [0x1DAA1, 0x1DAAF], + [0x1DF00, 0x1DF1E], + [0x1DF25, 0x1DF2A], + [0x1E000, 0x1E006], + [0x1E008, 0x1E018], + [0x1E01B, 0x1E021], + [0x1E023, 0x1E024], + [0x1E026, 0x1E02A], + [0x1E030, 0x1E06D], + [0x1E08F, 0x1E08F], + [0x1E100, 0x1E12C], + [0x1E130, 0x1E13D], + [0x1E140, 0x1E149], + [0x1E14E, 0x1E14E], + [0x1E290, 0x1E2AE], + [0x1E2C0, 0x1E2F9], + [0x1E4D0, 0x1E4F9], + [0x1E7E0, 0x1E7E6], + [0x1E7E8, 0x1E7EB], + [0x1E7ED, 0x1E7EE], + [0x1E7F0, 0x1E7FE], + [0x1E800, 0x1E8C4], + [0x1E8D0, 0x1E8D6], + [0x1E900, 0x1E94B], + [0x1E950, 0x1E959], + [0x1EE00, 0x1EE03], + [0x1EE05, 0x1EE1F], + [0x1EE21, 0x1EE22], + [0x1EE24, 0x1EE24], + [0x1EE27, 0x1EE27], + [0x1EE29, 0x1EE32], + [0x1EE34, 0x1EE37], + [0x1EE39, 0x1EE39], + [0x1EE3B, 0x1EE3B], + [0x1EE42, 0x1EE42], + [0x1EE47, 0x1EE47], + [0x1EE49, 0x1EE49], + [0x1EE4B, 0x1EE4B], + [0x1EE4D, 0x1EE4F], + [0x1EE51, 0x1EE52], + [0x1EE54, 0x1EE54], + [0x1EE57, 0x1EE57], + [0x1EE59, 0x1EE59], + [0x1EE5B, 0x1EE5B], + [0x1EE5D, 0x1EE5D], + [0x1EE5F, 0x1EE5F], + [0x1EE61, 0x1EE62], + [0x1EE64, 0x1EE64], + [0x1EE67, 0x1EE6A], + [0x1EE6C, 0x1EE72], + [0x1EE74, 0x1EE77], + [0x1EE79, 0x1EE7C], + [0x1EE7E, 0x1EE7E], + [0x1EE80, 0x1EE89], + [0x1EE8B, 0x1EE9B], + [0x1EEA1, 0x1EEA3], + [0x1EEA5, 0x1EEA9], + [0x1EEAB, 0x1EEBB], + [0x1FBF0, 0x1FBF9], + [0x20000, 0x2B739], + [0x2B740, 0x2B81D], + [0x2B820, 0x2CEA1], + [0x2CEB0, 0x2EBE0], + [0x2EBF0, 0x2EE5D], + [0x2F800, 0x2FA1D], + [0x30000, 0x323AF], + [0x40000, 0x4FFFD], + [0x50000, 0x5FFFD], + [0x60000, 0x6FFFD], + [0x70000, 0x7FFFD], + [0x80000, 0x8FFFD], + [0x90000, 0x9FFFD], + [0xA0000, 0xAFFFD], + [0xB0000, 0xBFFFD], + [0xC0000, 0xCFFFD], + [0xD0000, 0xDFFFD], + [0xE0000, 0xE01EF], +]; diff --git a/gcc/d/dmd/common/outbuffer.d b/gcc/d/dmd/common/outbuffer.d index cb2439f..01fc377 100644 --- a/gcc/d/dmd/common/outbuffer.d +++ b/gcc/d/dmd/common/outbuffer.d @@ -1,12 +1,12 @@ /** * An expandable buffer in which you can write text or binary data. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/outbuffer.d, root/_outbuffer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/outbuffer.d, root/_outbuffer.d) * Documentation: https://dlang.org/phobos/dmd_root_outbuffer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/outbuffer.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/outbuffer.d */ module dmd.common.outbuffer; @@ -141,7 +141,7 @@ struct OutBuffer memory buffer. The config variables `notlinehead`, `doindent` etc. are not changed. */ - extern (C++) void destroy() pure nothrow @trusted + extern (C++) void destroy() pure nothrow { dtor(); fileMapping = null; @@ -247,7 +247,7 @@ struct OutBuffer /** * Writes a 16 bit value, no reserve check. */ - @trusted nothrow + nothrow @safe void write16n(int v) { auto x = cast(ushort) v; @@ -367,8 +367,7 @@ struct OutBuffer } // Position buffer to accept the specified number of bytes at offset - @trusted - void position(size_t where, size_t nbytes) nothrow + void position(size_t where, size_t nbytes) nothrow @safe { if (where + nbytes > data.length) { @@ -382,7 +381,7 @@ struct OutBuffer /** * Writes an 8 bit byte, no reserve check. */ - extern (C++) @trusted nothrow + extern (C++) nothrow @safe void writeByten(int b) { this.data[offset++] = cast(ubyte) b; @@ -786,10 +785,11 @@ struct OutBuffer Returns: `true` iff the operation succeeded. */ - extern(D) bool moveToFile(const char* filename) @system + extern(D) bool moveToFile(const char[] filename) @system { bool result = true; - const bool identical = this[] == FileMapping!(const ubyte)(filename)[]; + const filenameZ = (filename ~ "\0").ptr; + const bool identical = this[] == FileMapping!(const ubyte)(filenameZ)[]; if (fileMapping && fileMapping.active) { @@ -802,7 +802,7 @@ struct OutBuffer { // Resize to fit to get rid of the slack bytes at the end fileMapping.resize(offset); - result = fileMapping.moveToFile(filename); + result = fileMapping.moveToFile(filenameZ); } // Can't call destroy() here because the file mapping is already closed. data = null; @@ -811,12 +811,12 @@ struct OutBuffer else { if (!identical) - writeFile(filename, this[]); + writeFile(filenameZ, this[]); destroy(); } return identical - ? result && touchFile(filename) + ? result && touchFile(filenameZ) : result; } } diff --git a/gcc/d/dmd/common/outbuffer.h b/gcc/d/dmd/common/outbuffer.h index 2250497..62aca7a 100644 --- a/gcc/d/dmd/common/outbuffer.h +++ b/gcc/d/dmd/common/outbuffer.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/common/smallbuffer.d b/gcc/d/dmd/common/smallbuffer.d index 608ecc8..65adec6 100644 --- a/gcc/d/dmd/common/smallbuffer.d +++ b/gcc/d/dmd/common/smallbuffer.d @@ -1,12 +1,12 @@ /** * Common string functions including filename manipulation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/common/smallbuffer.d, common/_smallbuffer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/common/smallbuffer.d, common/_smallbuffer.d) * Documentation: https://dlang.org/phobos/dmd_common_smallbuffer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/common/smallbuffer + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/common/smallbuffer.d */ module dmd.common.smallbuffer; diff --git a/gcc/d/dmd/compiler.d b/gcc/d/dmd/compiler.d index 65330cf..8203e0c 100644 --- a/gcc/d/dmd/compiler.d +++ b/gcc/d/dmd/compiler.d @@ -1,12 +1,12 @@ /** * Describes a back-end compiler and implements compiler-specific actions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/compiler.d, _compiler.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/compiler.d, _compiler.d) * Documentation: https://dlang.org/phobos/dmd_compiler.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/compiler.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/compiler.d */ module dmd.compiler; @@ -16,6 +16,7 @@ import dmd.ctfeexpr; import dmd.dmodule; import dmd.expression; import dmd.mtype; +import dmd.typesem; import dmd.root.array; extern (C++) __gshared diff --git a/gcc/d/dmd/compiler.h b/gcc/d/dmd/compiler.h index 74351ed..ca085c6 100644 --- a/gcc/d/dmd/compiler.h +++ b/gcc/d/dmd/compiler.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/cond.d b/gcc/d/dmd/cond.d index e194664..1b11a9f 100644 --- a/gcc/d/dmd/cond.d +++ b/gcc/d/dmd/cond.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/version.html, Conditional Compilation) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cond.d, _cond.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cond.d, _cond.d) * Documentation: https://dlang.org/phobos/dmd_cond.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cond.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cond.d */ module dmd.cond; @@ -24,7 +24,7 @@ import dmd.dscope; import dmd.dsymbol; import dmd.errors; import dmd.expression; -import dmd.expressionsem; +import dmd.expressionsem : evalStaticCondition; import dmd.globals; import dmd.identifier; import dmd.location; @@ -64,7 +64,7 @@ extern (C++) abstract class Condition : ASTNode return DYNCAST.condition; } - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { this.loc = loc; } @@ -126,7 +126,7 @@ extern (C++) final class StaticForeach : RootObject */ bool needExpansion = false; - extern (D) this(const ref Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe) @safe + extern (D) this(Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe) @safe { assert(!!aggrfe ^ !!rangefe); @@ -145,52 +145,6 @@ extern (C++) final class StaticForeach : RootObject } /***************************************** - * Turn an aggregate which is an array into an expression tuple - * of its elements. I.e., lower - * static foreach (x; [1, 2, 3, 4]) { ... } - * to - * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } - */ - private extern(D) void lowerArrayAggregate(Scope* sc) - { - auto aggr = aggrfe.aggr; - Expression el = new ArrayLengthExp(aggr.loc, aggr); - sc = sc.startCTFE(); - el = el.expressionSemantic(sc); - sc = sc.endCTFE(); - el = el.optimize(WANTvalue); - el = el.ctfeInterpret(); - if (el.op == EXP.int64) - { - Expressions *es = void; - if (auto ale = aggr.isArrayLiteralExp()) - { - // Directly use the elements of the array for the TupleExp creation - es = ale.elements; - } - else - { - const length = cast(size_t)el.toInteger(); - es = new Expressions(length); - foreach (i; 0 .. length) - { - auto index = new IntegerExp(loc, i, Type.tsize_t); - auto value = new IndexExp(aggr.loc, aggr, index); - (*es)[i] = value; - } - } - aggrfe.aggr = new TupleExp(aggr.loc, es); - aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); - aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); - aggrfe.aggr = aggrfe.aggr.ctfeInterpret(); - } - else - { - aggrfe.aggr = ErrorExp.get(); - } - } - - /***************************************** * Wrap a statement into a function literal and call it. * * Params: @@ -199,9 +153,9 @@ extern (C++) final class StaticForeach : RootObject * Returns: * AST of the expression `(){ s; }()` with location loc. */ - private extern(D) Expression wrapAndCall(const ref Loc loc, Statement s) + extern(D) Expression wrapAndCall(Loc loc, Statement s) { - auto tf = new TypeFunction(ParameterList(), null, LINK.default_, 0); + auto tf = new TypeFunction(ParameterList(), null, LINK.default_, STC.none); auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null); fd.fbody = s; auto fe = new FuncExp(loc, fd); @@ -222,7 +176,7 @@ extern (C++) final class StaticForeach : RootObject * `foreach (parameters; lower .. upper) s;` * Where aggregate/lower, upper are as for the current StaticForeach. */ - private extern(D) Statement createForeach(const ref Loc loc, Parameters* parameters, Statement s) + extern(D) Statement createForeach(Loc loc, Parameters* parameters, Statement s) { if (aggrfe) { @@ -255,7 +209,7 @@ extern (C++) final class StaticForeach : RootObject * } */ - private extern(D) TypeStruct createTupleType(const ref Loc loc, Expressions* e, Scope* sc) + extern(D) TypeStruct createTupleType(Loc loc, Expressions* e, Scope* sc) { // TODO: move to druntime? auto sid = Identifier.generateId("Tuple"); auto sdecl = new StructDeclaration(loc, sid, false); @@ -263,7 +217,7 @@ extern (C++) final class StaticForeach : RootObject sdecl.members = new Dsymbols(); auto fid = Identifier.idPool(tupleFieldName); auto ty = new TypeTypeof(loc, new TupleExp(loc, e)); - sdecl.members.push(new VarDeclaration(loc, ty, fid, null, 0)); + sdecl.members.push(new VarDeclaration(loc, ty, fid, null, STC.none)); auto r = cast(TypeStruct)sdecl.type; if (global.params.useTypeInfo && Type.dtypeinfo) r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file @@ -281,205 +235,11 @@ extern (C++) final class StaticForeach : RootObject * An AST for the expression `Tuple(e)`. */ - private extern(D) Expression createTuple(const ref Loc loc, TypeStruct type, Expressions* e) @safe + extern(D) Expression createTuple(Loc loc, TypeStruct type, Expressions* e) @safe { // TODO: move to druntime? return new CallExp(loc, new TypeExp(loc, type), e); } - - /***************************************** - * Lower any aggregate that is not an array to an array using a - * regular foreach loop within CTFE. If there are multiple - * `static foreach` loop variables, an array of tuples is - * generated. In thise case, the field `needExpansion` is set to - * true to indicate that the static foreach loop expansion will - * need to expand the tuples into multiple variables. - * - * For example, `static foreach (x; range) { ... }` is lowered to: - * - * static foreach (x; { - * typeof({ - * foreach (x; range) return x; - * }())[] __res; - * foreach (x; range) __res ~= x; - * return __res; - * }()) { ... } - * - * Finally, call `lowerArrayAggregate` to turn the produced - * array into an expression tuple. - * - * Params: - * sc = The current scope. - */ - - private void lowerNonArrayAggregate(Scope* sc) - { - auto nvars = aggrfe ? aggrfe.parameters.length : 1; - auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc; - // We need three sets of foreach loop variables because the - // lowering contains three foreach loops. - Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; - foreach (i; 0 .. nvars) - { - foreach (params; pparams) - { - auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm; - params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null)); - } - } - Expression[2] res; - TypeStruct tplty = null; - if (nvars == 1) // only one `static foreach` variable, generate identifiers. - { - foreach (i; 0 .. 2) - { - res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident); - } - } - else // multiple `static foreach` variables, generate tuples. - { - foreach (i; 0 .. 2) - { - auto e = new Expressions(pparams[0].length); - foreach (j, ref elem; *e) - { - auto p = (*pparams[i])[j]; - elem = new IdentifierExp(aloc, p.ident); - } - if (!tplty) - { - tplty = createTupleType(aloc, e, sc); - } - res[i] = createTuple(aloc, tplty, e); - } - needExpansion = true; // need to expand the tuples later - } - // generate remaining code for the new aggregate which is an - // array (see documentation comment). - if (rangefe) - { - sc = sc.startCTFE(); - rangefe.lwr = rangefe.lwr.expressionSemantic(sc); - rangefe.lwr = resolveProperties(sc, rangefe.lwr); - rangefe.upr = rangefe.upr.expressionSemantic(sc); - rangefe.upr = resolveProperties(sc, rangefe.upr); - sc = sc.endCTFE(); - rangefe.lwr = rangefe.lwr.optimize(WANTvalue); - rangefe.lwr = rangefe.lwr.ctfeInterpret(); - rangefe.upr = rangefe.upr.optimize(WANTvalue); - rangefe.upr = rangefe.upr.ctfeInterpret(); - } - auto s1 = new Statements(); - auto sfe = new Statements(); - if (tplty) sfe.push(new ExpStatement(loc, tplty.sym)); - sfe.push(new ReturnStatement(aloc, res[0])); - s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe))); - s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); - Type ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1))); - auto aty = ety.arrayOf(); - auto idres = Identifier.generateId("__res"); - auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp); - auto s2 = new Statements(); - - // Run 'typeof' gagged to avoid duplicate errors and if it fails just create - // an empty foreach to expose them. - uint olderrors = global.startGagging(); - ety = ety.typeSemantic(aloc, sc); - if (global.endGagging(olderrors)) - s2.push(createForeach(aloc, pparams[1], null)); - else - { - s2.push(new ExpStatement(aloc, vard)); - auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); - s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass))); - s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); - } - - Expression aggr = void; - Type indexty = void; - - if (rangefe && (indexty = ety).isintegral()) - { - rangefe.lwr.type = indexty; - rangefe.upr.type = indexty; - auto lwrRange = getIntRange(rangefe.lwr); - auto uprRange = getIntRange(rangefe.upr); - - const lwr = rangefe.lwr.toInteger(); - auto upr = rangefe.upr.toInteger(); - size_t length = 0; - - if (lwrRange.imin <= uprRange.imax) - length = cast(size_t) (upr - lwr); - - auto exps = new Expressions(length); - - if (rangefe.op == TOK.foreach_) - { - foreach (i; 0 .. length) - (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty); - } - else - { - --upr; - foreach (i; 0 .. length) - (*exps)[i] = new IntegerExp(aloc, upr - i, indexty); - } - aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps); - } - else - { - aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2)); - sc = sc.startCTFE(); - aggr = aggr.expressionSemantic(sc); - aggr = resolveProperties(sc, aggr); - sc = sc.endCTFE(); - aggr = aggr.optimize(WANTvalue); - aggr = aggr.ctfeInterpret(); - } - - assert(!!aggrfe ^ !!rangefe); - aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr, - aggrfe ? aggrfe._body : rangefe._body, - aggrfe ? aggrfe.endloc : rangefe.endloc); - rangefe = null; - lowerArrayAggregate(sc); // finally, turn generated array into expression tuple - } - - /***************************************** - * Perform `static foreach` lowerings that are necessary in order - * to finally expand the `static foreach` using - * `dmd.statementsem.makeTupleForeach`. - */ - extern(D) void prepare(Scope* sc) - { - assert(sc); - - if (aggrfe) - { - sc = sc.startCTFE(); - aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); - sc = sc.endCTFE(); - } - - if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror) - { - return; - } - - if (!ready()) - { - if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray) - { - lowerArrayAggregate(sc); - } - else - { - lowerNonArrayAggregate(sc); - } - } - } - /***************************************** * Returns: * `true` iff ready to call `dmd.statementsem.makeTupleForeach`. @@ -494,15 +254,13 @@ extern (C++) final class StaticForeach : RootObject */ extern (C++) class DVCondition : Condition { - uint level; Identifier ident; Module mod; - extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe + extern (D) this(Loc loc, Module mod, Identifier ident) @safe { super(loc); this.mod = mod; - this.level = level; this.ident = ident; } @@ -557,45 +315,45 @@ extern (C++) final class DebugCondition : DVCondition * * Params: * mod = Module this node belongs to - * level = Minimum global level this condition needs to pass. - * Only used if `ident` is `null`. * ident = Identifier required for this condition to pass. * If `null`, this conditiion will use an integer level. * loc = Location in the source file */ - extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe + extern (D) this(Loc loc, Module mod, Identifier ident) @safe { - super(loc, mod, level, ident); + super(loc, mod, ident); } override int include(Scope* sc) { //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - inc = Include.no; - bool definedInModule = false; - if (ident) + return inc == Include.yes; + } + inc = Include.no; + bool definedInModule = false; + if (ident) + { + if (mod.debugids && findCondition(*mod.debugids, ident)) { - if (mod.debugids && findCondition(*mod.debugids, ident)) - { - inc = Include.yes; - definedInModule = true; - } - else if (findCondition(global.debugids, ident)) - inc = Include.yes; - else - { - if (!mod.debugidsNot) - mod.debugidsNot = new Identifiers(); - mod.debugidsNot.push(ident); - } + inc = Include.yes; + definedInModule = true; } - else if (level <= global.params.debuglevel || level <= mod.debuglevel) + else if (findCondition(global.debugids, ident)) inc = Include.yes; - if (!definedInModule) - printDepsConditional(sc, this, "depsDebug "); + else + { + if (!mod.debugidsNot) + mod.debugidsNot = new Identifiers(); + mod.debugidsNot.push(ident); + } } + else if (global.params.debugEnabled) + inc = Include.yes; + + if (!definedInModule) + printDepsConditional(sc, this, "depsDebug "); return (inc == Include.yes); } @@ -608,11 +366,6 @@ extern (C++) final class DebugCondition : DVCondition { v.visit(this); } - - override const(char)* toChars() const - { - return ident ? ident.toChars() : "debug".ptr; - } } /** @@ -659,9 +412,9 @@ extern (C++) final class VersionCondition : DVCondition case "AVR": case "BigEndian": case "BSD": - case "CppRuntime_Clang": + case "CppRuntime_LLVM": case "CppRuntime_DigitalMars": - case "CppRuntime_Gcc": + case "CppRuntime_GNU": case "CppRuntime_Microsoft": case "CppRuntime_Sun": case "CRuntime_Bionic": @@ -744,6 +497,7 @@ extern (C++) final class VersionCondition : DVCondition case "Win32": case "Win64": case "Windows": + case "Xtensa": case "X86": case "X86_64": return true; @@ -764,7 +518,7 @@ extern (C++) final class VersionCondition : DVCondition * loc = Where the identifier is set * ident = identifier being checked (ident[$] must be '\0') */ - extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident) + extern(D) static void checkReserved(Loc loc, const(char)[] ident) { if (isReserved(ident)) error(loc, "version identifier `%s` is reserved and cannot be set", @@ -836,49 +590,47 @@ extern (C++) final class VersionCondition : DVCondition * * Params: * mod = Module this node belongs to - * level = Minimum global level this condition needs to pass. - * Only used if `ident` is `null`. * ident = Identifier required for this condition to pass. * If `null`, this conditiion will use an integer level. * loc = Location in the source file */ - extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe + extern (D) this(Loc loc, Module mod, Identifier ident) @safe { - super(loc, mod, level, ident); + super(loc, mod, ident); } override int include(Scope* sc) { //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); //if (ident) printf("\tident = '%s'\n", ident.toChars()); - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - inc = Include.no; - bool definedInModule = false; - if (ident) + return inc == Include.yes; + } + + inc = Include.no; + bool definedInModule = false; + if (ident) + { + if (mod.versionids && findCondition(*mod.versionids, ident)) { - if (mod.versionids && findCondition(*mod.versionids, ident)) - { - inc = Include.yes; - definedInModule = true; - } - else if (findCondition(global.versionids, ident)) - inc = Include.yes; - else - { - if (!mod.versionidsNot) - mod.versionidsNot = new Identifiers(); - mod.versionidsNot.push(ident); - } + inc = Include.yes; + definedInModule = true; } - else if (level <= global.params.versionlevel || level <= mod.versionlevel) + else if (findCondition(global.versionids, ident)) inc = Include.yes; - if (!definedInModule && - (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) + else { - printDepsConditional(sc, this, "depsVersion "); + if (!mod.versionidsNot) + mod.versionidsNot = new Identifiers(); + mod.versionidsNot.push(ident); } } + if (!definedInModule && + (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) + { + printDepsConditional(sc, this, "depsVersion "); + } return (inc == Include.yes); } @@ -891,11 +643,6 @@ extern (C++) final class VersionCondition : DVCondition { v.visit(this); } - - override const(char)* toChars() const - { - return ident ? ident.toChars() : "version".ptr; - } } /*********************************************************** @@ -904,7 +651,7 @@ extern (C++) final class StaticIfCondition : Condition { Expression exp; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc); this.exp = exp; @@ -926,31 +673,33 @@ extern (C++) final class StaticIfCondition : Condition return 0; } - if (inc == Include.notComputed) + if (inc != Include.notComputed) { - if (!sc) - { - error(loc, "`static if` conditional cannot be at global scope"); - inc = Include.no; - return 0; - } + return inc == Include.yes; + } - import dmd.staticcond; - bool errors; + if (!sc) + { + error(loc, "`static if` conditional cannot be at global scope"); + inc = Include.no; + return 0; + } - bool result = evalStaticCondition(sc, exp, exp, errors); + import dmd.staticcond; + bool errors; - // Prevent repeated condition evaluation. - // See: fail_compilation/fail7815.d - if (inc != Include.notComputed) - return (inc == Include.yes); - if (errors) - return errorReturn(); - if (result) - inc = Include.yes; - else - inc = Include.no; - } + bool result = evalStaticCondition(sc, exp, exp, errors); + + // Prevent repeated condition evaluation. + // See: fail_compilation/fail7815.d + if (inc != Include.notComputed) + return (inc == Include.yes); + if (errors) + return errorReturn(); + if (result) + inc = Include.yes; + else + inc = Include.no; return (inc == Include.yes); } @@ -963,11 +712,6 @@ extern (C++) final class StaticIfCondition : Condition { return this; } - - override const(char)* toChars() const - { - return exp ? exp.toChars() : "static if".ptr; - } } @@ -1005,7 +749,5 @@ private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[ ob.writestring(") : "); if (condition.ident) ob.writestring(condition.ident.toString()); - else - ob.print(condition.level); ob.writeByte('\n'); } diff --git a/gcc/d/dmd/cond.h b/gcc/d/dmd/cond.h index fe497c2d..174a8fb 100644 --- a/gcc/d/dmd/cond.h +++ b/gcc/d/dmd/cond.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -39,8 +39,8 @@ public: virtual Condition *syntaxCopy() = 0; virtual int include(Scope *sc) = 0; - virtual DebugCondition *isDebugCondition() { return NULL; } - virtual VersionCondition *isVersionCondition() { return NULL; } + virtual DebugCondition *isDebugCondition() { return nullptr; } + virtual VersionCondition *isVersionCondition() { return nullptr; } void accept(Visitor *v) override { v->visit(this); } }; @@ -58,7 +58,6 @@ public: class DVCondition : public Condition { public: - unsigned level; Identifier *ident; Module *mod; @@ -72,7 +71,6 @@ public: static void addGlobalIdent(const char *ident); int include(Scope *sc) override; - DebugCondition *isDebugCondition() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -83,7 +81,6 @@ public: static void addPredefinedGlobalIdent(const char *ident); int include(Scope *sc) override; - VersionCondition *isVersionCondition() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/constfold.d b/gcc/d/dmd/constfold.d index 6ec31d5..fad9c9a 100644 --- a/gcc/d/dmd/constfold.d +++ b/gcc/d/dmd/constfold.d @@ -5,12 +5,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/float.html#fp_const_folding, Floating Point Constant Folding) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/constfold.d, _constfold.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/constfold.d, _constfold.d) * Documentation: https://dlang.org/phobos/dmd_constfold.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/constfold.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/constfold.d */ module dmd.constfold; @@ -36,7 +36,7 @@ import dmd.root.utf; import dmd.sideeffect; import dmd.target; import dmd.tokens; -import dmd.typesem : toDsymbol, equivalent, sarrayOf; +import dmd.typesem : toDsymbol, equivalent, sarrayOf, size; private enum LOG = false; @@ -69,15 +69,15 @@ UnionExp Neg(Type type, Expression e1) { UnionExp ue = void; Loc loc = e1.loc; - if (e1.type.isreal()) + if (e1.type.isReal()) { emplaceExp!(RealExp)(&ue, loc, -e1.toReal(), type); } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { emplaceExp!(RealExp)(&ue, loc, -e1.toImaginary(), type); } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { emplaceExp!(ComplexExp)(&ue, loc, -e1.toComplex(), type); } @@ -107,22 +107,22 @@ UnionExp Not(Type type, Expression e1) return ue; } -UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Add(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; static if (LOG) { printf("Add(e1 = %s, e2 = %s)\n", e1.toChars(), e2.toChars()); } - if (type.isreal()) + if (type.isReal()) { emplaceExp!(RealExp)(&ue, loc, e1.toReal() + e2.toReal(), type); } - else if (type.isimaginary()) + else if (type.isImaginary()) { emplaceExp!(RealExp)(&ue, loc, e1.toImaginary() + e2.toImaginary(), type); } - else if (type.iscomplex()) + 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 @@ -134,12 +134,12 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) real_t i2 = CTFloat.zero; auto v = complex_t(CTFloat.zero); int x; - if (e1.type.isreal()) + if (e1.type.isReal()) { r1 = e1.toReal(); x = 0; } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { i1 = e1.toImaginary(); x = 3; @@ -149,11 +149,11 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) c1 = e1.toComplex(); x = 6; } - if (e2.type.isreal()) + if (e2.type.isReal()) { r2 = e2.toReal(); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { i2 = e2.toImaginary(); x += 1; @@ -212,7 +212,7 @@ UnionExp Add(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Min(Loc loc, Type type, Expression e1, Expression e2) { // Compute e1-e2 as e1+(-e2) UnionExp neg = Neg(e2.type, e2); @@ -220,32 +220,32 @@ UnionExp Min(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Mul(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - if (type.isfloating()) + if (type.isFloating()) { auto c = complex_t(CTFloat.zero); real_t r = CTFloat.zero; - if (e1.type.isreal()) + if (e1.type.isReal()) { r = e1.toReal(); c = e2.toComplex(); c = complex_t(r * creall(c), r * cimagl(c)); } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { r = e1.toImaginary(); c = e2.toComplex(); c = complex_t(-r * cimagl(c), r * creall(c)); } - else if (e2.type.isreal()) + else if (e2.type.isReal()) { r = e2.toReal(); c = e1.toComplex(); c = complex_t(r * creall(c), r * cimagl(c)); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { r = e2.toImaginary(); c = e1.toComplex(); @@ -253,11 +253,11 @@ UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) } else c = e1.toComplex() * e2.toComplex(); - if (type.isreal()) + if (type.isReal()) emplaceExp!(RealExp)(&ue, loc, creall(c), type); - else if (type.isimaginary()) + else if (type.isImaginary()) emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); - else if (type.iscomplex()) + else if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, c, type); else assert(0); @@ -269,15 +269,15 @@ UnionExp Mul(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Div(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - if (type.isfloating()) + if (type.isFloating()) { auto c = complex_t(CTFloat.zero); - if (e2.type.isreal()) + if (e2.type.isReal()) { - if (e1.type.isreal()) + if (e1.type.isReal()) { emplaceExp!(RealExp)(&ue, loc, e1.toReal() / e2.toReal(), type); return ue; @@ -286,7 +286,7 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) c = e1.toComplex(); c = complex_t(creall(c) / r, cimagl(c) / r); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { const r = e2.toImaginary(); c = e1.toComplex(); @@ -297,11 +297,11 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) c = e1.toComplex() / e2.toComplex(); } - if (type.isreal()) + if (type.isReal()) emplaceExp!(RealExp)(&ue, loc, creall(c), type); - else if (type.isimaginary()) + else if (type.isImaginary()) emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); - else if (type.iscomplex()) + else if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, c, type); else assert(0); @@ -319,7 +319,7 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(ErrorExp)(&ue); return ue; } - if (n2 == -1 && !type.isunsigned()) + if (n2 == -1 && !type.isUnsigned()) { // Check for int.min / -1 if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) @@ -335,7 +335,7 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } } - if (e1.type.isunsigned() || e2.type.isunsigned()) + if (e1.type.isUnsigned() || e2.type.isUnsigned()) n = (cast(dinteger_t)n1) / (cast(dinteger_t)n2); else n = n1 / n2; @@ -344,29 +344,29 @@ UnionExp Div(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Mod(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; - if (type.isfloating()) + if (type.isFloating()) { auto c = complex_t(CTFloat.zero); - if (e2.type.isreal()) + if (e2.type.isReal()) { const r2 = e2.toReal(); c = complex_t(e1.toReal() % r2, e1.toImaginary() % r2); } - else if (e2.type.isimaginary()) + else if (e2.type.isImaginary()) { const i2 = e2.toImaginary(); c = complex_t(e1.toReal() % i2, e1.toImaginary() % i2); } else assert(0); - if (type.isreal()) + if (type.isReal()) emplaceExp!(RealExp)(&ue, loc, creall(c), type); - else if (type.isimaginary()) + else if (type.isImaginary()) emplaceExp!(RealExp)(&ue, loc, cimagl(c), type); - else if (type.iscomplex()) + else if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, c, type); else assert(0); @@ -384,7 +384,7 @@ UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(ErrorExp)(&ue); return ue; } - if (n2 == -1 && !type.isunsigned()) + if (n2 == -1 && !type.isUnsigned()) { // Check for int.min % -1 if (n1 == 0xFFFFFFFF80000000UL && type.toBasetype().ty != Tint64) @@ -400,7 +400,7 @@ UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } } - if (e1.type.isunsigned() || e2.type.isunsigned()) + if (e1.type.isUnsigned() || e2.type.isUnsigned()) n = (cast(dinteger_t)n1) % (cast(dinteger_t)n2); else n = n1 % n2; @@ -409,18 +409,18 @@ UnionExp Mod(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Pow(Loc loc, Type type, Expression e1, Expression e2) { //printf("Pow()\n"); UnionExp ue; // Handle integer power operations. - if (e2.type.isintegral()) + if (e2.type.isIntegral()) { dinteger_t n = e2.toInteger(); bool neg; - if (!e2.type.isunsigned() && cast(sinteger_t)n < 0) + if (!e2.type.isUnsigned() && cast(sinteger_t)n < 0) { - if (e1.type.isintegral()) + if (e1.type.isIntegral()) { cantExp(ue); return ue; @@ -432,12 +432,12 @@ UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) else neg = false; UnionExp ur, uv; - if (e1.type.iscomplex()) + if (e1.type.isComplex()) { emplaceExp!(ComplexExp)(&ur, loc, e1.toComplex(), e1.type); emplaceExp!(ComplexExp)(&uv, loc, complex_t(CTFloat.one), e1.type); } - else if (e1.type.isfloating()) + else if (e1.type.isFloating()) { emplaceExp!(RealExp)(&ur, loc, e1.toReal(), e1.type); emplaceExp!(RealExp)(&uv, loc, CTFloat.one, e1.type); @@ -467,14 +467,14 @@ UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) emplaceExp!(RealExp)(&one, loc, CTFloat.one, v.type); uv = Div(loc, v.type, one.exp(), v); } - if (type.iscomplex()) + if (type.isComplex()) emplaceExp!(ComplexExp)(&ue, loc, v.toComplex(), type); - else if (type.isintegral()) + else if (type.isIntegral()) emplaceExp!(IntegerExp)(&ue, loc, v.toInteger(), type); else emplaceExp!(RealExp)(&ue, loc, v.toReal(), type); } - else if (e2.type.isfloating()) + else if (e2.type.isFloating()) { // x ^^ y for x < 0 and y not an integer is not defined; so set result as NaN if (e1.toReal() < CTFloat.zero) @@ -489,14 +489,14 @@ UnionExp Pow(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Shl(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Shl(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() << e2.toInteger(), type); return ue; } -UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Shr(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; dinteger_t value = e1.toInteger(); @@ -542,7 +542,7 @@ UnionExp Shr(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Ushr(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; dinteger_t value = e1.toInteger(); @@ -582,21 +582,21 @@ UnionExp Ushr(const ref Loc loc, Type type, Expression e1, Expression e2) return ue; } -UnionExp And(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp And(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() & e2.toInteger(), type); return ue; } -UnionExp Or(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Or(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger() | e2.toInteger(), type); return ue; } -UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Xor(Loc loc, Type type, Expression e1, Expression e2) { //printf("Xor(linnum = %d, e1 = %s, e2 = %s)\n", loc.linnum, e1.toChars(), e2.toChars()); UnionExp ue = void; @@ -606,7 +606,7 @@ UnionExp Xor(const ref Loc loc, Type type, Expression e1, Expression e2) /* Also returns EXP.cantExpression if cannot be computed. */ -UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Equal(EXP op, Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; int cmp = 0; @@ -765,13 +765,13 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e cantExp(ue); return ue; } - else if (e1.type.isreal()) + else if (e1.type.isReal()) { r1 = e1.toReal(); r2 = e2.toReal(); goto L1; } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { r1 = e1.toImaginary(); r2 = e2.toImaginary(); @@ -785,11 +785,11 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e cmp = (r1 == r2); } } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { cmp = e1.toComplex() == e2.toComplex(); } - else if (e1.type.isintegral() || e1.type.toBasetype().ty == Tpointer) + else if (e1.type.isIntegral() || e1.type.toBasetype().ty == Tpointer) { cmp = (e1.toInteger() == e2.toInteger()); } @@ -804,8 +804,9 @@ UnionExp Equal(EXP op, const ref Loc loc, Type type, Expression e1, Expression e return ue; } -UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Identity(EXP op, Loc loc, Type type, Expression e1, Expression e2) { + //printf("Identity %s %s\n", e1.toChars(), e2.toChars()); UnionExp ue = void; int cmp; if (e1.op == EXP.null_) @@ -820,11 +821,21 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio { SymOffExp es1 = e1.isSymOffExp(); SymOffExp es2 = e2.isSymOffExp(); - cmp = (es1.var == es2.var && es1.offset == es2.offset); + cmp = es1.offset == es2.offset; + if (cmp) + { + cmp = es1.var == es2.var; + if (!cmp && (es1.var.isParameter() || es2.var.isParameter())) + { + // because of ref's, they may still be the same, we cannot tell + cantExp(ue); + return ue; + } + } } else { - if (e1.type.isfloating()) + if (e1.type.isFloating()) cmp = e1.isIdentical(e2); else { @@ -838,7 +849,7 @@ UnionExp Identity(EXP op, const ref Loc loc, Type type, Expression e1, Expressio return ue; } -UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Cmp(EXP op, Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; dinteger_t n; @@ -866,20 +877,20 @@ UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) cantExp(ue); return ue; } - else if (e1.type.isreal()) + else if (e1.type.isReal()) { r1 = e1.toReal(); r2 = e2.toReal(); goto L1; } - else if (e1.type.isimaginary()) + else if (e1.type.isImaginary()) { r1 = e1.toImaginary(); r2 = e2.toImaginary(); L1: n = realCmp(op, r1, r2); } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { assert(0); } @@ -889,7 +900,7 @@ UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) sinteger_t n2; n1 = e1.toInteger(); n2 = e2.toInteger(); - if (e1.type.isunsigned() || e2.type.isunsigned()) + if (e1.type.isUnsigned() || e2.type.isUnsigned()) n = intUnsignedCmp(op, n1, n2); else n = intSignedCmp(op, n1, n2); @@ -902,7 +913,7 @@ UnionExp Cmp(EXP op, const ref Loc loc, Type type, Expression e1, Expression e2) * to: type to cast to * type: type to paint the result */ -UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) +UnionExp Cast(Loc loc, Type type, Type to, Expression e1) { UnionExp ue = void; Type tb = to.toBasetype(); @@ -968,9 +979,9 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) emplaceExp!(IntegerExp)(&ue, loc, opt.get(), type); } - else if (type.isintegral()) + else if (type.isIntegral()) { - if (e1.type.isfloating()) + if (e1.type.isFloating()) { dinteger_t result; real_t r = e1.toReal(); @@ -1008,27 +1019,27 @@ UnionExp Cast(const ref Loc loc, Type type, Type to, Expression e1) } emplaceExp!(IntegerExp)(&ue, loc, result, type); } - else if (type.isunsigned()) + else if (type.isUnsigned()) emplaceExp!(IntegerExp)(&ue, loc, e1.toUInteger(), type); else emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); } - else if (tb.isreal()) + else if (tb.isReal()) { real_t value = e1.toReal(); emplaceExp!(RealExp)(&ue, loc, value, type); } - else if (tb.isimaginary()) + else if (tb.isImaginary()) { real_t value = e1.toImaginary(); emplaceExp!(RealExp)(&ue, loc, value, type); } - else if (tb.iscomplex()) + else if (tb.isComplex()) { complex_t value = e1.toComplex(); emplaceExp!(ComplexExp)(&ue, loc, value, type); } - else if (tb.isscalar()) + else if (tb.isScalar()) { emplaceExp!(IntegerExp)(&ue, loc, e1.toInteger(), type); } @@ -1385,7 +1396,7 @@ private Expressions* copyElements(Expression e1, Expression e2 = null) /* Also return EXP.cantExpression if this fails */ -UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp Cat(Loc loc, Type type, Expression e1, Expression e2) { UnionExp ue = void; Expression e = CTFEExp.cantexp; @@ -1494,7 +1505,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) assert(ue.exp().type); return ue; } - else if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral()) + else if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isIntegral()) { // [chars] ~ string --> [chars] StringExp es = e2.isStringExp(); @@ -1511,7 +1522,7 @@ UnionExp Cat(const ref Loc loc, Type type, Expression e1, Expression e2) assert(ue.exp().type); return ue; } - else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral()) + else if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isIntegral()) { // string ~ [chars] --> [chars] StringExp es = e1.isStringExp(); diff --git a/gcc/d/dmd/cparse.d b/gcc/d/dmd/cparse.d index aeedb49..1a2a1e9 100644 --- a/gcc/d/dmd/cparse.d +++ b/gcc/d/dmd/cparse.d @@ -3,12 +3,12 @@ * * Specification: C11 * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d, _cparse.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cparse.d, _cparse.d) * Documentation: https://dlang.org/phobos/dmd_cparse.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cparse.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cparse.d */ module dmd.cparse; @@ -27,6 +27,7 @@ import dmd.root.array; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.tokens; +import dmd.typesem : size; /*********************************************************** */ @@ -35,7 +36,7 @@ final class CParser(AST) : Parser!AST AST.Dsymbols* symbols; // symbols declared in current scope bool addFuncName; /// add declaration of __func__ to function symbol table - bool importBuiltins; /// seen use of C compiler builtins, so import __builtins; + bool importBuiltins; /// seen use of C compiler builtins, so import __importc_builtins; private { @@ -44,6 +45,9 @@ final class CParser(AST) : Parser!AST // #pragma pack stack Array!Identifier* records; // identifers (or null) Array!structalign_t* packs; // parallel alignment values + + STC defaultStorageClasses; + Array!STC* defaultStorageClassesStack; } /* C cannot be parsed without determining if an identifier is a type or a variable. @@ -89,6 +93,9 @@ final class CParser(AST) : Parser!AST this.wchar_tsize = target.wchar_tsize; // C `char` is always unsigned in ImportC + + // We know that we are parsing out C, due the parent not knowing this, we have to setup tables here. + charLookup = compileEnv.cCharLookupTable; } /******************************************** @@ -125,7 +132,7 @@ final class CParser(AST) : Parser!AST /* Seen references to C builtin functions. * Import their definitions */ - auto s = new AST.Import(Loc.initial, null, Id.builtins, null, false); + auto s = new AST.Import(Loc.initial, null, Id.importc_builtins, null, false); wrap.push(s); } @@ -1116,75 +1123,74 @@ final class CParser(AST) : Parser!AST */ private AST.Expression cparseCastExp() { - if (token.value == TOK.leftParenthesis) + if (token.value != TOK.leftParenthesis) + return cparseUnaryExp(); + + //printf("cparseCastExp()\n"); + auto tk = peek(&token); + bool iscast; + bool isexp; + if (tk.value == TOK.identifier) { - //printf("cparseCastExp()\n"); - auto tk = peek(&token); - bool iscast; - bool isexp; - if (tk.value == TOK.identifier) - { - iscast = isTypedef(tk.ident); - isexp = !iscast; - } - if (isexp) - { - // ( identifier ) is an expression - return cparseUnaryExp(); - } + iscast = isTypedef(tk.ident); + isexp = !iscast; + } + if (isexp) + { + // ( identifier ) is an expression + return cparseUnaryExp(); + } - // If ( type-name ) - auto pt = &token; + // If ( type-name ) + auto pt = &token; - if (isCastExpression(pt)) - { - // Expression may be either a cast or a compound literal, which - // requires checking whether the next token is leftCurly - const loc = token.loc; - nextToken(); - auto t = cparseTypeName(); - check(TOK.rightParenthesis); - pt = &token; + if (!isCastExpression(pt)) + return cparseUnaryExp(); - if (token.value == TOK.leftCurly) - { - // C11 6.5.2.5 ( type-name ) { initializer-list } - auto ci = cparseInitializer(); - auto ce = new AST.CompoundLiteralExp(loc, t, ci); - return cparsePostfixOperators(ce); - } + // Expression may be either a cast or a compound literal, which + // requires checking whether the next token is leftCurly + const loc = token.loc; + nextToken(); + auto t = cparseTypeName(); + check(TOK.rightParenthesis); + pt = &token; - if (iscast) - { - // ( type-name ) cast-expression - auto ce = cparseCastExp(); - return new AST.CastExp(loc, ce, t); - } + if (token.value == TOK.leftCurly) + { + // C11 6.5.2.5 ( type-name ) { initializer-list } + auto ci = cparseInitializer(); + auto ce = new AST.CompoundLiteralExp(loc, t, ci); + return cparsePostfixOperators(ce); + } - if (t.isTypeIdentifier() && - isexp && - token.value == TOK.leftParenthesis && - !isCastExpression(pt)) - { - /* (t)(...)... might be a cast expression or a function call, - * with different grammars: a cast would be cparseCastExp(), - * a function call would be cparsePostfixExp(CallExp(cparseArguments())). - * We can't know until t is known. So, parse it as a function call - * and let semantic() rewrite the AST as a CastExp if it turns out - * to be a type. - */ - auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident); - ie.parens = true; // let semantic know it might be a CastExp - AST.Expression e = new AST.CallExp(loc, ie, cparseArguments()); - return cparsePostfixOperators(e); - } + if (iscast) + { + // ( type-name ) cast-expression + auto ce = cparseCastExp(); + return new AST.CastExp(loc, ce, t); + } - // ( type-name ) cast-expression - auto ce = cparseCastExp(); - return new AST.CastExp(loc, ce, t); - } + if (t.isTypeIdentifier() && + isexp && + token.value == TOK.leftParenthesis && + !isCastExpression(pt)) + { + /* (t)(...)... might be a cast expression or a function call, + * with different grammars: a cast would be cparseCastExp(), + * a function call would be cparsePostfixExp(CallExp(cparseArguments())). + * We can't know until t is known. So, parse it as a function call + * and let semantic() rewrite the AST as a CastExp if it turns out + * to be a type. + */ + auto ie = new AST.IdentifierExp(loc, t.isTypeIdentifier().ident); + ie.parens = true; // let semantic know it might be a CastExp + AST.Expression e = new AST.CallExp(loc, ie, cparseArguments()); + return cparsePostfixOperators(e); } - return cparseUnaryExp(); + + // ( type-name ) cast-expression + auto ce = cparseCastExp(); + return new AST.CastExp(loc, ce, t); } /************** @@ -1439,8 +1445,10 @@ final class CParser(AST) : Parser!AST auto e = cparseOrExp(); while (token.value == TOK.andAnd) { + e = new AST.CastExp(loc, e, AST.Type.tbool); nextToken(); auto e2 = cparseOrExp(); + e2 = new AST.CastExp(loc, e2, AST.Type.tbool); e = new AST.LogicalExp(loc, EXP.andAnd, e, e2); } return e; @@ -1459,8 +1467,10 @@ final class CParser(AST) : Parser!AST auto e = cparseAndAndExp(); while (token.value == TOK.orOr) { + e = new AST.CastExp(loc, e, AST.Type.tbool); nextToken(); auto e2 = cparseAndAndExp(); + e2 = new AST.CastExp(loc, e2, AST.Type.tbool); e = new AST.LogicalExp(loc, EXP.orOr, e, e2); } return e; @@ -1680,7 +1690,7 @@ final class CParser(AST) : Parser!AST private AST.Expression cparseStatementExpression() { AST.ParameterList parameterList; - StorageClass stc = 0; + STC stc = STC.none; const loc = token.loc; auto symbolsSave = symbols; symbols = new AST.Dsymbols(); @@ -1704,7 +1714,7 @@ final class CParser(AST) : Parser!AST } auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); - auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, 0); + auto fd = new AST.FuncLiteralDeclaration(loc, token.loc, tf, TOK.delegate_, null, null, STC.none); fd.fbody = fbody; auto fe = new AST.FuncExp(loc, fd); @@ -1869,22 +1879,30 @@ final class CParser(AST) : Parser!AST * init-declarator: * declarator simple-asm-expr (opt) gnu-attributes (opt) * declarator simple-asm-expr (opt) gnu-attributes (opt) = initializer + * + * Clang also allows simple-asm-expr after gnu-attributes. */ + while (1) + { + if (token.value == TOK.asm_) + { + asmName = cparseGnuAsmLabel(); + /* This is a data definition, there cannot now be a + * function definition. + */ + first = false; + } + else if (token.value == TOK.__attribute__) + cparseGnuAttributes(specifier); + else + break; + } + switch (token.value) { case TOK.assign: case TOK.comma: case TOK.semicolon: - case TOK.asm_: - case TOK.__attribute__: - if (token.value == TOK.asm_) - asmName = cparseGnuAsmLabel(); - if (token.value == TOK.__attribute__) - { - cparseGnuAttributes(specifier); - if (token.value == TOK.leftCurly) - break; // function definition - } /* This is a data definition, there cannot now be a * function definition. */ @@ -1919,6 +1937,14 @@ final class CParser(AST) : Parser!AST auto s = cparseFunctionDefinition(id, dt.isTypeFunction(), specifier); typedefTab.setDim(typedefTabLengthSave); symbols = symbolsSave; + if (specifier.mod & MOD.x__stdcall) + { + // If this function is __stdcall, wrap it in a LinkDeclaration so that + // it's extern(Windows) when imported in D. + auto decls = new AST.Dsymbols(1); + (*decls)[0] = s; + s = new AST.LinkDeclaration(s.loc, LINK.windows, decls); + } symbols.push(s); return; } @@ -1984,7 +2010,7 @@ final class CParser(AST) : Parser!AST //printf("AliasDeclaration %s %s\n", id.toChars(), dt.toChars()); auto ad = new AST.AliasDeclaration(token.loc, id, dt); if (id == idt) - ad.adFlags |= ad.hidden; // do not print when generating .di files + ad.hidden = true; // do not print when generating .di files s = ad; } @@ -2024,7 +2050,7 @@ final class CParser(AST) : Parser!AST error("no initializer for function declaration"); if (specifier.scw & SCW.x_Thread_local) error("functions cannot be `_Thread_local`"); // C11 6.7.1-4 - StorageClass stc = specifiersToSTC(level, specifier); + STC stc = specifiersToSTC(level, specifier); stc &= ~STC.gshared; // no gshared functions auto fd = new AST.FuncDeclaration(token.loc, Loc.initial, id, stc, dt, specifier.noreturn); specifiersToFuncDeclaration(fd, specifier); @@ -2066,18 +2092,18 @@ final class CParser(AST) : Parser!AST { auto str = asmName.peekString(); p.mangleOverride = str; -// p.adFlags |= AST.VarDeclaration.nounderscore; - p.adFlags |= 4; // cannot get above line to compile on Ubuntu + p.noUnderscore = true; } } s = applySpecifier(s, specifier); - if (level == LVL.local) + if (level == LVL.local || (specifier.mod & MOD.x__stdcall)) { - // Wrap the declaration in `extern (C) { declaration }` + // Wrap the declaration in `extern (C/Windows) { declaration }` // Necessary for function pointers, but harmless to apply to all. auto decls = new AST.Dsymbols(1); (*decls)[0] = s; - s = new AST.LinkDeclaration(s.loc, linkage, decls); + const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; + s = new AST.LinkDeclaration(s.loc, lkg, decls); } symbols.push(s); } @@ -2211,7 +2237,7 @@ final class CParser(AST) : Parser!AST auto body = cparseStatement(ParseStatementFlags.curly); // don't start a new scope; continue with parameter scope typedefTab.pop(); // end of function scope - StorageClass stc = specifiersToSTC(LVL.global, specifier); + STC stc = specifiersToSTC(LVL.global, specifier); stc &= ~STC.gshared; // no gshared functions auto fd = new AST.FuncDeclaration(locFunc, prevloc, id, stc, ft, specifier.noreturn); specifiersToFuncDeclaration(fd, specifier); @@ -2967,7 +2993,7 @@ final class CParser(AST) : Parser!AST if (isStatic || mod) error("static or type qualifier used outside of function prototype"); } - if (ts.isTypeSArray() || ts.isTypeDArray()) + if (ts.isStaticOrDynamicArray()) { /* C11 6.7.6.2-1: type qualifiers and 'static' shall only appear * in the outermost array type derivation. @@ -2996,9 +3022,10 @@ final class CParser(AST) : Parser!AST auto parameterList = cparseParameterList(); const lkg = specifier.mod & MOD.x__stdcall ? LINK.windows : linkage; - StorageClass stc = specifier._nothrow ? STC.nothrow_ : 0; + STC stc = specifier._nothrow ? STC.nothrow_ : STC.none; if (specifier._pure) stc |= STC.pure_; + stc |= defaultStorageClasses; AST.Type tf = new AST.TypeFunction(parameterList, t, lkg, stc); //tf = tf.addSTC(storageClass); // TODO insertTx(ts, tf, t); // ts -> ... -> tf -> t @@ -3151,7 +3178,7 @@ final class CParser(AST) : Parser!AST { auto parameters = new AST.Parameters(); AST.VarArg varargs = AST.VarArg.none; - StorageClass varargsStc; + STC varargsStc; check(TOK.leftParenthesis); if (token.value == TOK.void_ && peekNext() == TOK.rightParenthesis) // func(void) @@ -3514,7 +3541,7 @@ final class CParser(AST) : Parser!AST break; } nextToken(); - auto s = new AST.CompoundAsmStatement(loc, statements, 0); + auto s = new AST.CompoundAsmStatement(loc, statements, STC.none); return s; } @@ -3614,6 +3641,12 @@ final class CParser(AST) : Parser!AST * type on the target machine. It's the opposite of __attribute__((packed)) */ } + else if (token.ident == Id.packed) + { + specifier.packalign.set(1); + specifier.packalign.setPack(true); + nextToken(); + } else if (token.ident == Id.always_inline) // https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html { specifier.scw |= SCW.xinline; @@ -3893,7 +3926,7 @@ final class CParser(AST) : Parser!AST cparseGnuAttributes(specifierx); } - auto em = new AST.EnumMember(mloc, ident, value, null, 0, null, null); + auto em = new AST.EnumMember(mloc, ident, value, null, STC.none, null, null); members.push(em); if (token.value == TOK.comma) @@ -3962,7 +3995,7 @@ final class CParser(AST) : Parser!AST members = new AST.Dsymbols(); // so `members` will be non-null even with 0 members while (token.value != TOK.rightCurly) { - cparseStructDeclaration(members); + cparseStructDeclaration(members, packalign); if (token.value == TOK.endOfFile) break; @@ -3976,6 +4009,24 @@ final class CParser(AST) : Parser!AST * struct-declarator (opt) */ } + + /* GNU Extensions + * Parse the postfix gnu-attributes (opt) + */ + Specifier specifier; + if (token.value == TOK.__attribute__) + cparseGnuAttributes(specifier); + if (!specifier.packalign.isUnknown) + { + packalign.set(specifier.packalign.get()); + packalign.setPack(specifier.packalign.isPack()); + foreach (ref d; (*members)[]) + { + auto decls = new AST.Dsymbols(1); + (*decls)[0] = d; + d = new AST.AlignDeclaration(d.loc, specifier.packalign, decls); + } + } } else if (!tag) error("missing tag `identifier` after `%s`", Token.toChars(structOrUnion)); @@ -4007,8 +4058,9 @@ final class CParser(AST) : Parser!AST * declarator (opt) : constant-expression * Params: * members = where to put the fields (members) + * packalign = alignment to use for struct members */ - void cparseStructDeclaration(AST.Dsymbols* members) + void cparseStructDeclaration(AST.Dsymbols* members, structalign_t packalign) { //printf("cparseStructDeclaration()\n"); if (token.value == TOK._Static_assert) @@ -4019,7 +4071,7 @@ final class CParser(AST) : Parser!AST } Specifier specifier; - specifier.packalign = this.packalign; + specifier.packalign = packalign.isUnknown ? this.packalign : packalign; auto tspec = cparseSpecifierQualifierList(LVL.member, specifier); if (!tspec) { @@ -5114,9 +5166,9 @@ final class CParser(AST) : Parser!AST * Returns: * corresponding D storage class */ - StorageClass specifiersToSTC(LVL level, const ref Specifier specifier) + STC specifiersToSTC(LVL level, const ref Specifier specifier) { - StorageClass stc; + STC stc; if (specifier.scw & SCW.x_Thread_local) { if (level == LVL.global) @@ -5478,6 +5530,12 @@ final class CParser(AST) : Parser!AST if (pt && *pt) t = *pt; } + if (t.mcache && t.mcache.typedefIdent) + { + t = t.copy(); + t.mcache = null; + } + t.getMcache().typedefIdent = id; auto tab = cast(void*[void*])(typedefTab[$ - 1]); tab[cast(void*)id] = cast(void*)t; typedefTab[$ - 1] = cast(void*)tab; @@ -5571,7 +5629,7 @@ final class CParser(AST) : Parser!AST * Params: * startloc = location to use for error messages */ - private void uupragmaDirective(const ref Loc startloc) + private void uupragmaDirective(Loc startloc) { const loc = startloc; nextToken(); // move past __pragma @@ -5619,12 +5677,14 @@ final class CParser(AST) : Parser!AST * the preprocessed output. Ignore them. * Upon return, p is at start of next line. */ - private void pragmaDirective(const ref Loc loc) + private void pragmaDirective(Loc loc) { Token n; scan(&n); if (n.value == TOK.identifier && n.ident == Id.pack) return pragmaPack(loc, true); + if (n.value == TOK.identifier && n.ident == Id.attribute) + return pragmaAttribute(loc); if (n.value != TOK.endOfLine) skipToNextLine(); } @@ -5638,7 +5698,7 @@ final class CParser(AST) : Parser!AST * startloc = location to use for error messages * useScan = use scan() to retrieve next token, instead of nextToken() */ - private void pragmaPack(const ref Loc startloc, bool useScan) + private void pragmaPack(Loc startloc, bool useScan) { const loc = startloc; @@ -5768,7 +5828,7 @@ final class CParser(AST) : Parser!AST if (len == 1) // stack is now empty packalign.setDefault(); else - packalign = (*this.packs)[len - 1]; + packalign = (*this.packs)[len - 2]; return closingParen(); } while (n.value == TOK.comma) // #pragma pack ( pop , @@ -5832,6 +5892,125 @@ final class CParser(AST) : Parser!AST skipToNextLine(); } + /********* + * # pragma attribute(...) + * Sets default storage classes + * Params: + * startloc = location to use for error messages + */ + private void pragmaAttribute(Loc startloc) + { + const loc = startloc; + + if (!defaultStorageClassesStack) + { + defaultStorageClassesStack = new Array!STC; + } + + Token n; + Lexer.scan(&n); + if (n.value != TOK.leftParenthesis) + { + error(loc, "left parenthesis expected to follow `#pragma attribute`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + void closingParen() + { + if (n.value != TOK.rightParenthesis) + { + error(loc, "right parenthesis expected to close `#pragma attribute(`"); + } + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + + Lexer.scan(&n); + + /* # pragma attribute (push, ...) + */ + if (n.value == TOK.identifier && n.ident == Id.push) + { + Lexer.scan(&n); + if (n.value != TOK.comma) + { + error(loc, "comma expected to follow `#pragma attribute(push`"); + if (n.value != TOK.endOfLine) + skipToNextLine(); + return; + } + + while (1) + { + Lexer.scan(&n); + if (n.value == TOK.endOfLine) + { + error(loc, "right parenthesis expected to close `#pragma attribute(push, `"); + break; + } + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value == TOK.identifier) + { + if (n.ident == Id._nothrow) + defaultStorageClasses |= STC.nothrow_; + else if (n.ident == Id.nogc) + defaultStorageClasses |= STC.nogc; + else if (n.ident == Id._pure) + defaultStorageClasses |= STC.pure_; + // Ignore unknown identifiers + } + else + { + error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars()); + break; + } + + Lexer.scan(&n); + + if (n.value == TOK.rightParenthesis) + break; + + if (n.value != TOK.comma) + { + error(loc, "unrecognized `#pragma attribute(push, %s)`", n.toChars()); + break; + } + } + + this.defaultStorageClassesStack.push(defaultStorageClasses); + + return closingParen(); + } + + /* # pragma attribute(pop) + */ + if (n.value == TOK.identifier && n.ident == Id.pop) + { + scan(&n); + size_t len = this.defaultStorageClassesStack.length; + + if (len) + { + this.defaultStorageClassesStack.setDim(len - 1); + if (len == 1) // stack is now empty + defaultStorageClasses = STC.init; + else + defaultStorageClasses = (*this.defaultStorageClassesStack)[len - 2]; + } + + return closingParen(); + } + + error(loc, "unrecognized `#pragma attribute(%s)`", n.toChars()); + if (n.value != TOK.endOfLine) + skipToNextLine(); + } + //} /******************************************************************************/ @@ -5860,13 +6039,15 @@ final class CParser(AST) : Parser!AST const(char)* endp = &slice[length - 7]; + AST.Dsymbols newSymbols; + size_t[void*] defineTab; // hash table of #define's turned into Symbol's - // indexed by Identifier, returns index into symbols[] + // indexed by Identifier, returns index into newSymbols[] // The memory for this is leaked - void addVar(AST.Dsymbol s) + void addSym(AST.Dsymbol s) { - //printf("addVar() %s\n", s.toChars()); + //printf("addSym() %s\n", s.toChars()); if (auto v = s.isVarDeclaration()) v.isCmacro(true); // mark it as coming from a C #define /* If it's already defined, replace the earlier @@ -5874,13 +6055,22 @@ final class CParser(AST) : Parser!AST */ if (size_t* pd = cast(void*)s.ident in defineTab) { - //printf("replacing %s\n", v.toChars()); - (*symbols)[*pd] = s; + //printf("replacing %s\n", s.toChars()); + newSymbols[*pd] = s; return; } - assert(symbols, "symbols is null"); - defineTab[cast(void*)s.ident] = symbols.length; - symbols.push(s); + defineTab[cast(void*)s.ident] = newSymbols.length; + newSymbols.push(s); + } + + void removeSym(Identifier ident) + { + //printf("removeSym() %s\n", ident.toChars()); + if (size_t* pd = cast(void*)ident in defineTab) + { + //printf("removing %s\n", ident.toChars()); + newSymbols[*pd] = null; + } } while (p < endp) @@ -5899,6 +6089,30 @@ final class CParser(AST) : Parser!AST AST.Type t; + bool hasMinus; + if (token.value == TOK.min) + { + nextToken(); + // Only allow integer and float literals after minus. + switch (token.value) + { + case TOK.int32Literal: + case TOK.uns32Literal: + case TOK.int64Literal: + case TOK.uns64Literal: + case TOK.float32Literal: + case TOK.float64Literal: + case TOK.float80Literal: + case TOK.imaginary32Literal: + case TOK.imaginary64Literal: + case TOK.imaginary80Literal: + hasMinus = true; + break; + default: + continue; + } + } + Lswitch: switch (token.value) { @@ -5923,8 +6137,10 @@ final class CParser(AST) : Parser!AST * enum id = intvalue; */ AST.Expression e = new AST.IntegerExp(scanloc, intvalue, t); + if (hasMinus) + e = new AST.NegExp(scanloc, e); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); - addVar(v); + addSym(v); ++p; continue; } @@ -5946,8 +6162,10 @@ final class CParser(AST) : Parser!AST * enum id = floatvalue; */ AST.Expression e = new AST.RealExp(scanloc, floatvalue, t); + if (hasMinus) + e = new AST.NegExp(scanloc, e); auto v = new AST.VarDeclaration(scanloc, t, id, new AST.ExpInitializer(scanloc, e), STC.manifest); - addVar(v); + addSym(v); ++p; continue; } @@ -5965,7 +6183,7 @@ final class CParser(AST) : Parser!AST */ AST.Expression e = new AST.StringExp(scanloc, str[0 .. len], len, 1, postfix); auto v = new AST.VarDeclaration(scanloc, null, id, new AST.ExpInitializer(scanloc, e), STC.manifest); - addVar(v); + addSym(v); ++p; continue; } @@ -5991,8 +6209,8 @@ final class CParser(AST) : Parser!AST if (token.value != TOK.endOfFile) break; auto ret = new AST.ReturnStatement(exp.loc, exp); - auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, 0); - StorageClass stc = STC.auto_; + auto parameterList = AST.ParameterList(new AST.Parameters(), VarArg.none, STC.none); + STC stc = STC.auto_; auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); fd.fbody = ret; @@ -6001,7 +6219,7 @@ final class CParser(AST) : Parser!AST AST.TemplateParameters* tpl = new AST.TemplateParameters(); AST.Expression constraint = null; auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, constraint, decldefs, false); - addVar(tempdecl); + addSym(tempdecl); ++p; continue; } @@ -6037,7 +6255,7 @@ final class CParser(AST) : Parser!AST if (token.value != TOK.identifier) break Lswitch; - auto param = new AST.Parameter(token.loc, 0, null, token.ident, null, null); + auto param = new AST.Parameter(token.loc, STC.none, null, token.ident, null, null); parameters.push(param); nextToken(); if (token.value == TOK.comma) @@ -6052,7 +6270,7 @@ final class CParser(AST) : Parser!AST //auto pstart = p; nextToken(); - auto parameterList = AST.ParameterList(parameters, varargs, 0); + auto parameterList = AST.ParameterList(parameters, varargs, STC.none); /* Create a type for each parameter. Add it to the template parameter list, * and the parameter list. */ @@ -6083,7 +6301,7 @@ final class CParser(AST) : Parser!AST // Generate function auto ret = new AST.ReturnStatement(exp.loc, exp); - StorageClass stc = STC.auto_; + STC stc = STC.auto_; auto tf = new AST.TypeFunction(parameterList, null, LINK.d, stc); auto fd = new AST.FuncDeclaration(exp.loc, exp.loc, id, stc, tf, 0); fd.fbody = ret; @@ -6092,7 +6310,7 @@ final class CParser(AST) : Parser!AST AST.Dsymbols* decldefs = new AST.Dsymbols(); decldefs.push(fd); auto tempdecl = new AST.TemplateDeclaration(exp.loc, id, tpl, null, decldefs, false); - addVar(tempdecl); + addSym(tempdecl); ++p; continue; @@ -6103,11 +6321,28 @@ final class CParser(AST) : Parser!AST } } } + else if (p[0 .. 6] == "#undef") + { + p += 6; + nextToken(); + //printf("undef %s\n", token.toChars()); + if (token.value == TOK.identifier) + removeSym(token.ident); + } // scan to end of line while (*p) ++p; ++p; // advance to start of next line - scanloc.linnum = scanloc.linnum + 1; + } + + if (newSymbols.length) + { + assert(symbols, "symbols is null"); + symbols.reserve(newSymbols.length); + + foreach (sym; newSymbols) + if (sym) // undefined entries are null + symbols.push(sym); } scanloc = scanlocSave; diff --git a/gcc/d/dmd/ctfe.h b/gcc/d/dmd/ctfe.h index 72d895c..e022d7c 100644 --- a/gcc/d/dmd/ctfe.h +++ b/gcc/d/dmd/ctfe.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -48,7 +48,6 @@ class ThrownExceptionExp final : public Expression { public: ClassReferenceExp *thrown; // the thing being tossed - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -58,6 +57,4 @@ public: class CTFEExp final : public Expression { -public: - const char *toChars() const override; }; diff --git a/gcc/d/dmd/ctfeexpr.d b/gcc/d/dmd/ctfeexpr.d index d2fcf5f..2f577ce 100644 --- a/gcc/d/dmd/ctfeexpr.d +++ b/gcc/d/dmd/ctfeexpr.d @@ -1,12 +1,12 @@ /** * CTFE for expressions involving pointers, slices, array concatenation etc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d, _ctfeexpr.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ctfeexpr.d, _ctfeexpr.d) * Documentation: https://dlang.org/phobos/dmd_ctfeexpr.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctfeexpr.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ctfeexpr.d */ module dmd.ctfeexpr; @@ -17,6 +17,7 @@ import dmd.arraytypes; import dmd.astenums; import dmd.constfold; import dmd.compiler; +import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; @@ -464,8 +465,7 @@ Expression resolveSlice(Expression e, UnionExp* pue = null) *pue = Slice(e.type, se.e1, se.lwr, se.upr); return pue.exp(); } - else - return Slice(e.type, se.e1, se.lwr, se.upr).copy(); + return Slice(e.type, se.e1, se.lwr, se.upr).copy(); } /* Determine the array length, without interpreting it. @@ -522,7 +522,7 @@ uinteger_t resolveArrayLength(Expression e) * Returns: * Constructed ArrayLiteralExp */ -ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc loc, Type type, Expression elem, size_t dim) +ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, Loc loc, Type type, Expression elem, size_t dim) { if (type.ty == Tsarray && type.nextOf().ty == Tsarray && elem.type.ty != Tsarray) { @@ -553,7 +553,7 @@ ArrayLiteralExp createBlockDuplicatedArrayLiteral(UnionExp* pue, const ref Loc l * Helper for NewExp * Create a string literal consisting of 'value' duplicated 'dim' times. */ -StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, const ref Loc loc, Type type, dchar value, size_t dim, ubyte sz) +StringExp createBlockDuplicatedStringLiteral(UnionExp* pue, Loc loc, Type type, dchar value, size_t dim, ubyte sz) { auto s = cast(char*)mem.xcalloc(dim, sz); foreach (elemi; 0 .. dim) @@ -660,7 +660,7 @@ bool isSafePointerCast(Type srcPointee, Type destPointee) srcPointee = srcPointee.baseElemOf(); destPointee = destPointee.baseElemOf(); } - return srcPointee.isintegral() && destPointee.isintegral() && srcPointee.size() == destPointee.size(); + return srcPointee.isIntegral() && destPointee.isIntegral() && srcPointee.size() == destPointee.size(); } Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) @@ -688,7 +688,7 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) if (auto ie = e.isIndexExp()) { // Note that each AA element is part of its own memory block - if ((ie.e1.type.ty == Tarray || ie.e1.type.ty == Tsarray || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64) + if ((ie.e1.type.isStaticOrDynamicArray() || ie.e1.op == EXP.string_ || ie.e1.op == EXP.arrayLiteral) && ie.e2.op == EXP.int64) { *ofs = ie.e2.toInteger(); return ie.e1; @@ -697,7 +697,7 @@ Expression getAggregateFromPointer(Expression e, dinteger_t* ofs) if (auto se = e.isSliceExp()) { if (se && e.type.toBasetype().ty == Tsarray && - (se.e1.type.ty == Tarray || se.e1.type.ty == Tsarray || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64) + (se.e1.type.isStaticOrDynamicArray() || se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral) && se.lwr.op == EXP.int64) { *ofs = se.lwr.toInteger(); return se.e1; @@ -741,7 +741,7 @@ bool pointToSameMemoryBlock(Expression agg1, Expression agg2) } // return e1 - e2 as an integer, or error if not possible -Expression pointerDifference(UnionExp* pue, const ref Loc loc, Type type, Expression e1, Expression e2) +Expression pointerDifference(UnionExp* pue, Loc loc, Type type, Expression e1, Expression e2) { dinteger_t ofs1, ofs2; Expression agg1 = getAggregateFromPointer(e1, &ofs1); @@ -774,7 +774,7 @@ Expression pointerDifference(UnionExp* pue, const ref Loc loc, Type type, Expres // Return eptr op e2, where eptr is a pointer, e2 is an integer, // and op is EXP.add or EXP.min -Expression pointerArithmetic(UnionExp* pue, const ref Loc loc, EXP op, Type type, Expression eptr, Expression e2) +Expression pointerArithmetic(UnionExp* pue, Loc loc, EXP op, Type type, Expression eptr, Expression e2) { if (eptr.type.nextOf().ty == Tvoid) { @@ -950,7 +950,7 @@ int comparePointers(EXP op, Expression agg1, dinteger_t ofs1, Expression agg2, d // floating point -> integer or integer -> floating point bool isFloatIntPaint(Type to, Type from) { - return from.size() == to.size() && (from.isintegral() && to.isfloating() || from.isfloating() && to.isintegral()); + return from.size() == to.size() && (from.isIntegral() && to.isFloating() || from.isFloating() && to.isIntegral()); } // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. @@ -1052,7 +1052,7 @@ bool realCmp(EXP op, real_t r1, real_t r2) @safe nothrow * Returns: * -1,0,1 */ -private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinteger_t len) +private int ctfeCmpArrays(Loc loc, Expression e1, Expression e2, uinteger_t len) { // Resolve slices, if necessary uinteger_t lo1 = 0; @@ -1087,7 +1087,7 @@ private int ctfeCmpArrays(const ref Loc loc, Expression e1, Expression e2, uinte // Comparing two array literals. This case is potentially recursive. // If they aren't strings, we just need an equality check rather than // a full cmp. - const bool needCmp = ae1.type.nextOf().isintegral(); + const bool needCmp = ae1.type.nextOf().isIntegral(); foreach (size_t i; 0 .. cast(size_t)len) { Expression ee1 = (*ae1.elements)[cast(size_t)(lo1 + i)]; @@ -1138,7 +1138,7 @@ private bool isArray(const Expression e) @safe nothrow * For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. */ -private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool identity = false) +private int ctfeRawCmp(Loc loc, Expression e1, Expression e2, bool identity = false) { if (e1.op == EXP.classReference || e2.op == EXP.classReference) { @@ -1212,26 +1212,23 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide } return cast(int)(len1 - len2); } - if (e1.type.isintegral()) + if (e1.type.isIntegral()) { return e1.toInteger() != e2.toInteger(); } - if (identity && e1.type.isfloating()) + if (identity && e1.type.isFloating()) return !e1.isIdentical(e2); - if (e1.type.isreal() || e1.type.isimaginary()) + if (e1.type.isReal() || e1.type.isImaginary()) { - real_t r1 = e1.type.isreal() ? e1.toReal() : e1.toImaginary(); - real_t r2 = e1.type.isreal() ? e2.toReal() : e2.toImaginary(); + real_t r1 = e1.type.isReal() ? e1.toReal() : e1.toImaginary(); + real_t r2 = e1.type.isReal() ? e2.toReal() : e2.toImaginary(); if (CTFloat.isNaN(r1) || CTFloat.isNaN(r2)) // if unordered { return 1; // they are not equal } - else - { - return (r1 != r2); - } + return (r1 != r2); } - else if (e1.type.iscomplex()) + else if (e1.type.isComplex()) { return e1.toComplex() != e2.toComplex(); } @@ -1242,33 +1239,30 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide // For structs, we only need to return 0 or 1 (< and > aren't legal). if (es1.sd != es2.sd) return 1; - else if ((!es1.elements || !es1.elements.length) && (!es2.elements || !es2.elements.length)) + if ((!es1.elements || !es1.elements.length) && (!es2.elements || !es2.elements.length)) return 0; // both arrays are empty - else if (!es1.elements || !es2.elements) + if (!es1.elements || !es2.elements) return 1; - else if (es1.elements.length != es2.elements.length) + if (es1.elements.length != es2.elements.length) return 1; - else + foreach (size_t i; 0 .. es1.elements.length) { - foreach (size_t i; 0 .. es1.elements.length) - { - Expression ee1 = (*es1.elements)[i]; - Expression ee2 = (*es2.elements)[i]; + Expression ee1 = (*es1.elements)[i]; + Expression ee2 = (*es2.elements)[i]; - // https://issues.dlang.org/show_bug.cgi?id=16284 - if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp - continue; + // https://issues.dlang.org/show_bug.cgi?id=16284 + if (ee1.op == EXP.void_ && ee2.op == EXP.void_) // if both are VoidInitExp + continue; - if (ee1 == ee2) - continue; - if (!ee1 || !ee2) - return 1; - const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); - if (cmp) - return 1; - } - return 0; // All elements are equal + if (ee1 == ee2) + continue; + if (!ee1 || !ee2) + return 1; + const int cmp = ctfeRawCmp(loc, ee1, ee2, identity); + if (cmp) + return 1; } + return 0; // All elements are equal } if (e1.op == EXP.assocArrayLiteral && e2.op == EXP.assocArrayLiteral) { @@ -1316,13 +1310,13 @@ private int ctfeRawCmp(const ref Loc loc, Expression e1, Expression e2, bool ide } /// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 -bool ctfeEqual(const ref Loc loc, EXP op, Expression e1, Expression e2) +bool ctfeEqual(Loc loc, EXP op, Expression e1, Expression e2) { return !ctfeRawCmp(loc, e1, e2) ^ (op == EXP.notEqual); } /// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 -bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) +bool ctfeIdentity(Loc loc, EXP op, Expression e1, Expression e2) { //printf("ctfeIdentity %s %s\n", e1.toChars(), e2.toChars()); //printf("ctfeIdentity op = '%s', e1 = %s %s, e2 = %s %s\n", EXPtoString(op).ptr, @@ -1342,7 +1336,7 @@ bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) SymOffExp es2 = e2.isSymOffExp(); cmp = (es1.var == es2.var && es1.offset == es2.offset); } - else if (e1.type.isfloating()) + else if (e1.type.isFloating()) cmp = e1.isIdentical(e2); else { @@ -1354,29 +1348,29 @@ bool ctfeIdentity(const ref Loc loc, EXP op, Expression e1, Expression e2) } /// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 -bool ctfeCmp(const ref Loc loc, EXP op, Expression e1, Expression e2) +bool ctfeCmp(Loc loc, EXP op, Expression e1, Expression e2) { Type t1 = e1.type.toBasetype(); Type t2 = e2.type.toBasetype(); if (t1.isString() && t2.isString()) return specificCmp(op, ctfeRawCmp(loc, e1, e2)); - else if (t1.isreal()) + if (t1.isReal()) return realCmp(op, e1.toReal(), e2.toReal()); - else if (t1.isimaginary()) + if (t1.isImaginary()) return realCmp(op, e1.toImaginary(), e2.toImaginary()); - else if (t1.isunsigned() || t2.isunsigned()) + if (t1.isUnsigned() || t2.isUnsigned()) return intUnsignedCmp(op, e1.toInteger(), e2.toInteger()); else return intSignedCmp(op, e1.toInteger(), e2.toInteger()); } -UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) +UnionExp ctfeCat(Loc loc, Type type, Expression e1, Expression e2) { Type t1 = e1.type.toBasetype(); Type t2 = e2.type.toBasetype(); UnionExp ue; - if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isintegral()) + if (e2.op == EXP.string_ && e1.op == EXP.arrayLiteral && t1.nextOf().isIntegral()) { // [chars] ~ string => string (only valid for CTFE) StringExp es1 = e2.isStringExp(); @@ -1405,7 +1399,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) es.type = type; return ue; } - if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isintegral()) + if (e1.op == EXP.string_ && e2.op == EXP.arrayLiteral && t2.nextOf().isIntegral()) { // string ~ [chars] => string (only valid for CTFE) // Concatenate the strings @@ -1465,7 +1459,7 @@ UnionExp ctfeCat(const ref Loc loc, Type type, Expression e1, Expression e2) /* Given an AA literal 'ae', and a key 'e2': * Return ae[e2] if present, or NULL if not found. */ -Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2) +Expression findKeyInAA(Loc loc, AssocArrayLiteralExp ae, Expression e2) { /* Search the keys backwards, in case there are duplicate keys */ @@ -1486,7 +1480,7 @@ Expression findKeyInAA(const ref Loc loc, AssocArrayLiteralExp ae, Expression e2 * dynamic arrays, and strings. We know that e1 is an * interpreted CTFE expression, so it cannot have side-effects. */ -Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, uinteger_t indx) +Expression ctfeIndex(UnionExp* pue, Loc loc, Type type, Expression e1, uinteger_t indx) { //printf("ctfeIndex(e1 = %s)\n", e1.toChars()); assert(e1.type); @@ -1515,7 +1509,7 @@ Expression ctfeIndex(UnionExp* pue, const ref Loc loc, Type type, Expression e1, assert(0); } -Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expression e, bool explicitCast = false) +Expression ctfeCast(UnionExp* pue, Loc loc, Type type, Type to, Expression e, bool explicitCast = false) { Expression paint() { @@ -1535,11 +1529,9 @@ Expression ctfeCast(UnionExp* pue, const ref Loc loc, Type type, Type to, Expres : tclass.implicitConvTo(to.mutableOf()); if (match) return paint(); - else - { - emplaceExp!(NullExp)(pue, loc, to); - return pue.exp(); - } + + emplaceExp!(NullExp)(pue, loc, to); + return pue.exp(); } // Allow TypeInfo type painting @@ -1657,7 +1649,7 @@ void assignInPlace(Expression dest, Expression src) } // Given an AA literal aae, set aae[index] = newval and return newval. -Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) +Expression assignAssocArrayElement(Loc loc, AssocArrayLiteralExp aae, Expression index, Expression newval) { /* Create new associative array literal reflecting updated key/value */ @@ -1687,7 +1679,7 @@ Expression assignAssocArrayElement(const ref Loc loc, AssocArrayLiteralExp aae, /// Given array literal oldval of type ArrayLiteralExp or StringExp, of length /// oldlen, change its length to newlen. If the newlen is longer than oldlen, /// all new elements will be set to the default initializer for the element type. -Expression changeArrayLiteralLength(UnionExp* pue, const ref Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) +Expression changeArrayLiteralLength(UnionExp* pue, Loc loc, TypeArray arrayType, Expression oldval, size_t oldlen, size_t newlen) { Type elemType = arrayType.next; assert(elemType); @@ -1774,7 +1766,7 @@ bool isCtfeValueValid(Expression newval) case EXP.int64: case EXP.float64: case EXP.complex80: - return tb.isscalar(); + return tb.isScalar(); case EXP.null_: return tb.ty == Tnull || @@ -1849,7 +1841,7 @@ bool isCtfeValueValid(Expression newval) const SliceExp se = newval.isSliceExp(); assert(se.lwr && se.lwr.op == EXP.int64); assert(se.upr && se.upr.op == EXP.int64); - return (tb.ty == Tarray || tb.ty == Tsarray) && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral); + return tb.isStaticOrDynamicArray() && (se.e1.op == EXP.string_ || se.e1.op == EXP.arrayLiteral); } case EXP.void_: diff --git a/gcc/d/dmd/ctorflow.d b/gcc/d/dmd/ctorflow.d index ba5240e..e604700 100644 --- a/gcc/d/dmd/ctorflow.d +++ b/gcc/d/dmd/ctorflow.d @@ -1,12 +1,12 @@ /** * Manage flow analysis for constructors. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d, _ctorflow.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ctorflow.d, _ctorflow.d) * Documentation: https://dlang.org/phobos/dmd_ctorflow.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctorflow.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ctorflow.d */ module dmd.ctorflow; diff --git a/gcc/d/dmd/cxxfrontend.d b/gcc/d/dmd/cxxfrontend.d index a0432d2..234b652 100644 --- a/gcc/d/dmd/cxxfrontend.d +++ b/gcc/d/dmd/cxxfrontend.d @@ -1,19 +1,22 @@ /** * Contains C++ interfaces for interacting with DMD as a library. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cxxfrontend.d, _cxxfrontend.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/cxxfrontend.d, _cxxfrontend.d) * Documentation: https://dlang.org/phobos/dmd_cxxfrontend.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cxxfrontend.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/cxxfrontend.d */ module dmd.cxxfrontend; import dmd.aggregate : AggregateDeclaration; import dmd.arraytypes; import dmd.astenums; +import dmd.attrib; import dmd.common.outbuffer : OutBuffer; +import dmd.dclass : ClassDeclaration; +import dmd.declaration : TypeInfoDeclaration; import dmd.denum : EnumDeclaration; import dmd.dmodule /*: Module*/; import dmd.dscope : Scope; @@ -35,24 +38,33 @@ import dmd.statement : Statement, AsmStatement, GccAsmStatement; extern (C++, "dmd"): /*********************************************************** - * cppmangle.d + * atrtibsem.d + */ +Expressions* getAttributes(UserAttributeDeclaration a) +{ + import dmd.attribsem; + return dmd.attribsem.getAttributes(a); +} + +/*********************************************************** + * mangle/cpp.d */ const(char)* toCppMangleItanium(Dsymbol s) { - import dmd.cppmangle; - return dmd.cppmangle.toCppMangleItanium(s); + import dmd.mangle.cpp; + return dmd.mangle.cpp.toCppMangleItanium(s); } const(char)* cppTypeInfoMangleItanium(Dsymbol s) { - import dmd.cppmangle; - return dmd.cppmangle.cppTypeInfoMangleItanium(s); + import dmd.mangle.cpp; + return dmd.mangle.cpp.cppTypeInfoMangleItanium(s); } const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset) { - import dmd.cppmangle; - return dmd.cppmangle.cppThunkMangleItanium(fd, offset); + import dmd.mangle.cpp; + return dmd.mangle.cpp.cppThunkMangleItanium(fd, offset); } /*********************************************************** @@ -65,36 +77,36 @@ Expression ctfeInterpret(Expression e) } /*********************************************************** - * dmangle.d + * mangle/package.d */ const(char)* mangleExact(FuncDeclaration fd) { - import dmd.dmangle; - return dmd.dmangle.mangleExact(fd); + import dmd.mangle; + return dmd.mangle.mangleExact(fd); } void mangleToBuffer(Type t, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(t, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(t, buf); } void mangleToBuffer(Expression e, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(e, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(e, buf); } void mangleToBuffer(Dsymbol s, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(s, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(s, buf); } void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) { - import dmd.dmangle; - return dmd.dmangle.mangleToBuffer(ti, buf); + import dmd.mangle; + return dmd.mangle.mangleToBuffer(ti, buf); } /*********************************************************** @@ -143,7 +155,7 @@ void addMember(Dsymbol dsym, Scope* sc, ScopeDsymbol sds) return dmd.dsymbolsem.addMember(dsym, sc, sds); } -Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, SearchOptFlags +Dsymbol search(Dsymbol d, Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) { import dmd.dsymbolsem; @@ -162,6 +174,24 @@ void importAll(Dsymbol d, Scope* sc) return dmd.dsymbolsem.importAll(d, sc); } +Dsymbols* include(Dsymbol d, Scope* sc) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.include(d, sc); +} + +bool isFuncHidden(ClassDeclaration cd, FuncDeclaration fd) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.isFuncHidden(cd, fd); +} + +Dsymbol vtblSymbol(ClassDeclaration cd) +{ + import dmd.dsymbolsem; + return dmd.dsymbolsem.vtblSymbol(cd); +} + /*********************************************************** * dtemplate.d */ @@ -217,7 +247,7 @@ void genCppHdrFiles(ref Modules ms) /*********************************************************** * enumsem.d */ -Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) +Expression getDefaultValue(EnumDeclaration ed, Loc loc) { import dmd.enumsem; return dmd.enumsem.getDefaultValue(ed, loc); @@ -240,6 +270,26 @@ Expression expressionSemantic(Expression e, Scope* sc) return dmd.expressionsem.expressionSemantic(e, sc); } +bool fill(StructDeclaration sd, Loc loc, + ref Expressions elements, bool ctorinit) +{ + import dmd.expressionsem; + return dmd.expressionsem.fill(sd, loc, elements, ctorinit); +} + +/*********************************************************** + * func.d + */ +FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = STC.none) +{ + return FuncDeclaration.genCfunc(fparams, treturn, name, cast(STC) stc); +} + +FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = STC.none) +{ + return FuncDeclaration.genCfunc(fparams, treturn, id, cast(STC) stc); +} + /*********************************************************** * funcsem.d */ @@ -255,6 +305,18 @@ bool functionSemantic3(FuncDeclaration fd) return dmd.funcsem.functionSemantic3(fd); } +MATCH leastAsSpecialized(FuncDeclaration fd, FuncDeclaration g, Identifiers* names) +{ + import dmd.funcsem; + return dmd.funcsem.leastAsSpecialized(fd, g, names); +} + +PURE isPure(FuncDeclaration fd) +{ + import dmd.funcsem; + return dmd.funcsem.isPure(fd); +} + /*********************************************************** * hdrgen.d */ @@ -303,13 +365,13 @@ const(char)* parametersTypeToChars(ParameterList pl) /*********************************************************** * iasm.d */ -Statement asmSemantic(AsmStatement s, Scope *sc) +Statement asmSemantic(AsmStatement s, Scope* sc) { import dmd.iasm; return dmd.iasm.asmSemantic(s, sc); } -void asmSemantic(CAsmDeclaration d, Scope *sc) +void asmSemantic(CAsmDeclaration d, Scope* sc) { import dmd.iasm; return dmd.iasm.asmSemantic(d, sc); @@ -318,13 +380,13 @@ void asmSemantic(CAsmDeclaration d, Scope *sc) /*********************************************************** * iasmgcc.d */ -Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) +Statement gccAsmSemantic(GccAsmStatement s, Scope* sc) { import dmd.iasmgcc; return dmd.iasmgcc.gccAsmSemantic(s, sc); } -void gccAsmSemantic(CAsmDeclaration d, Scope *sc) +void gccAsmSemantic(CAsmDeclaration d, Scope* sc) { import dmd.iasmgcc; return dmd.iasmgcc.gccAsmSemantic(d, sc); @@ -403,6 +465,11 @@ void semanticTypeInfoMembers(StructDeclaration sd) return dmd.semantic3.semanticTypeInfoMembers(sd); } +bool checkClosure(FuncDeclaration fd) +{ + import dmd.semantic3; + return dmd.semantic3.checkClosure(fd); +} /*********************************************************** * statementsem.d */ @@ -430,13 +497,13 @@ bool hasPointers(Type t) return dmd.typesem.hasPointers(t); } -Type typeSemantic(Type type, const ref Loc loc, Scope* sc) +Type typeSemantic(Type type, Loc loc, Scope* sc) { import dmd.typesem; return dmd.typesem.typeSemantic(type, loc, sc); } -Type trySemantic(Type type, const ref Loc loc, Scope* sc) +Type trySemantic(Type type, Loc loc, Scope* sc) { import dmd.typesem; return dmd.typesem.trySemantic(type, loc, sc); @@ -454,7 +521,7 @@ Type merge2(Type type) return dmd.typesem.merge2(type); } -Expression defaultInit(Type mt, const ref Loc loc, const bool isCfile = false) +Expression defaultInit(Type mt, Loc loc, const bool isCfile = false) { import dmd.typesem; return dmd.typesem.defaultInit(mt, loc, isCfile); @@ -470,7 +537,13 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) { import dmd.typesem; - return dmd.typesem.covariant(src, t, pstc, cppCovariant); + return dmd.typesem.covariant(src, t, cast(STC*) pstc, cppCovariant); +} + +bool isZeroInit(Type t, Loc loc) +{ + import dmd.typesem; + return dmd.typesem.isZeroInit(t, loc); } bool isBaseOf(Type tthis, Type t, int* poffset) @@ -596,7 +669,7 @@ Type addMod(Type type, MOD mod) Type addStorageClass(Type type, StorageClass stc) { import dmd.typesem; - return dmd.typesem.addStorageClass(type, stc); + return dmd.typesem.addStorageClass(type, cast(STC) stc); } Type pointerTo(Type type) @@ -611,10 +684,34 @@ Type referenceTo(Type type) return dmd.typesem.referenceTo(type); } +uinteger_t size(Type type) +{ + import dmd.typesem; + return dmd.typesem.size(type); +} + +uinteger_t size(Type type, Loc loc) +{ + import dmd.typesem; + return dmd.typesem.size(type, loc); +} + +MATCH implicitConvTo(Type from, Type to) +{ + import dmd.dcast; + return dmd.dcast.implicitConvTo(from, to); +} + +MATCH constConv(Type from, Type to) +{ + import dmd.typesem; + return dmd.typesem.constConv(from, to); +} + /*********************************************************** * typinf.d */ -bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) +bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc) { import dmd.typinf; return dmd.typinf.genTypeInfo(e, loc, torig, sc); @@ -632,6 +729,18 @@ bool builtinTypeInfo(Type t) return dmd.typinf.builtinTypeInfo(t); } +Type makeNakedAssociativeArray(TypeAArray t) +{ + import dmd.typinf; + return dmd.typinf.makeNakedAssociativeArray(t); +} + +TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc) +{ + import dmd.typinf; + return dmd.typinf.getTypeInfoAssocArrayDeclaration(t, sc); +} + version (IN_LLVM) { /*********************************************************** diff --git a/gcc/d/dmd/dcast.d b/gcc/d/dmd/dcast.d index 8a713f4..86d2f0f 100644 --- a/gcc/d/dmd/dcast.d +++ b/gcc/d/dmd/dcast.d @@ -1,12 +1,12 @@ /** * Semantic analysis for cast-expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d, _dcast.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dcast.d, _dcast.d) * Documentation: https://dlang.org/phobos/dmd_dcast.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dcast.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dcast.d */ module dmd.dcast; @@ -20,6 +20,7 @@ import dmd.arraytypes; import dmd.astenums; import dmd.dclass; import dmd.declaration; +import dmd.denum; import dmd.dinterpret; import dmd.dscope; import dmd.dstruct; @@ -45,6 +46,7 @@ import dmd.root.ctfloat; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.root.utf; +import dmd.safe : setUnsafe; import dmd.tokens; import dmd.typesem; @@ -69,8 +71,8 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) { Expression visit(Expression e) { - // printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); - if (const match = (sc && sc.flags & SCOPE.Cfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) + //printf("Expression.implicitCastTo(%s of type %s) => %s\n", e.toChars(), e.type.toChars(), t.toChars()); + if (const match = (sc && sc.inCfile) ? e.cimplicitConvTo(t) : e.implicitConvTo(t)) { // no need for an extra cast when matching is exact @@ -148,7 +150,7 @@ Expression implicitCastTo(Expression e, Scope* sc, Type t) //printf("type %s t %s\n", type.deco, t.deco); auto ts = toAutoQualChars(e.type, t); error(e.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", - e.toChars(), ts[0], ts[1]); + e.toErrMsg(), ts[0], ts[1]); } } return ErrorExp.get(); @@ -271,7 +273,7 @@ MATCH implicitConvTo(Expression e, Type t) /* See if we can do integral narrowing conversions */ - if (e.type.isintegral() && t.isintegral() && e.type.isTypeBasic() && t.isTypeBasic()) + if (e.type.isIntegral() && t.isIntegral() && e.type.isTypeBasic() && t.isTypeBasic()) { IntRange src = getIntRange(e); IntRange target = IntRange.fromType(t); @@ -320,14 +322,14 @@ MATCH implicitConvTo(Expression e, Type t) Type t1b = e.e1.type.toBasetype(); Type t2b = e.e2.type.toBasetype(); - if (t1b.ty == Tpointer && t2b.isintegral() && t1b.equivalent(tb)) + if (t1b.ty == Tpointer && t2b.isIntegral() && t1b.equivalent(tb)) { // ptr + offset // ptr - offset MATCH m = e.e1.implicitConvTo(t); return (m > MATCH.constant) ? MATCH.constant : m; } - if (t2b.ty == Tpointer && t1b.isintegral() && t2b.equivalent(tb)) + if (t2b.ty == Tpointer && t1b.isIntegral() && t2b.equivalent(tb)) { // offset + ptr MATCH m = e.e2.implicitConvTo(t); @@ -452,7 +454,7 @@ MATCH implicitConvTo(Expression e, Type t) bool isLosslesslyConvertibleToFP(T)() { - if (e.type.isunsigned()) + if (e.type.isUnsigned()) { const f = cast(T) value; return cast(dinteger_t) f == value; @@ -472,7 +474,7 @@ MATCH implicitConvTo(Expression e, Type t) case Tint8: if (ty == Tuns64 && value & ~0x7FU) return MATCH.nomatch; - else if (cast(byte)value != value) + if (cast(byte)value != value) return MATCH.nomatch; break; @@ -489,7 +491,7 @@ MATCH implicitConvTo(Expression e, Type t) case Tint16: if (ty == Tuns64 && value & ~0x7FFFU) return MATCH.nomatch; - else if (cast(short)value != value) + if (cast(short)value != value) return MATCH.nomatch; break; @@ -624,7 +626,7 @@ MATCH implicitConvTo(Expression e, Type t) if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid) return MATCH.nomatch; - if (!(e.type.ty == Tsarray || e.type.ty == Tarray || e.type.ty == Tpointer)) + if (!(e.type.isStaticOrDynamicArray() || e.type.ty == Tpointer)) return visit(e); TY tyn = e.type.nextOf().ty; @@ -702,8 +704,14 @@ MATCH implicitConvTo(Expression e, Type t) if (!tn.isConst() && !tn.isImmutable()) return MATCH.nomatch; m = MATCH.constant; + + // After converting e.g. ubyte[] to const(ubyte)[], don't change + // to MATCH.convert, return MATCH.constant + // https://github.com/dlang/dmd/issues/20635 + if (e.type.ty == t.ty && e.type.nextOf().ty == tn.ty) + return m; } - if (e.hexString && tn.isintegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) + if (e.type != t && e.hexString && tn.isIntegral && (tn.size == e.sz || (!e.committed && (e.len % tn.size) == 0))) { m = MATCH.convert; return m; @@ -756,8 +764,7 @@ MATCH implicitConvTo(Expression e, Type t) Type typeb = e.type.toBasetype(); auto result = MATCH.nomatch; - if ((tb.ty == Tarray || tb.ty == Tsarray) && - (typeb.ty == Tarray || typeb.ty == Tsarray)) + if (tb.isStaticOrDynamicArray() && typeb.isStaticOrDynamicArray()) { result = MATCH.exact; Type typen = typeb.nextOf().toBasetype(); @@ -800,7 +807,7 @@ MATCH implicitConvTo(Expression e, Type t) return result; } - else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray || typeb.ty == Tpointer)) + else if (tb.ty == Tvector && (typeb.isStaticOrDynamicArray() || typeb.ty == Tpointer)) { // Tpointer because ImportC eagerly converts Tsarray to Tpointer result = MATCH.exact; // Convert array literal to vector type @@ -930,7 +937,7 @@ MATCH implicitConvTo(Expression e, Type t) */ Type tb = t.toBasetype(); MOD mod = tb.mod; - if (tf.isref) + if (tf.isRef) { } else @@ -1162,7 +1169,7 @@ MATCH implicitConvTo(Expression e, Type t) if (result != MATCH.nomatch) return result; - if (t.isintegral() && e.e1.type.isintegral() && e.e1.implicitConvTo(t) != MATCH.nomatch) + if (t.isIntegral() && e.e1.type.isIntegral() && e.e1.implicitConvTo(t) != MATCH.nomatch) result = MATCH.convert; else result = visit(e); @@ -1468,6 +1475,462 @@ MATCH implicitConvTo(Expression e, Type t) } } +/******************************** + * Determine if 'from' can be implicitly converted + * to type 'to'. + * Returns: + * MATCH.nomatch, MATCH.convert, MATCH.constant, MATCH.exact + */ +MATCH implicitConvTo(Type from, Type to) +{ + MATCH visitType(Type from) + { + //printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to); + //printf("from: %s\n", from.toChars()); + //printf("to : %s\n", to.toChars()); + if (from.equals(to)) + return MATCH.exact; + return MATCH.nomatch; + + } + + MATCH visitBasic(TypeBasic from) + { + //printf("TypeBasic::implicitConvTo(%s) from %s\n", to.toChars(), from.toChars()); + if (from == to) + return MATCH.exact; + + if (from.ty == to.ty) + { + if (from.mod == to.mod) + return MATCH.exact; + if (MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + if (!((from.mod ^ to.mod) & MODFlags.shared_)) // for wild matching + return MATCH.constant; + return MATCH.convert; + } + + if (from.ty == Tvoid || to.ty == Tvoid) + return MATCH.nomatch; + if (to.ty == Tbool) + return MATCH.nomatch; + + TypeBasic tob; + if (to.ty == Tvector && to.deco) + { + TypeVector tv = cast(TypeVector)to; + tob = tv.elementType(); + } + else if (auto te = to.isTypeEnum()) + { + EnumDeclaration ed = te.sym; + if (ed.isSpecial()) + { + /* Special enums that allow implicit conversions to them + * with a MATCH.convert + */ + tob = to.toBasetype().isTypeBasic(); + } + else + return MATCH.nomatch; + } + else + tob = to.isTypeBasic(); + if (!tob) + return MATCH.nomatch; + + if (from.flags & TFlags.integral) + { + // Disallow implicit conversion of integers to imaginary or complex + if (tob.flags & (TFlags.imaginary | TFlags.complex)) + return MATCH.nomatch; + + // If converting from integral to integral + if (tob.flags & TFlags.integral) + { + const sz = size(from, Loc.initial); + const tosz = tob.size(Loc.initial); + + /* Can't convert to smaller size + */ + if (sz > tosz) + return MATCH.nomatch; + /* Can't change sign if same size + */ + //if (sz == tosz && (flags ^ tob.flags) & TFlags.unsigned) + // return MATCH.nomatch; + } + } + else if (from.flags & TFlags.floating) + { + // Disallow implicit conversion of floating point to integer + if (tob.flags & TFlags.integral) + return MATCH.nomatch; + + assert(tob.flags & TFlags.floating || to.ty == Tvector); + + // Disallow implicit conversion from complex to non-complex + if (from.flags & TFlags.complex && !(tob.flags & TFlags.complex)) + return MATCH.nomatch; + + // Disallow implicit conversion of real or imaginary to complex + if (from.flags & (TFlags.real_ | TFlags.imaginary) && tob.flags & TFlags.complex) + return MATCH.nomatch; + + // Disallow implicit conversion to-from real and imaginary + if ((from.flags & (TFlags.real_ | TFlags.imaginary)) != (tob.flags & (TFlags.real_ | TFlags.imaginary))) + return MATCH.nomatch; + } + return MATCH.convert; + + } + + MATCH visitVector(TypeVector from) + { + //printf("TypeVector::implicitConvTo(%s) from %s\n", to.toChars(), from.toChars()); + if (from == to) + return MATCH.exact; + if (to.ty != Tvector) + return MATCH.nomatch; + + TypeVector tv = cast(TypeVector)to; + assert(from.basetype.ty == Tsarray && tv.basetype.ty == Tsarray); + + // Can't convert to a vector which has different size. + if (from.basetype.size() != tv.basetype.size()) + return MATCH.nomatch; + + // Allow conversion to void[] + if (tv.basetype.nextOf().ty == Tvoid) + return MATCH.convert; + + // Otherwise implicitly convertible only if basetypes are. + return from.basetype.implicitConvTo(tv.basetype); + } + + MATCH visitSArray(TypeSArray from) + { + //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), from.toChars()); + if (auto ta = to.isTypeDArray()) + { + if (!MODimplicitConv(from.next.mod, ta.next.mod)) + return MATCH.nomatch; + + /* Allow conversion to void[] + */ + if (ta.next.ty == Tvoid) + { + return MATCH.convert; + } + + MATCH m = from.next.constConv(ta.next); + if (m > MATCH.nomatch) + { + return MATCH.convert; + } + return MATCH.nomatch; + } + if (auto tsa = to.isTypeSArray()) + { + if (from == to) + return MATCH.exact; + + if (from.dim.equals(tsa.dim)) + { + MATCH m = from.next.implicitConvTo(tsa.next); + + /* Allow conversion to non-interface base class. + */ + if (m == MATCH.convert && + from.next.ty == Tclass) + { + if (auto toc = tsa.next.isTypeClass) + { + if (!toc.sym.isInterfaceDeclaration) + return MATCH.convert; + } + } + + /* Since static arrays are value types, allow + * conversions from const elements to non-const + * ones, just like we allow conversion from const int + * to int. + */ + if (m >= MATCH.constant) + { + if (from.mod != to.mod) + m = MATCH.constant; + return m; + } + } + } + return MATCH.nomatch; + } + + MATCH visitDArray(TypeDArray from) + { + //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), from.toChars()); + if (from.equals(to)) + return MATCH.exact; + + if (auto ta = to.isTypeDArray()) + { + if (!MODimplicitConv(from.next.mod, ta.next.mod)) + return MATCH.nomatch; // not const-compatible + + /* Allow conversion to void[] + */ + if (from.next.ty != Tvoid && ta.next.ty == Tvoid) + { + return MATCH.convert; + } + + MATCH m = from.next.constConv(ta.next); + if (m > MATCH.nomatch) + { + if (m == MATCH.exact && from.mod != to.mod) + m = MATCH.constant; + return m; + } + } + + return visitType(from); + } + + MATCH visitAArray(TypeAArray from) + { + //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), from.toChars()); + if (from.equals(to)) + return MATCH.exact; + + if (auto ta = to.isTypeAArray()) + { + if (!MODimplicitConv(from.next.mod, ta.next.mod)) + return MATCH.nomatch; // not const-compatible + + if (!MODimplicitConv(from.index.mod, ta.index.mod)) + return MATCH.nomatch; // not const-compatible + + MATCH m = from.next.constConv(ta.next); + MATCH mi = from.index.constConv(ta.index); + if (m > MATCH.nomatch && mi > MATCH.nomatch) + { + return MODimplicitConv(from.mod, to.mod) ? MATCH.constant : MATCH.nomatch; + } + } + return visitType(from); + } + + /+ + + Checks whether this function type is convertible to ` to` + + when used in a function pointer / delegate. + + + + Params: + + to = target type + + + + Returns: + + MATCH.nomatch: `to` is not a covaraint function + + MATCH.convert: `to` is a covaraint function + + MATCH.exact: `to` is identical to this function + +/ + MATCH implicitPointerConv(TypeFunction tf, Type to) + { + assert(to); + + if (tf.equals(to)) + return MATCH.constant; + + if (tf.covariant(to) == Covariant.yes) + { + Type tret = tf.nextOf(); + Type toret = to.nextOf(); + if (tret.ty == Tclass && toret.ty == Tclass) + { + /* https://issues.dlang.org/show_bug.cgi?id=10219 + * Check covariant interface return with offset tweaking. + * interface I {} + * class C : Object, I {} + * I function() dg = function C() {} // should be error + */ + int offset = 0; + if (toret.isBaseOf(tret, &offset) && offset != 0) + return MATCH.nomatch; + } + return MATCH.convert; + } + + return MATCH.nomatch; + } + + MATCH visitPointer(TypePointer from) + { + //printf("TypePointer::implicitConvTo(to = %s) %s\n", to.toChars(), from.toChars()); + if (from.equals(to)) + return MATCH.exact; + + // Only convert between pointers + auto tp = to.isTypePointer(); + if (!tp) + return MATCH.nomatch; + + assert(from.next); + assert(tp.next); + + // Conversion to void* + if (tp.next.ty == Tvoid) + { + // Function pointer conversion doesn't check constness? + if (from.next.ty == Tfunction) + return MATCH.convert; + + if (!MODimplicitConv(from.next.mod, tp.next.mod)) + return MATCH.nomatch; // not const-compatible + + return from.next.ty == Tvoid ? MATCH.constant : MATCH.convert; + } + + // Conversion between function pointers + if (auto thisTf = from.next.isTypeFunction()) + return implicitPointerConv(thisTf, tp.next); + + // Default, no implicit conversion between the pointer targets + MATCH m = from.next.constConv(tp.next); + + if (m == MATCH.exact && from.mod != to.mod) + m = MATCH.constant; + return m; + } + + MATCH visitDelegate(TypeDelegate from) + { + //printf("TypeDelegate.implicitConvTo(this=%p, to=%p)\n", from, to); + //printf("from: %s\n", from.toChars()); + //printf("to : %s\n", to.toChars()); + if (from.equals(to)) + return MATCH.exact; + + if (auto toDg = to.isTypeDelegate()) + { + MATCH m = implicitPointerConv(from.next.isTypeFunction(), toDg.next); + + // Retain the old behaviour for this refactoring + // Should probably be changed to constant to match function pointers + if (m > MATCH.convert) + m = MATCH.convert; + + return m; + } + + return MATCH.nomatch; + } + + MATCH visitStruct(TypeStruct from) + { + //printf("TypeStruct::implicitConvTo(%s => %s)\n", from.toChars(), to.toChars()); + MATCH m = from.implicitConvToWithoutAliasThis(to); + return m == MATCH.nomatch ? from.implicitConvToThroughAliasThis(to) : m; + } + + MATCH visitEnum(TypeEnum from) + { + import dmd.enumsem : getMemtype; + + MATCH m; + //printf("TypeEnum::implicitConvTo() %s to %s\n", from.toChars(), to.toChars()); + if (from.ty == to.ty && from.sym == (cast(TypeEnum)to).sym) + m = (from.mod == to.mod) ? MATCH.exact : MATCH.constant; + else if (from.sym.getMemtype(Loc.initial).implicitConvTo(to)) + m = MATCH.convert; // match with conversions + else + m = MATCH.nomatch; // no match + return m; + } + + MATCH visitClass(TypeClass from) + { + //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to.toChars(), from.toChars()); + MATCH m = from.implicitConvToWithoutAliasThis(to); + return m ? m : from.implicitConvToThroughAliasThis(to); + } + + MATCH visitTuple(TypeTuple from) + { + if (from == to) + return MATCH.exact; + if (auto tt = to.isTypeTuple()) + { + if (from.arguments.length == tt.arguments.length) + { + MATCH m = MATCH.exact; + for (size_t i = 0; i < tt.arguments.length; i++) + { + Parameter arg1 = (*from.arguments)[i]; + Parameter arg2 = (*tt.arguments)[i]; + MATCH mi = arg1.type.implicitConvTo(arg2.type); + if (mi < m) + m = mi; + } + return m; + } + } + return MATCH.nomatch; + } + + MATCH visitNull(TypeNull from) + { + //printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", from, to); + //printf("from: %s\n", from.toChars()); + //printf("to : %s\n", to.toChars()); + MATCH m = visitType(cast(Type)from); + if (m != MATCH.nomatch) + return m; + + //NULL implicitly converts to any pointer type or dynamic array + //if (type.ty == Tpointer && type.nextOf().ty == Tvoid) + { + Type tb = to.toBasetype(); + if (tb.ty == Tnull || tb.ty == Tpointer || tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tclass || tb.ty == Tdelegate) + return MATCH.constant; + } + + return MATCH.nomatch; + } + + MATCH visitNoreturn(TypeNoreturn from) + { + //printf("TypeNoreturn::implicitConvTo(this=%p, to=%p)\n", from, to); + //printf("from: %s\n", from.toChars()); + //printf("to : %s\n", to.toChars()); + if (from.equals(to)) + return MATCH.exact; + + // Different qualifiers? + if (to.ty == Tnoreturn) + return MATCH.constant; + + // Implicitly convertible to any type + return MATCH.convert; + } + + switch(from.ty) + { + default: return from.isTypeBasic() ? visitBasic(from.isTypeBasic()) : visitType(from); + case Tvector: return visitVector(from.isTypeVector()); + case Tsarray: return visitSArray(from.isTypeSArray()); + case Tarray: return visitDArray(from.isTypeDArray()); + case Taarray: return visitAArray(from.isTypeAArray()); + case Tpointer: return visitPointer(from.isTypePointer()); + case Tdelegate: return visitDelegate(from.isTypeDelegate()); + case Tstruct: return visitStruct(from.isTypeStruct()); + case Tenum: return visitEnum(from.isTypeEnum()); + case Tclass: return visitClass(from.isTypeClass()); + case Ttuple: return visitTuple(from.isTypeTuple()); + case Tnull: return visitNull(from.isTypeNull()); + case Tnoreturn: return visitNoreturn(from.isTypeNoreturn()); + } +} + /** * Same as implicitConvTo(); except follow C11 rules, which are quite a bit * more permissive than D. @@ -1489,12 +1952,12 @@ MATCH cimplicitConvTo(Expression e, Type t) if (tb.isTypeVector() || typeb.isTypeVector()) return implicitConvTo(e, t); // permissive checking doesn't apply to vectors - if ((typeb.isintegral() || typeb.isfloating()) && - (tb.isintegral() || tb.isfloating())) + if ((typeb.isIntegral() || typeb.isFloating()) && + (tb.isIntegral() || tb.isFloating())) return MATCH.convert; - if (tb.ty == Tpointer && typeb.isintegral()) // C11 6.3.2.3-5 + if (tb.ty == Tpointer && typeb.isIntegral()) // C11 6.3.2.3-5 return MATCH.convert; - if (tb.isintegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6 + if (tb.isIntegral() && typeb.ty == Tpointer) // C11 6.3.2.3-6 return MATCH.convert; if (tb.ty == Tpointer && typeb.ty == Tpointer) // C11 6.3.2.3-7 return MATCH.convert; @@ -1595,8 +2058,8 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) const(bool) t1b_isR = (t1b_isFR || t1b.ty == Tpointer || t1b.ty == Taarray || t1b.ty == Tclass); // Arithmetic types (== valueable basic types) - const(bool) tob_isA = ((tob.isintegral() || tob.isfloating()) && tob.ty != Tvector); - const(bool) t1b_isA = ((t1b.isintegral() || t1b.isfloating()) && t1b.ty != Tvector); + const(bool) tob_isA = ((tob.isIntegral() || tob.isFloating()) && tob.ty != Tvector); + const(bool) t1b_isA = ((t1b.isIntegral() || t1b.isFloating()) && t1b.ty != Tvector); // Try casting the alias this member. // Return the expression if it succeeds, null otherwise. @@ -1682,8 +2145,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (hasAliasThis) { - auto result = tryAliasThisCast(); - if (result) + if (auto result = tryAliasThisCast()) return result; } @@ -1777,8 +2239,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) */ if (hasAliasThis) { - auto result = tryAliasThisCast(); - if (result) + if (auto result = tryAliasThisCast()) return result; } error(e.loc, "cannot cast expression `%s` of type `%s` to `%s`", e.toChars(), e.type.toChars(), t.toChars()); @@ -1801,7 +2262,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (!e.type.equals(t)) { - if ((e.type.isreal() && t.isreal()) || (e.type.isimaginary() && t.isimaginary())) + if ((e.type.isReal() && t.isReal()) || (e.type.isImaginary() && t.isImaginary())) { auto result = e.copy(); result.type = t; @@ -1817,7 +2278,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (!e.type.equals(t)) { - if (e.type.iscomplex() && t.iscomplex()) + if (e.type.isComplex() && t.isComplex()) { auto result = e.copy(); result.type = t; @@ -1848,7 +2309,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t.toChars(), e.toChars(), e.committed); if (!e.committed && t.ty == Tpointer && t.nextOf().ty == Tvoid && - (!sc || !(sc.flags & SCOPE.Cfile))) + (!sc || !sc.inCfile)) { error(e.loc, "cannot convert string literal to `void*`"); return ErrorExp.get(); @@ -1878,7 +2339,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); - if (e.hexString && !e.committed) + if (e.hexString && !e.committed && tb.nextOf().isIntegral) { const szx = cast(ubyte) tb.nextOf().size(); if (szx != se.sz && (e.len % szx) == 0) @@ -1979,9 +2440,9 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (e.committed) goto Lcast; - auto X(T, U)(T tf, U tt) + static auto X(T, U)(T tf, U tt) { - return (cast(int)tf * 256 + cast(int)tt); + return cast(int)tf * 256 + cast(int)tt; } { @@ -2099,7 +2560,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) // See if need to truncate or extend the literal if (auto tsa = tb.isTypeSArray()) { - size_t dim2 = cast(size_t)tsa.dim.toInteger(); + const dim2 = cast(size_t)tsa.dim.toInteger(); //printf("dim from = %d, to = %d\n", cast(int)se.len, cast(int)dim2); // Changing dimensions @@ -2179,8 +2640,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) tb.ty == Tpointer && tb.nextOf().ty == Tfunction) { auto ve = e.e1.isVarExp(); - auto f = ve.var.isFuncDeclaration(); - if (f) + if (auto f = ve.var.isFuncDeclaration()) { assert(f.isImportedSymbol()); f = f.overloadExactMatch(tb.nextOf()); @@ -2257,7 +2717,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) Type tb = t.toBasetype(); if (tb.ty == Tarray) { - if (checkArrayLiteralEscape(sc, ae, false)) + if (checkArrayLiteralEscape(*sc, ae, false)) { return ErrorExp.get(); } @@ -2269,8 +2729,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) } Type typeb = e.type.toBasetype(); - if ((tb.ty == Tarray || tb.ty == Tsarray) && - (typeb.ty == Tarray || typeb.ty == Tsarray)) + if (tb.isStaticOrDynamicArray() && typeb.isStaticOrDynamicArray()) { if (tb.nextOf().toBasetype().ty == Tvoid && typeb.nextOf().toBasetype().ty != Tvoid) { @@ -2285,7 +2744,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) if (auto tsa = tb.isTypeSArray()) { if (e.elements.length != tsa.dim.toInteger()) - goto L1; + return visit(ae); } ae = e.copy().isArrayLiteralExp(); @@ -2313,7 +2772,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) ae.type = tp; } } - else if (tb.ty == Tvector && (typeb.ty == Tarray || typeb.ty == Tsarray || typeb.ty == Tpointer)) + else if (tb.ty == Tvector && (typeb.isStaticOrDynamicArray() || typeb.ty == Tpointer)) { // Convert array literal to vector type // The Tpointer case comes from C eagerly converting Tsarray to Tpointer @@ -2323,7 +2782,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) const edim = e.elements.length; const tbasedim = tbase.dim.toInteger(); if (edim > tbasedim) - goto L1; + return visit(ae); ae = e.copy().isArrayLiteralExp(); ae.type = tbase; // https://issues.dlang.org/show_bug.cgi?id=12642 @@ -2347,7 +2806,6 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) ev = ev.expressionSemantic(sc); return ev; } - L1: return visit(ae); } @@ -2380,6 +2838,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) (*ae.keys)[i] = ex; } ae.type = t; + semanticTypeInfo(sc, ae.type); return ae; } return visit(e); @@ -2467,7 +2926,10 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", e.toChars(), e.type.toChars(), t.toChars()); } - static immutable msg = "cannot form delegate due to covariant return type"; + void errorCovariantReturnType() + { + error(e.loc, "cannot form delegate due to covariant return type"); + } Type tb = t.toBasetype(); Type typeb = e.type.toBasetype(); @@ -2477,7 +2939,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) int offset; e.func.tookAddressOf++; if (e.func.tintro && e.func.tintro.nextOf().isBaseOf(e.func.type.nextOf(), &offset) && offset) - error(e.loc, "%s", msg.ptr); + errorCovariantReturnType(); auto result = e.copy(); result.type = t; return result; @@ -2488,12 +2950,11 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) { if (e.func) { - auto f = e.func.overloadExactMatch(tb.nextOf()); - if (f) + if (auto f = e.func.overloadExactMatch(tb.nextOf())) { int offset; if (f.tintro && f.tintro.nextOf().isBaseOf(f.type.nextOf(), &offset) && offset) - error(e.loc, "%s", msg.ptr); + errorCovariantReturnType(); if (f != e.func) // if address not already marked as taken f.tookAddressOf++; auto result = new DelegateExp(e.loc, e.e1, f, false, e.vthis2); @@ -2501,7 +2962,7 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null) return result; } if (e.func.tintro) - error(e.loc, "%s", msg.ptr); + errorCovariantReturnType(); } } @@ -2683,7 +3144,7 @@ Expression inferType(Expression e, Type t, int flag = 0) Expression visitAle(ArrayLiteralExp ale) { Type tb = t.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { Type tn = tb.nextOf(); if (ale.basis) @@ -2765,7 +3226,7 @@ Expression scaleFactor(BinExp be, Scope* sc) Type t2b = be.e2.type.toBasetype(); Expression eoff; - if (t1b.ty == Tpointer && t2b.isintegral()) + if (t1b.ty == Tpointer && t2b.isIntegral()) { // Need to adjust operator by the stride // Replace (ptr + int) with (ptr + (int * stride)) @@ -2779,7 +3240,7 @@ Expression scaleFactor(BinExp be, Scope* sc) be.e2.type = t; be.type = be.e1.type; } - else if (t2b.ty == Tpointer && t1b.isintegral()) + else if (t2b.ty == Tpointer && t1b.isIntegral()) { // Need to adjust operator by the stride // Replace (int + ptr) with (ptr + (int * stride)) @@ -2806,7 +3267,7 @@ Expression scaleFactor(BinExp be, Scope* sc) if (eoff.op == EXP.int64 && eoff.toInteger() == 0) { } - else if (sc.setUnsafe(false, be.loc, "pointer arithmetic not allowed in @safe functions")) + else if (sc.setUnsafe(false, be.loc, "pointer arithmetic")) { return ErrorExp.get(); } @@ -2827,7 +3288,7 @@ private bool isVoidArrayLiteral(Expression e, Type other) { auto ale = e.isArrayLiteralExp(); e = ale[0]; - if (other.ty == Tsarray || other.ty == Tarray) + if (other.isStaticOrDynamicArray()) other = other.nextOf(); else return false; @@ -2902,16 +3363,16 @@ Type typeMerge(Scope* sc, EXP op, ref Expression pe1, ref Expression pe2) Type t1b = e1.type.toBasetype(); Type t2b = e2.type.toBasetype(); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { // Integral types can be implicitly converted to pointers if ((t1b.ty == Tpointer) != (t2b.ty == Tpointer)) { - if (t1b.isintegral()) + if (t1b.isIntegral()) { return convert(e1, t2b); } - else if (t2b.isintegral()) + else if (t2b.isIntegral()) { return convert(e2, t1b); } @@ -3033,8 +3494,8 @@ Lagain: d.purity = PURE.impure; assert(d.purity != PURE.fwdref); - d.isnothrow = (tf1.isnothrow && tf2.isnothrow); - d.isnogc = (tf1.isnogc && tf2.isnogc); + d.isNothrow = (tf1.isNothrow && tf2.isNothrow); + d.isNogc = (tf1.isNogc && tf2.isNogc); if (tf1.trust == tf2.trust) d.trust = tf1.trust; @@ -3093,7 +3554,7 @@ Lagain: return null; } - if ((t1.ty == Tsarray || t1.ty == Tarray) && (e2.op == EXP.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == EXP.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1))) + if (t1.isStaticOrDynamicArray() && (e2.op == EXP.null_ && t2.ty == Tpointer && t2.nextOf().ty == Tvoid || e2.op == EXP.arrayLiteral && t2.ty == Tsarray && t2.nextOf().ty == Tvoid && t2.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e2, t1))) { /* (T[n] op void*) => T[] * (T[] op void*) => T[] @@ -3105,7 +3566,9 @@ Lagain: return coerce(t1.nextOf().arrayOf()); } - if ((t2.ty == Tsarray || t2.ty == Tarray) && (e1.op == EXP.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == EXP.arrayLiteral && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2))) + if (t2.isStaticOrDynamicArray() && + (e1.op == EXP.null_ && t1.ty == Tpointer && t1.nextOf().ty == Tvoid || e1.op == EXP.arrayLiteral + && t1.ty == Tsarray && t1.nextOf().ty == Tvoid && t1.isTypeSArray().dim.toInteger() == 0 || isVoidArrayLiteral(e1, t2))) { /* (void* op T[n]) => T[] * (void* op T[]) => T[] @@ -3117,7 +3580,7 @@ Lagain: return coerce(t2.nextOf().arrayOf()); } - if ((t1.ty == Tsarray || t1.ty == Tarray) && (m = t1.implicitConvTo(t2)) != MATCH.nomatch) + if (t1.isStaticOrDynamicArray() && (m = t1.implicitConvTo(t2)) != MATCH.nomatch) { // https://issues.dlang.org/show_bug.cgi?id=7285 // Tsarray op [x, y, ...] should to be Tsarray @@ -3133,7 +3596,7 @@ Lagain: return convert(e1, t2); } - if ((t2.ty == Tsarray || t2.ty == Tarray) && t2.implicitConvTo(t1)) + if (t2.isStaticOrDynamicArray() && t2.implicitConvTo(t1)) { // https://issues.dlang.org/show_bug.cgi?id=7285 // https://issues.dlang.org/show_bug.cgi?id=14737 @@ -3142,7 +3605,8 @@ Lagain: return convert(e2, t1); } - if ((t1.ty == Tsarray || t1.ty == Tarray || t1.ty == Tpointer) && (t2.ty == Tsarray || t2.ty == Tarray || t2.ty == Tpointer) && t1.nextOf().mod != t2.nextOf().mod) + if ((t1.isStaticOrDynamicArray() || t1.ty == Tpointer) && (t2.isStaticOrDynamicArray() || t2.ty == Tpointer) + && t1.nextOf().mod != t2.nextOf().mod) { /* If one is mutable and the other immutable, then retry * with both of them as const @@ -3424,7 +3888,7 @@ LmergeClassTypes: goto Lagain; } - if (t1.isintegral() && t2.isintegral()) + if (t1.isIntegral() && t2.isIntegral()) { if (t1.ty != t2.ty) { @@ -3462,7 +3926,7 @@ LmodCompare: return convert(e1, t2); /// Covers array operations for user-defined types - Type checkArrayOpType(Expression e1, Expression e2, EXP op, Scope *sc) + Type checkArrayOpType(Expression e1, Expression e2, EXP op, Scope* sc) { // scalar op scalar - we shouldn't be here if (e1.type.ty != Tarray && e1.type.ty != Tsarray && e2.type.ty != Tarray && e2.type.ty != Tsarray) @@ -3681,26 +4145,12 @@ Expression typeCombine(BinExp be, Scope* sc) { Expression errorReturn() { - Expression ex = be.incompatibleTypes(); + Expression ex = be.incompatibleTypes(sc); if (ex.op == EXP.error) return ex; return ErrorExp.get(); } - Type t1 = be.e1.type.toBasetype(); - Type t2 = be.e2.type.toBasetype(); - - if (be.op == EXP.min || be.op == EXP.add) - { - // struct+struct, and class+class are errors - if (t1.ty == Tstruct && t2.ty == Tstruct) - return errorReturn(); - else if (t1.ty == Tclass && t2.ty == Tclass) - return errorReturn(); - else if (t1.ty == Taarray && t2.ty == Taarray) - return errorReturn(); - } - if (auto result = typeMerge(sc, be.op, be.e1, be.e2)) { if (be.type is null) @@ -3762,7 +4212,7 @@ Expression integralPromotions(Expression e, Scope* sc) void fix16997(Scope* sc, UnaExp ue) { - if (global.params.fix16997 || sc.flags & SCOPE.Cfile) + if (global.params.fix16997 || sc.inCfile) ue.e1 = integralPromotions(ue.e1, sc); // desired C-like behavor else { @@ -3797,7 +4247,7 @@ extern (D) bool arrayTypeCompatibleWithoutCasting(Type t1, Type t2) t1 = t1.toBasetype(); t2 = t2.toBasetype(); - if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && t2.ty == t1.ty) + if ((t1.isStaticOrDynamicArray() || t1.ty == Tpointer) && t2.ty == t1.ty) { if (t1.nextOf().implicitConvTo(t2.nextOf()) >= MATCH.constant || t2.nextOf().implicitConvTo(t1.nextOf()) >= MATCH.constant) return true; @@ -3944,10 +4394,9 @@ IntRange getIntRange(Expression e) VarDeclaration vd = e.var.isVarDeclaration(); if (vd && vd.range) return vd.range._cast(e.type); - else if (vd && vd._init && !vd.type.isMutable() && (ie = vd.getConstInitializer()) !is null) + if (vd && vd._init && !vd.type.isMutable() && (ie = vd.getConstInitializer()) !is null) return getIntRange(ie); - else - return visit(e); + return visit(e); } IntRange visitComma(CommaExp e) diff --git a/gcc/d/dmd/dclass.d b/gcc/d/dmd/dclass.d index 8bac1f4..80d39fa 100644 --- a/gcc/d/dmd/dclass.d +++ b/gcc/d/dmd/dclass.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/class.html, Classes) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dclass.d, _dclass.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dclass.d, _dclass.d) * Documentation: https://dlang.org/phobos/dmd_dclass.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dclass.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dclass.d */ module dmd.dclass; @@ -23,7 +23,7 @@ import dmd.gluelayer; import dmd.declaration; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, addMember, setFieldOffset; import dmd.errors; import dmd.func; import dmd.id; @@ -33,7 +33,7 @@ import dmd.mtype; import dmd.objc; import dmd.root.rmem; import dmd.target; -import dmd.typesem; +import dmd.typesem : covariant, immutableOf, sarrayOf; import dmd.visitor; /*********************************************************** @@ -198,7 +198,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration Symbol* cpp_type_info_ptr_sym; // cached instance of class Id.cpp_type_info_ptr - final extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) + final extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) { objc = ObjcClassDeclaration(this); @@ -206,6 +206,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration id = Identifier.generateAnonymousId("class"); super(loc, id); + this.dsym = DSYM.classDeclaration; static immutable msg = "only object.d can define this reserved class name"; @@ -375,7 +376,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration .error(loc, fmt, kind, toPrettyChars, arg); } - static ClassDeclaration create(const ref Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) + static ClassDeclaration create(Loc loc, Identifier id, BaseClasses* baseclasses, Dsymbols* members, bool inObject) { return new ClassDeclaration(loc, id, baseclasses, members, inObject); } @@ -489,8 +490,7 @@ extern (C++) class ClassDeclaration : AggregateDeclaration return null; if (cdb.ident.equals(ident)) return cdb; - auto result = cdb.searchBase(ident); - if (result) + if (auto result = cdb.searchBase(ident)) return result; } return null; @@ -614,39 +614,6 @@ extern (C++) class ClassDeclaration : AggregateDeclaration return classKind == ClassKind.d; } - final bool isFuncHidden(FuncDeclaration fd) - { - //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars()); - Dsymbol s = this.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors); - if (!s) - { - //printf("not found\n"); - /* Because, due to a hack, if there are multiple definitions - * of fd.ident, NULL is returned. - */ - return false; - } - s = s.toAlias(); - if (auto os = s.isOverloadSet()) - { - foreach (sm; os.a) - { - auto fm = sm.isFuncDeclaration(); - if (overloadApply(fm, s => fd == s.isFuncDeclaration())) - return false; - } - return true; - } - else - { - auto f = s.isFuncDeclaration(); - //printf("%s fdstart = %p\n", s.kind(), fdstart); - if (overloadApply(f, s => fd == s.isFuncDeclaration())) - return false; - return !fd.parent.isTemplateMixin(); - } - } - /**************** * Find virtual function matching identifier and type. * Used to build virtual function tables for interface implementations. @@ -921,31 +888,11 @@ extern (C++) class ClassDeclaration : AggregateDeclaration // Back end Dsymbol vtblsym; - final Dsymbol vtblSymbol() - { - if (!vtblsym) - { - auto vtype = Type.tvoidptr.immutableOf().sarrayOf(vtbl.length); - auto var = new VarDeclaration(loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_); - var.addMember(null, this); - var.isdataseg = 1; - var._linkage = LINK.d; - var.semanticRun = PASS.semanticdone; // no more semantic wanted - vtblsym = var; - } - return vtblsym; - } - extern (D) final bool isErrorException() { return errorException && (this == errorException || errorException.isBaseOf(this, null)); } - override final inout(ClassDeclaration) isClassDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -956,9 +903,10 @@ extern (C++) class ClassDeclaration : AggregateDeclaration */ extern (C++) final class InterfaceDeclaration : ClassDeclaration { - extern (D) this(const ref Loc loc, Identifier id, BaseClasses* baseclasses) + extern (D) this(Loc loc, Identifier id, BaseClasses* baseclasses) { super(loc, id, baseclasses, null, false); + this.dsym = DSYM.interfaceDeclaration; if (id == Id.IUnknown) // IUnknown is the root of all COM interfaces { com = true; @@ -1058,11 +1006,6 @@ extern (C++) final class InterfaceDeclaration : ClassDeclaration return com; } - override inout(InterfaceDeclaration) isInterfaceDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/declaration.d b/gcc/d/dmd/declaration.d index 3f9769d..4510927 100644 --- a/gcc/d/dmd/declaration.d +++ b/gcc/d/dmd/declaration.d @@ -2,12 +2,12 @@ * Miscellaneous declarations, including typedef, alias, variable declarations including the * implicit this declaration, type tuples, ClassInfo, ModuleInfo and various TypeInfos. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/declaration.d, _declaration.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/declaration.d, _declaration.d) * Documentation: https://dlang.org/phobos/dmd_declaration.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/declaration.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/declaration.d */ module dmd.declaration; @@ -22,200 +22,52 @@ import dmd.delegatize; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, aliasSemantic; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem : overloadApply, getLevelAndCheck; import dmd.globals; import dmd.gluelayer; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; -import dmd.initsem; +import dmd.initsem : initializerToExpression, initializerSemantic; import dmd.intrange; import dmd.location; import dmd.mtype; import dmd.common.outbuffer; import dmd.rootobject; +import dmd.root.filename; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : toDsymbol, typeSemantic, size, hasPointers; import dmd.visitor; version (IN_GCC) {} else version (IN_LLVM) {} else version = MARS; -/************************************ - * Check to see the aggregate type is nested and its context pointer is - * accessible from the current scope. - * Returns true if error occurs. +/****************************************** */ -bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) +void ObjectNotFound(Loc loc, Identifier id) { - Dsymbol sparent = ad.toParentLocal(); - Dsymbol sparent2 = ad.toParent2(); - Dsymbol s = sc.func; - if (ad.isNested() && s) - { - //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); - //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); - //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); - if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) - { - error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); - return true; - } - } - - bool result = false; - for (size_t i = iStart; i < ad.fields.length; i++) + global.gag = 0; // never gag the fatal error + error(loc, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars()); + version (IN_LLVM) { - VarDeclaration vd = ad.fields[i]; - Type tb = vd.type.baseElemOf(); - if (tb.ty == Tstruct) - { - result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); - } + errorSupplemental(loc, "ldc2 might not be correctly installed."); + errorSupplemental(loc, "Please check your ldc2.conf configuration file."); + errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC."); } - return result; -} - -/*********************************************** - * Mark variable v as modified if it is inside a constructor that var - * is a field in. - * Also used to allow immutable globals to be initialized inside a static constructor. - * Returns: - * true if it's an initialization of v - */ -bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) -{ - //printf("modifyFieldVar(var = %s)\n", var.toChars()); - Dsymbol s = sc.func; - while (1) - { - FuncDeclaration fd = null; - if (s) - fd = s.isFuncDeclaration(); - if (fd && - ((fd.isCtorDeclaration() && var.isField()) || - ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) && - fd.toParentDecl() == var.toParent2() && - (!e1 || e1.op == EXP.this_)) - { - bool result = true; - - var.ctorinit = true; - //printf("setting ctorinit\n"); - - if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) - { - assert(e1); - auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || - var.type.needsNested()); - - const dim = sc.ctorflow.fieldinit.length; - auto ad = fd.isMemberDecl(); - assert(ad); - size_t i; - for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? - { - if (ad.fields[i] == var) - break; - } - assert(i < dim); - auto fieldInit = &sc.ctorflow.fieldinit[i]; - const fi = fieldInit.csx; - - if (fi & CSX.this_ctor) - { - if (var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); - .errorSupplemental(fieldInit.loc, "Previous initialization is here."); - } - } - else if (sc.inLoop || (fi & CSX.label)) - { - if (!mustInit && var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); - } - } - - fieldInit.csx |= CSX.this_ctor; - fieldInit.loc = e1.loc; - if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 - { - foreach (j, v; ad.fields) - { - if (v is var || !var.isOverlappedWith(v)) - continue; - v.ctorinit = true; - sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; - } - } - } - else if (fd != sc.func) - { - if (var.type.isMutable()) - result = false; - else if (sc.func.fes) - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", - MODtoChars(var.type.mod), p, var.toChars()); - } - else - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", - MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); - } - } - else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && - var.type.isImmutable()) - { - .error(loc, "%s %s `%s` initialization is not allowed in `static this`", - MODtoChars(var.type.mod), var.kind(), var.toChars()); - errorSupplemental(loc, "Use `shared static this` instead."); - } - else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && - var.type.isConst()) - { - // @@@DEPRECATED_2.116@@@ - // Turn this into an error, merging with the branch above - .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`", - MODtoChars(var.type.mod), var.kind(), var.toChars()); - deprecationSupplemental(loc, "Use `shared static this` instead."); - } - return result; - } - else - { - if (s) - { - s = s.toParentP(var.toParent2()); - continue; - } - } - break; + else version (MARS) + { + errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); + const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found"; + errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr); } - return false; -} - -/****************************************** - */ -void ObjectNotFound(Identifier id) -{ - error(Loc.initial, "`%s` not found. object.d may be incorrectly installed or corrupt.", id.toChars()); fatal(); } @@ -235,29 +87,34 @@ extern (C++) abstract class Declaration : Dsymbol { Type type; Type originalType; // before semantic analysis - StorageClass storage_class = STC.undefined_; + STC storage_class = STC.none; + // overridden symbol with pragma(mangle, "...") + const(char)[] mangleOverride; Visibility visibility; - LINK _linkage = LINK.default_; // may be `LINK.system`; use `resolvedLinkage()` to resolve it short inuse; // used to detect cycles - ubyte adFlags; // control re-assignment of AliasDeclaration (put here for packing reasons) - enum wasRead = 1; // set if AliasDeclaration was read - enum ignoreRead = 2; // ignore any reads of AliasDeclaration - enum nounderscore = 4; // don't prepend _ to mangled name - enum hidden = 8; // don't print this in .di files + private extern (D) static struct BitFields + { + LINK _linkage = LINK.default_; // may be `LINK.system`; use `resolvedLinkage()` to resolve it + bool wasRead; // set if AliasDeclaration was read + bool ignoreRead; // ignore any reads of AliasDeclaration + bool noUnderscore; // don't prepend _ to mangled name + bool hidden; // don't print this in .di files + bool nrvo; /// forward to fd.nrvo_var when generating code + } - // overridden symbol with pragma(mangle, "...") - const(char)[] mangleOverride; + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); - final extern (D) this(Identifier ident) @safe + final extern (D) this(DSYM tag, Identifier ident) @safe { - super(ident); + super(tag, ident); visibility = Visibility(Visibility.Kind.undefined); } - final extern (D) this(const ref Loc loc, Identifier ident) @safe + final extern (D) this(DSYM tag, Loc loc, Identifier ident) @safe { - super(loc, ident); + super(tag, loc, ident); visibility = Visibility(Visibility.Kind.undefined); } @@ -266,7 +123,7 @@ extern (C++) abstract class Declaration : Dsymbol return "declaration"; } - override final uinteger_t size(const ref Loc loc) + override final uinteger_t size(Loc loc) { assert(type); const sz = type.size(); @@ -275,150 +132,6 @@ extern (C++) abstract class Declaration : Dsymbol return sz; } - /** - * Issue an error if an attempt to call a disabled method is made - * - * If the declaration is disabled but inside a disabled function, - * returns `true` but do not issue an error message. - * - * Params: - * loc = Location information of the call - * sc = Scope in which the call occurs - * isAliasedDeclaration = if `true` searches overload set - * - * Returns: - * `true` if this `Declaration` is `@disable`d, `false` otherwise. - */ - extern (D) final bool checkDisabled(Loc loc, Scope* sc, bool isAliasedDeclaration = false) - { - if (!(storage_class & STC.disable)) - return false; - - if (sc.func && sc.func.storage_class & STC.disable) - return true; - - if (auto p = toParent()) - { - if (auto postblit = isPostBlitDeclaration()) - { - /* https://issues.dlang.org/show_bug.cgi?id=21885 - * - * If the generated postblit is disabled, it - * means that one of the fields has a disabled - * postblit. Print the first field that has - * a disabled postblit. - */ - if (postblit.isGenerated()) - { - auto sd = p.isStructDeclaration(); - assert(sd); - for (size_t i = 0; i < sd.fields.length; i++) - { - auto structField = sd.fields[i]; - if (structField.overlapped) - continue; - Type tv = structField.type.baseElemOf(); - if (tv.ty != Tstruct) - continue; - auto sdv = (cast(TypeStruct)tv).sym; - if (!sdv.postblit) - continue; - if (sdv.postblit.isDisabled()) - { - .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars()); - return true; - } - } - } - .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars); - return true; - } - } - - // if the function is @disabled, maybe there - // is an overload in the overload set that isn't - if (isAliasedDeclaration) - { - FuncDeclaration fd = isFuncDeclaration(); - if (fd) - { - for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) - if (!(ovl.storage_class & STC.disable)) - return false; - } - } - - if (auto ctor = isCtorDeclaration()) - { - if (ctor.isCpCtor && ctor.isGenerated()) - { - .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", parent.toPrettyChars()); - return true; - } - } - .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", kind, toPrettyChars); - return true; - } - - /************************************* - * Check to see if declaration can be modified in this context (sc). - * Issue error if not. - * Params: - * loc = location for error messages - * e1 = `null` or `this` expression when this declaration is a field - * sc = context - * flag = if the first bit is set it means do not issue error message for - * invalid modification; if the second bit is set, it means that - this declaration is a field and a subfield of it is modified. - * Returns: - * Modifiable.yes or Modifiable.initialization - */ - extern (D) final Modifiable checkModify(Loc loc, Scope* sc, Expression e1, ModifyFlags flag) - { - VarDeclaration v = isVarDeclaration(); - if (v && v.canassign) - return Modifiable.initialization; - - if (isParameter() || isResult()) - { - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (scx.func == parent && (scx.flags & SCOPE.contract)) - { - const(char)* s = isParameter() && parent.ident != Id.ensure ? "parameter" : "result"; - if (!(flag & ModifyFlags.noError)) - error(loc, "%s `%s` cannot modify %s `%s` in contract", kind, toPrettyChars, s, toChars()); - return Modifiable.initialization; // do not report type related errors - } - } - } - - if (e1 && e1.op == EXP.this_ && isField()) - { - VarDeclaration vthis = e1.isThisExp().var; - for (Scope* scx = sc; scx; scx = scx.enclosing) - { - if (scx.func == vthis.parent && (scx.flags & SCOPE.contract)) - { - if (!(flag & ModifyFlags.noError)) - error(loc, "%s `%s` cannot modify parameter `this` in contract", kind, toPrettyChars); - return Modifiable.initialization; // do not report type related errors - } - } - } - - if (v && (v.isCtorinit() || isField())) - { - // It's only modifiable if inside the right constructor - if ((storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) - return Modifiable.initialization; - if (flag & ModifyFlags.fieldAssign) - return Modifiable.yes; - return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; - } - return Modifiable.yes; - } - final bool isStatic() const pure nothrow @nogc @safe { return (storage_class & STC.static_) != 0; @@ -561,11 +274,6 @@ extern (C++) abstract class Declaration : Dsymbol return visibility; } - override final inout(Declaration) isDeclaration() inout pure nothrow @nogc @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -581,9 +289,9 @@ extern (C++) final class TupleDeclaration : Declaration bool isexp; // true: expression tuple bool building; // it's growing in AliasAssign semantic - extern (D) this(const ref Loc loc, Identifier ident, Objects* objects) @safe + extern (D) this(Loc loc, Identifier ident, Objects* objects) @safe { - super(loc, ident); + super(DSYM.tupleDeclaration, loc, ident); this.objects = objects; } @@ -637,7 +345,7 @@ extern (C++) final class TupleDeclaration : Declaration } else { - auto arg = new Parameter(Loc.initial, 0, t, null, null, null); + auto arg = new Parameter(Loc.initial, STC.none, t, null, null, null); } (*args)[i] = arg; if (!t.deco) @@ -711,11 +419,6 @@ extern (C++) final class TupleDeclaration : Declaration return 0; } - override inout(TupleDeclaration) isTupleDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -732,25 +435,24 @@ extern (C++) final class AliasDeclaration : Declaration Dsymbol overnext; // next in overload list Dsymbol _import; // !=null if unresolved internal alias for selective import - extern (D) this(const ref Loc loc, Identifier ident, Type type) @safe + extern (D) this(Loc loc, Identifier ident, Type type) @safe { - super(loc, ident); - //printf("AliasDeclaration(id = '%s', type = %p)\n", ident.toChars(), type); - //printf("type = '%s'\n", type.toChars()); + super(DSYM.aliasDeclaration, loc, ident); + //debug printf("AliasDeclaration(id = '%s', type = `%s`, %p)\n", ident.toChars(), dmd.hdrgen.toChars(type), type.isTypeIdentifier()); this.type = type; assert(type); } - extern (D) this(const ref Loc loc, Identifier ident, Dsymbol s) @safe + extern (D) this(Loc loc, Identifier ident, Dsymbol s) @safe { - super(loc, ident); - //printf("AliasDeclaration(id = '%s', s = %p)\n", ident.toChars(), s); + super(DSYM.aliasDeclaration, loc, ident); + //debug printf("AliasDeclaration(id = '%s', s = `%s`)\n", ident.toChars(), s.toChars()); assert(s != this); this.aliassym = s; assert(s); } - static AliasDeclaration create(const ref Loc loc, Identifier id, Type type) @safe + static AliasDeclaration create(Loc loc, Identifier id, Type type) @safe { return new AliasDeclaration(loc, id, type); } @@ -905,28 +607,38 @@ extern (C++) final class AliasDeclaration : Declaration override Dsymbol toAlias() { - //printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s', inuse = %d)\n", - // loc.toChars(), toChars(), this, aliassym, aliassym ? aliassym.kind() : "", inuse); + static if (0) + printf("[%s] AliasDeclaration::toAlias('%s', this = %p, aliassym: %s, kind: '%s', inuse = %d)\n", + loc.toChars(), toChars(), this, aliassym ? aliassym.toChars() : "", aliassym ? aliassym.kind() : "", inuse); assert(this != aliassym); //static int count; if (++count == 10) *(char*)0=0; + Dsymbol err() + { + // Avoid breaking "recursive alias" state during errors gagged + if (global.gag) + return this; + aliassym = new AliasDeclaration(loc, ident, Type.terror); + type = Type.terror; + return aliassym; + } // Reading the AliasDeclaration - if (!(adFlags & ignoreRead)) - adFlags |= wasRead; // can never assign to this AliasDeclaration again + if (!this.ignoreRead) + this.wasRead = true; // can never assign to this AliasDeclaration again if (inuse == 1 && type && _scope) { inuse = 2; - uint olderrors = global.errors; + const olderrors = global.errors; Dsymbol s = type.toDsymbol(_scope); //printf("[%s] type = %s, s = %p, this = %p\n", loc.toChars(), type.toChars(), s, this); if (global.errors != olderrors) - goto Lerr; + return err(); if (s) { s = s.toAlias(); if (global.errors != olderrors) - goto Lerr; + return err(); aliassym = s; inuse = 0; } @@ -934,9 +646,9 @@ extern (C++) final class AliasDeclaration : Declaration { Type t = type.typeSemantic(loc, _scope); if (t.ty == Terror) - goto Lerr; + return err(); if (global.errors != olderrors) - goto Lerr; + return err(); //printf("t = %s\n", t.toChars()); inuse = 0; } @@ -944,14 +656,7 @@ extern (C++) final class AliasDeclaration : Declaration if (inuse) { .error(loc, "%s `%s` recursive alias declaration", kind, toPrettyChars); - - Lerr: - // Avoid breaking "recursive alias" state during errors gagged - if (global.gag) - return this; - aliassym = new AliasDeclaration(loc, ident, Type.terror); - type = Type.terror; - return aliassym; + return err(); } if (semanticRun >= PASS.semanticdone) @@ -1017,11 +722,6 @@ extern (C++) final class AliasDeclaration : Declaration aliassym && aliassym.isOverloadable(); } - override inout(AliasDeclaration) isAliasDeclaration() inout - { - return this; - } - /** Returns: `true` if this instance was created to make a template parameter visible in the scope of a template body, `false` otherwise */ extern (D) bool isAliasedTemplateParameter() const @@ -1044,7 +744,7 @@ extern (C++) final class OverDeclaration : Declaration extern (D) this(Identifier ident, Dsymbol s) @safe { - super(ident); + super(DSYM.overDeclaration, ident); this.aliassym = s; } @@ -1102,11 +802,6 @@ extern (C++) final class OverDeclaration : Declaration return result; } - override inout(OverDeclaration) isOverDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1123,7 +818,6 @@ extern (C++) class VarDeclaration : Declaration VarDeclaration lastVar; // Linked list of variables for goto-skips-init detection Expression edtor; // if !=null, does the destruction of the variable IntRange* range; // if !=null, the variable is known to be within the range - VarDeclarations* maybes; // maybeScope variables that are assigned to this maybeScope variable uint endlinnum; // line number of end of scope that this var lives in uint offset; @@ -1173,7 +867,7 @@ extern (C++) class VarDeclaration : Declaration byte canassign; // it can be assigned to ubyte isdataseg; // private data for isDataseg 0 unset, 1 true, 2 false - final extern (D) this(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) + final extern (D) this(Loc loc, Type type, Identifier ident, Initializer _init, STC storage_class = STC.none) in { assert(ident); @@ -1181,7 +875,7 @@ extern (C++) class VarDeclaration : Declaration do { //printf("VarDeclaration('%s')\n", ident.toChars()); - super(loc, ident); + super(DSYM.varDeclaration, loc, ident); debug { if (!type && !_init) @@ -1198,9 +892,9 @@ extern (C++) class VarDeclaration : Declaration this.storage_class = storage_class; } - static VarDeclaration create(const ref Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.undefined_) + static VarDeclaration create(Loc loc, Type type, Identifier ident, Initializer _init, StorageClass storage_class = STC.none) { - return new VarDeclaration(loc, type, ident, _init, storage_class); + return new VarDeclaration(loc, type, ident, _init, cast(STC) storage_class); } override VarDeclaration syntaxCopy(Dsymbol s) @@ -1225,8 +919,7 @@ extern (C++) class VarDeclaration : Declaration */ for (auto s = cast(Dsymbol)this; s; s = s.parent) { - auto ad = (cast(inout)s).isMember(); - if (ad) + if (auto ad = (cast(inout)s).isMember()) return ad; if (!s.parent || !s.parent.isTemplateMixin()) break; @@ -1339,7 +1032,7 @@ extern (C++) class VarDeclaration : Declaration auto vbitoffset = v.offset * 8; // Bitsize of types are overridden by any bit-field widths. - ulong tbitsize = void; + ulong tbitsize; if (auto bf = isBitFieldDeclaration()) { bitoffset += bf.bitOffset; @@ -1348,7 +1041,7 @@ extern (C++) class VarDeclaration : Declaration else tbitsize = tsz * 8; - ulong vbitsize = void; + ulong vbitsize; if (auto vbf = v.isBitFieldDeclaration()) { vbitoffset += vbf.bitOffset; @@ -1370,7 +1063,7 @@ extern (C++) class VarDeclaration : Declaration /************************************* * Return true if we can take the address of this variable. */ - final bool canTakeAddressOf() + final bool canTakeAddressOf() @safe { return !(storage_class & STC.manifest); } @@ -1378,124 +1071,12 @@ extern (C++) class VarDeclaration : Declaration /****************************************** * Return true if variable needs to call the destructor. */ - final bool needsScopeDtor() + final bool needsScopeDtor() @safe { //printf("VarDeclaration::needsScopeDtor() %s %d\n", toChars(), edtor && !(storage_class & STC.nodtor)); return edtor && !(storage_class & STC.nodtor); } - /****************************************** - * If a variable has a scope destructor call, return call for it. - * Otherwise, return NULL. - */ - extern (D) final Expression callScopeDtor(Scope* sc) - { - //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); - - // Destruction of STC.field's is handled by buildDtor() - if (storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) - { - return null; - } - - if (iscatchvar) - return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here - - Expression e = null; - // Destructors for structs and arrays of structs - Type tv = type.baseElemOf(); - if (tv.ty == Tstruct) - { - StructDeclaration sd = (cast(TypeStruct)tv).sym; - if (!sd.dtor || sd.errors) - return null; - - const sz = type.size(); - assert(sz != SIZE_INVALID); - if (!sz) - return null; - - if (type.toBasetype().ty == Tstruct) - { - // v.__xdtor() - e = new VarExp(loc, this); - - /* This is a hack so we can call destructors on const/immutable objects. - * Need to add things like "const ~this()" and "immutable ~this()" to - * fix properly. - */ - e.type = e.type.mutableOf(); - - // Enable calling destructors on shared objects. - // The destructor is always a single, non-overloaded function, - // and must serve both shared and non-shared objects. - e.type = e.type.unSharedOf; - - e = new DotVarExp(loc, e, sd.dtor, false); - e = new CallExp(loc, e); - } - else - { - // __ArrayDtor(v[0 .. n]) - e = new VarExp(loc, this); - - const sdsz = sd.type.size(); - assert(sdsz != SIZE_INVALID && sdsz != 0); - const n = sz / sdsz; - SliceExp se = new SliceExp(loc, e, new IntegerExp(loc, 0, Type.tsize_t), - new IntegerExp(loc, n, Type.tsize_t)); - - // Prevent redundant bounds check - se.upperIsInBounds = true; - se.lowerIsLessThanUpper = true; - - // This is a hack so we can call destructors on const/immutable objects. - se.type = sd.type.arrayOf(); - - e = new CallExp(loc, new IdentifierExp(loc, Id.__ArrayDtor), se); - } - return e; - } - // Destructors for classes - if (storage_class & (STC.auto_ | STC.scope_) && !(storage_class & STC.parameter)) - { - for (ClassDeclaration cd = type.isClassHandle(); cd; cd = cd.baseClass) - { - /* We can do better if there's a way with onstack - * classes to determine if there's no way the monitor - * could be set. - */ - //if (cd.isInterfaceDeclaration()) - // error("interface `%s` cannot be scope", cd.toChars()); - - if (onstack) // if any destructors - { - // delete'ing C++ classes crashes (and delete is deprecated anyway) - if (cd.classKind == ClassKind.cpp) - { - // Don't call non-existant dtor - if (!cd.dtor) - break; - - e = new VarExp(loc, this); - e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances - e = new DotVarExp(loc, e, cd.dtor, false); - e = new CallExp(loc, e); - break; - } - - // delete this; - Expression ec; - ec = new VarExp(loc, this); - e = new DeleteExp(loc, ec, true); - e.type = Type.tvoid; - break; - } - } - } - return e; - } - /******************************************* * If variable has a constant expression initializer, get it. * Otherwise, return null. @@ -1505,7 +1086,7 @@ extern (C++) class VarDeclaration : Declaration assert(type && _init); // Ungag errors when not speculative - uint oldgag = global.gag; + const oldgag = global.gag; if (global.gag) { Dsymbol sym = isMember(); @@ -1528,35 +1109,6 @@ extern (C++) class VarDeclaration : Declaration return e; } - /******************************************* - * Helper function for the expansion of manifest constant. - */ - extern (D) final Expression expandInitializer(Loc loc) - { - assert((storage_class & STC.manifest) && _init); - - auto e = getConstInitializer(); - if (!e) - { - .error(loc, "cannot make expression out of initializer for `%s`", toChars()); - return ErrorExp.get(); - } - - e = e.copy(); - e.loc = loc; // for better error message - return e; - } - - override final void checkCtorConstInit() - { - version (none) - { - /* doesn't work if more than one static ctor */ - if (ctorinit == 0 && isCtorinit() && !isField()) - error("missing initializer in static constructor for const variable"); - } - } - /************************************ * Check to see if this variable is actually in an enclosing function * rather than the current one. @@ -1566,7 +1118,7 @@ extern (C++) class VarDeclaration : Declaration extern (D) final bool checkNestedReference(Scope* sc, Loc loc) { //printf("VarDeclaration::checkNestedReference() %s\n", toChars()); - if (sc.intypeof == 1 || (sc.flags & SCOPE.ctfe)) + if (sc.intypeof == 1 || sc.ctfe) return false; if (!parent || parent == sc.parent) return false; @@ -1601,7 +1153,7 @@ extern (C++) class VarDeclaration : Declaration } // Add this VarDeclaration to fdv.closureVars[] if not already there - if (!sc.intypeof && !(sc.flags & SCOPE.compile) && + if (!sc.intypeof && !sc.traitsCompiles && // https://issues.dlang.org/show_bug.cgi?id=17605 (fdv.skipCodegen || !fdthis.skipCodegen)) { @@ -1645,12 +1197,6 @@ extern (C++) class VarDeclaration : Declaration return s; } - // Eliminate need for dynamic_cast - override final inout(VarDeclaration) isVarDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1667,10 +1213,10 @@ extern (C++) class BitFieldDeclaration : VarDeclaration uint fieldWidth; uint bitOffset; - final extern (D) this(const ref Loc loc, Type type, Identifier ident, Expression width) + final extern (D) this(Loc loc, Type type, Identifier ident, Expression width) { super(loc, type, ident, null); - + this.dsym = DSYM.bitFieldDeclaration; this.width = width; this.storage_class |= STC.field; } @@ -1684,11 +1230,6 @@ extern (C++) class BitFieldDeclaration : VarDeclaration return bf; } - override final inout(BitFieldDeclaration) isBitFieldDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1705,7 +1246,7 @@ extern (C++) class BitFieldDeclaration : VarDeclaration final ulong getMinMax(Identifier id) { const width = fieldWidth; - const uns = type.isunsigned(); + const uns = type.isUnsigned(); const min = id == Id.min; ulong v; assert(width != 0); // should have been rejected in semantic pass @@ -1728,19 +1269,13 @@ extern (C++) final class SymbolDeclaration : Declaration { AggregateDeclaration dsym; - extern (D) this(const ref Loc loc, AggregateDeclaration dsym) @safe + extern (D) this(Loc loc, AggregateDeclaration dsym) @safe { - super(loc, dsym.ident); + super(DSYM.symbolDeclaration, loc, dsym.ident); this.dsym = dsym; storage_class |= STC.const_; } - // Eliminate need for dynamic_cast - override inout(SymbolDeclaration) isSymbolDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1751,7 +1286,7 @@ extern (C++) final class SymbolDeclaration : Declaration */ private Identifier getTypeInfoIdent(Type t) { - import dmd.dmangle; + import dmd.mangle; import core.stdc.stdlib; import dmd.root.rmem; // _init_10TypeInfo_%s @@ -1785,6 +1320,7 @@ extern (C++) class TypeInfoDeclaration : VarDeclaration final extern (D) this(Type tinfo) { super(Loc.initial, Type.dtypeinfo.type, tinfo.getTypeInfoIdent(), null); + this.dsym = DSYM.typeInfoDeclaration; this.tinfo = tinfo; storage_class = STC.static_ | STC.gshared; visibility = Visibility(Visibility.Kind.public_); @@ -1802,21 +1338,6 @@ extern (C++) class TypeInfoDeclaration : VarDeclaration assert(0); // should never be produced by syntax } - override final const(char)* toChars() const - { - //printf("TypeInfoDeclaration::toChars() tinfo = %s\n", tinfo.toChars()); - OutBuffer buf; - buf.writestring("typeid("); - buf.writestring(tinfo.toChars()); - buf.writeByte(')'); - return buf.extractChars(); - } - - override final inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1832,7 +1353,7 @@ extern (C++) final class TypeInfoStructDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfostruct) { - ObjectNotFound(Id.TypeInfo_Struct); + ObjectNotFound(loc, Id.TypeInfo_Struct); } type = Type.typeinfostruct.type; } @@ -1857,7 +1378,7 @@ extern (C++) final class TypeInfoClassDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoclass) { - ObjectNotFound(Id.TypeInfo_Class); + ObjectNotFound(loc, Id.TypeInfo_Class); } type = Type.typeinfoclass.type; } @@ -1882,7 +1403,7 @@ extern (C++) final class TypeInfoInterfaceDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfointerface) { - ObjectNotFound(Id.TypeInfo_Interface); + ObjectNotFound(loc, Id.TypeInfo_Interface); } type = Type.typeinfointerface.type; } @@ -1907,7 +1428,7 @@ extern (C++) final class TypeInfoPointerDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfopointer) { - ObjectNotFound(Id.TypeInfo_Pointer); + ObjectNotFound(loc, Id.TypeInfo_Pointer); } type = Type.typeinfopointer.type; } @@ -1932,7 +1453,7 @@ extern (C++) final class TypeInfoArrayDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoarray) { - ObjectNotFound(Id.TypeInfo_Array); + ObjectNotFound(loc, Id.TypeInfo_Array); } type = Type.typeinfoarray.type; } @@ -1957,7 +1478,7 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfostaticarray) { - ObjectNotFound(Id.TypeInfo_StaticArray); + ObjectNotFound(loc, Id.TypeInfo_StaticArray); } type = Type.typeinfostaticarray.type; } @@ -1977,12 +1498,14 @@ extern (C++) final class TypeInfoStaticArrayDeclaration : TypeInfoDeclaration */ extern (C++) final class TypeInfoAssociativeArrayDeclaration : TypeInfoDeclaration { + Type entry; // type of TypeInfo_AssociativeArray.Entry!(t.index, t.next) + extern (D) this(Type tinfo) { super(tinfo); if (!Type.typeinfoassociativearray) { - ObjectNotFound(Id.TypeInfo_AssociativeArray); + ObjectNotFound(loc, Id.TypeInfo_AssociativeArray); } type = Type.typeinfoassociativearray.type; } @@ -2007,7 +1530,7 @@ extern (C++) final class TypeInfoEnumDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoenum) { - ObjectNotFound(Id.TypeInfo_Enum); + ObjectNotFound(loc, Id.TypeInfo_Enum); } type = Type.typeinfoenum.type; } @@ -2032,7 +1555,7 @@ extern (C++) final class TypeInfoFunctionDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfofunction) { - ObjectNotFound(Id.TypeInfo_Function); + ObjectNotFound(loc, Id.TypeInfo_Function); } type = Type.typeinfofunction.type; } @@ -2057,7 +1580,7 @@ extern (C++) final class TypeInfoDelegateDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfodelegate) { - ObjectNotFound(Id.TypeInfo_Delegate); + ObjectNotFound(loc, Id.TypeInfo_Delegate); } type = Type.typeinfodelegate.type; } @@ -2082,7 +1605,7 @@ extern (C++) final class TypeInfoTupleDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfotypelist) { - ObjectNotFound(Id.TypeInfo_Tuple); + ObjectNotFound(loc, Id.TypeInfo_Tuple); } type = Type.typeinfotypelist.type; } @@ -2107,7 +1630,7 @@ extern (C++) final class TypeInfoConstDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoconst) { - ObjectNotFound(Id.TypeInfo_Const); + ObjectNotFound(loc, Id.TypeInfo_Const); } type = Type.typeinfoconst.type; } @@ -2132,7 +1655,7 @@ extern (C++) final class TypeInfoInvariantDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoinvariant) { - ObjectNotFound(Id.TypeInfo_Invariant); + ObjectNotFound(loc, Id.TypeInfo_Invariant); } type = Type.typeinfoinvariant.type; } @@ -2157,7 +1680,7 @@ extern (C++) final class TypeInfoSharedDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfoshared) { - ObjectNotFound(Id.TypeInfo_Shared); + ObjectNotFound(loc, Id.TypeInfo_Shared); } type = Type.typeinfoshared.type; } @@ -2182,7 +1705,7 @@ extern (C++) final class TypeInfoWildDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfowild) { - ObjectNotFound(Id.TypeInfo_Wild); + ObjectNotFound(loc, Id.TypeInfo_Wild); } type = Type.typeinfowild.type; } @@ -2207,7 +1730,7 @@ extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration super(tinfo); if (!Type.typeinfovector) { - ObjectNotFound(Id.TypeInfo_Vector); + ObjectNotFound(loc, Id.TypeInfo_Vector); } type = Type.typeinfovector.type; } @@ -2228,9 +1751,10 @@ extern (C++) final class TypeInfoVectorDeclaration : TypeInfoDeclaration */ extern (C++) final class ThisDeclaration : VarDeclaration { - extern (D) this(const ref Loc loc, Type t) + extern (D) this(Loc loc, Type t) { super(loc, t, Id.This, null); + this.dsym = DSYM.thisDeclaration; storage_class |= STC.nodtor; } @@ -2239,11 +1763,6 @@ extern (C++) final class ThisDeclaration : VarDeclaration assert(0); // should never be produced by syntax } - override inout(ThisDeclaration) isThisDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/declaration.h b/gcc/d/dmd/declaration.h index 998beba9..c7e4552 100644 --- a/gcc/d/dmd/declaration.h +++ b/gcc/d/dmd/declaration.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -34,6 +34,11 @@ namespace dmd { bool functionSemantic(FuncDeclaration* fd); bool functionSemantic3(FuncDeclaration* fd); + bool checkClosure(FuncDeclaration* fd); + MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names); + PURE isPure(FuncDeclaration *f); + FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0); + FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0); } //enum STC : ulong from astenums.d: @@ -118,14 +123,17 @@ public: Type *type; Type *originalType; // before semantic analysis StorageClass storage_class; + DString mangleOverride; // overridden symbol with pragma(mangle, "...") Visibility visibility; - LINK _linkage; // may be `LINK::system`; use `resolvedLinkage()` to resolve it short inuse; // used to detect cycles - uint8_t adFlags; - DString mangleOverride; // overridden symbol with pragma(mangle, "...") + uint8_t bitFields; + + LINK _linkage() const; + LINK _linkage(LINK v); + bool noUnderscore() const; const char *kind() const override; - uinteger_t size(const Loc &loc) override final; + uinteger_t size(Loc loc) override final; bool isStatic() const { return (storage_class & STCstatic) != 0; } @@ -158,7 +166,6 @@ public: Visibility visible() override final; - Declaration *isDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -178,7 +185,6 @@ public: Dsymbol *toAlias2() override; bool needThis() override; - TupleDeclaration *isTupleDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -191,7 +197,7 @@ public: Dsymbol *overnext; // next in overload list Dsymbol *_import; // !=NULL if unresolved internal alias for selective import - static AliasDeclaration *create(const Loc &loc, Identifier *id, Type *type); + static AliasDeclaration *create(Loc loc, Identifier *id, Type *type); AliasDeclaration *syntaxCopy(Dsymbol *) override; bool overloadInsert(Dsymbol *s) override; const char *kind() const override; @@ -200,7 +206,6 @@ public: Dsymbol *toAlias2() override; bool isOverloadable() const override; - AliasDeclaration *isAliasDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -220,7 +225,6 @@ public: Dsymbol *isUnique(); bool isOverloadable() const override; - OverDeclaration *isOverDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -235,7 +239,6 @@ public: VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection Expression *edtor; // if !=NULL, does the destruction of the variable IntRange *range; // if !NULL, the variable is known to be within the range - VarDeclarations *maybes; // STCmaybescope variables that are assigned to this STCmaybescope variable unsigned endlinnum; // line number of end of scope that this var lives in unsigned offset; @@ -282,7 +285,7 @@ public: #endif bool systemInferred() const; bool systemInferred(bool v); - static VarDeclaration *create(const Loc &loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined); + static VarDeclaration *create(Loc loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined); VarDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; AggregateDeclaration *isThis() override final; @@ -297,10 +300,8 @@ public: bool hasPointers() override final; bool canTakeAddressOf(); bool needsScopeDtor(); - void checkCtorConstInit() override final; Dsymbol *toAlias() override final; // Eliminate need for dynamic_cast - VarDeclaration *isVarDeclaration() override final { return (VarDeclaration *)this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -315,7 +316,6 @@ public: unsigned bitOffset; BitFieldDeclaration *syntaxCopy(Dsymbol *) override; - BitFieldDeclaration *isBitFieldDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -329,7 +329,6 @@ public: AggregateDeclaration *dsym; // Eliminate need for dynamic_cast - SymbolDeclaration *isSymbolDeclaration() override { return (SymbolDeclaration *)this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -340,9 +339,7 @@ public: static TypeInfoDeclaration *create(Type *tinfo); TypeInfoDeclaration *syntaxCopy(Dsymbol *) override final; - const char *toChars() const override final; - TypeInfoDeclaration *isTypeInfoDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -397,6 +394,8 @@ public: class TypeInfoAssociativeArrayDeclaration final : public TypeInfoDeclaration { public: + Type* entry; + static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo); void accept(Visitor *v) override { v->visit(this); } @@ -480,7 +479,6 @@ class ThisDeclaration final : public VarDeclaration { public: ThisDeclaration *syntaxCopy(Dsymbol *) override; - ThisDeclaration *isThisDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -534,7 +532,7 @@ enum class BUILTIN : unsigned char toPrecReal }; -Expression *eval_builtin(const Loc &loc, FuncDeclaration *fd, Expressions *arguments); +Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments); BUILTIN isBuiltin(FuncDeclaration *fd); struct ContractInfo; @@ -588,13 +586,6 @@ public: // Things that should really go into Scope - // 1 if there's a return exp; statement - // 2 if there's a throw statement - // 4 if there's an assert(0) - // 8 if there's inline asm - // 16 if there are multiple return statements - int hasReturnExp; - VarDeclaration *nrvo_var; // variable to replace with shidden Symbol *shidden; // hidden pointer passed to function @@ -637,12 +628,12 @@ public: bool nothrowInprocess(bool v); bool nogcInprocess() const; bool nogcInprocess(bool v); - bool returnInprocess() const; - bool returnInprocess(bool v); + bool saferD() const; + bool saferD(bool v); + bool scopeInprocess() const; + bool scopeInprocess(bool v); bool inlineScanned() const; bool inlineScanned(bool v); - bool inferScope() const; - bool inferScope(bool v); bool hasCatches() const; bool hasCatches(bool v); bool skipCodegen() const; @@ -679,12 +670,18 @@ public: bool dllImport(bool v); bool dllExport() const; bool dllExport(bool v); + bool hasReturnExp() const; + bool hasReturnExp(bool v); + bool hasInlineAsm() const; + bool hasInlineAsm(bool v); + bool hasMultipleReturnExp() const; + bool hasMultipleReturnExp(bool v); // Data for a function declaration that is needed for the Objective-C // integration. ObjcFuncDeclaration objc; - static FuncDeclaration *create(const Loc &loc, const Loc &endloc, Identifier *id, StorageClass storage_class, Type *type, bool noreturn = false); + static FuncDeclaration *create(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type, bool noreturn = false); FuncDeclaration *syntaxCopy(Dsymbol *) override; Statements *frequires(); Ensures *fensures(); @@ -706,8 +703,7 @@ public: bool overloadInsert(Dsymbol *s) override; bool inUnittest(); - static MATCH leastAsSpecialized(FuncDeclaration *f, FuncDeclaration *g, Identifiers *names); - LabelDsymbol *searchLabel(Identifier *ident, const Loc &loc); + LabelDsymbol *searchLabel(Identifier *ident, Loc loc); const char *toPrettyChars(bool QualifyTypes = false) override; const char *toFullSignature(); // for diagnostics, e.g. 'int foo(int x, int y) pure' bool isMain() const; @@ -719,10 +715,8 @@ public: bool isCodeseg() const override final; bool isOverloadable() const override final; bool isAbstract() override final; - PURE isPure(); bool isSafe(); bool isTrusted(); - bool isNogc(); virtual bool isNested() const; AggregateDeclaration *isThis() override; @@ -735,15 +729,9 @@ public: const char *kind() const override; bool isUnique(); bool needsClosure(); - bool checkClosure(); bool hasNestedFrameRefs(); ParameterList getParameterList(); - static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, const char *name, StorageClass stc=0); - static FuncDeclaration *genCfunc(Parameters *args, Type *treturn, Identifier *id, StorageClass stc=0); - - FuncDeclaration *isFuncDeclaration() override final { return this; } - virtual FuncDeclaration *toAliasFunc() { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -754,7 +742,6 @@ public: FuncDeclaration *funcalias; d_bool hasOverloads; - FuncAliasDeclaration *isFuncAliasDeclaration() override { return this; } const char *kind() const override; FuncDeclaration *toAliasFunc() override; @@ -777,7 +764,6 @@ public: bool addPreInvariant() override; bool addPostInvariant() override; - FuncLiteralDeclaration *isFuncLiteralDeclaration() override { return this; } const char *kind() const override; const char *toPrettyChars(bool QualifyTypes = false) override; void accept(Visitor *v) override { v->visit(this); } @@ -787,14 +773,13 @@ class CtorDeclaration final : public FuncDeclaration { public: d_bool isCpCtor; + d_bool isMoveCtor; CtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; - const char *toChars() const override; bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; - CtorDeclaration *isCtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -807,7 +792,6 @@ public: bool addPostInvariant() override; bool overloadInsert(Dsymbol *s) override; - PostBlitDeclaration *isPostBlitDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -816,13 +800,11 @@ class DtorDeclaration final : public FuncDeclaration public: DtorDeclaration *syntaxCopy(Dsymbol *) override; const char *kind() const override; - const char *toChars() const override; bool isVirtual() const override; bool addPreInvariant() override; bool addPostInvariant() override; bool overloadInsert(Dsymbol *s) override; - DtorDeclaration *isDtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -834,9 +816,7 @@ public: bool isVirtual() const override final; bool addPreInvariant() override final; bool addPostInvariant() override final; - bool hasStaticCtorOrDtor() override final; - StaticCtorDeclaration *isStaticCtorDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -846,7 +826,6 @@ public: bool standalone; SharedStaticCtorDeclaration *syntaxCopy(Dsymbol *) override; - SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -858,11 +837,9 @@ public: StaticDtorDeclaration *syntaxCopy(Dsymbol *) override; AggregateDeclaration *isThis() override final; bool isVirtual() const override final; - bool hasStaticCtorOrDtor() override final; bool addPreInvariant() override final; bool addPostInvariant() override final; - StaticDtorDeclaration *isStaticDtorDeclaration() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -871,7 +848,6 @@ class SharedStaticDtorDeclaration final : public StaticDtorDeclaration public: SharedStaticDtorDeclaration *syntaxCopy(Dsymbol *) override; - SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -883,7 +859,6 @@ public: bool addPreInvariant() override; bool addPostInvariant() override; - InvariantDeclaration *isInvariantDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -901,7 +876,6 @@ public: bool addPreInvariant() override; bool addPostInvariant() override; - UnitTestDeclaration *isUnitTestDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -914,6 +888,5 @@ public: bool addPreInvariant() override; bool addPostInvariant() override; - NewDeclaration *isNewDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/delegatize.d b/gcc/d/dmd/delegatize.d index 62800d3..344130b 100644 --- a/gcc/d/dmd/delegatize.d +++ b/gcc/d/dmd/delegatize.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d, _delegatize.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/delegatize.d, _delegatize.d) * Documentation: https://dlang.org/phobos/dmd_delegatize.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/delegatize.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/delegatize.d */ module dmd.delegatize; @@ -25,10 +25,10 @@ import dmd.init; import dmd.initsem; import dmd.location; import dmd.mtype; -import dmd.postordervisitor; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /********************************* @@ -215,15 +215,13 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc) override void visit(SymOffExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.var.isVarDeclaration()) result = v.checkNestedReference(sc, Loc.initial); } override void visit(VarExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.var.isVarDeclaration()) result = v.checkNestedReference(sc, Loc.initial); } @@ -235,8 +233,7 @@ bool lambdaCheckForNestedRef(Expression e, Scope* sc) override void visit(DeclarationExp e) { - VarDeclaration v = e.declaration.isVarDeclaration(); - if (v) + if (VarDeclaration v = e.declaration.isVarDeclaration()) { result = v.checkNestedReference(sc, Loc.initial); if (result) diff --git a/gcc/d/dmd/denum.d b/gcc/d/dmd/denum.d index 5c739ee..e1a4ab6 100644 --- a/gcc/d/dmd/denum.d +++ b/gcc/d/dmd/denum.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/enum.html, Enums) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/denum.d, _denum.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/denum.d, _denum.d) * Documentation: https://dlang.org/phobos/dmd_denum.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/denum.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/denum.d * References: https://dlang.org/spec/enum.html */ @@ -64,12 +64,13 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol Symbol* sinit; - extern (D) this(const ref Loc loc, Identifier ident, Type memtype) + extern (D) this(Loc loc, Identifier ident, Type memtype) { super(loc, ident); //printf("EnumDeclaration() %p %s : %s\n", this, toChars(), memtype.toChars()); type = new TypeEnum(this); this.memtype = memtype; + this.dsym = DSYM.enumDeclaration; visibility = Visibility(Visibility.Kind.undefined); } @@ -81,13 +82,6 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol return ed; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - if (isAnonymous()) - return Dsymbol.oneMembers(members, ps, ident); - return Dsymbol.oneMember(ps, ident); - } - override Type getType() { return type; @@ -120,11 +114,6 @@ extern (C++) final class EnumDeclaration : ScopeDsymbol return isSpecialEnumIdent(ident) && memtype; } - override inout(EnumDeclaration) isEnumDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -154,15 +143,16 @@ extern (C++) final class EnumMember : VarDeclaration EnumDeclaration ed; - extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType) + extern (D) this(Loc loc, Identifier id, Expression value, Type origType) { super(loc, null, id ? id : Id.empty, new ExpInitializer(loc, value)); this.origValue = value; this.origType = origType; + this.dsym = DSYM.enumMember; } extern(D) this(Loc loc, Identifier id, Expression value, Type memtype, - StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) + STC stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) { this(loc, id, value, memtype); storage_class = stc; @@ -187,11 +177,6 @@ extern (C++) final class EnumMember : VarDeclaration return "enum member"; } - override inout(EnumMember) isEnumMember() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/deps.d b/gcc/d/dmd/deps.d new file mode 100644 index 0000000..f586a84 --- /dev/null +++ b/gcc/d/dmd/deps.d @@ -0,0 +1,141 @@ +/** + * Implement the `-deps` and `-makedeps` switches, which output dependencies of modules for build tools. + * + * The grammar of the `-deps` output is: + * --- + * ImportDeclaration + * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " + * ModuleAliasIdentifier ] "\n" + * + * BasicImportDeclaration + * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" + * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" + * + * FilePath + * - any string with '(', ')' and '\' escaped with the '\' character + * --- + * + * Make dependencies as generated by `-makedeps` look like this: + * --- + * source/app.d: + * source/importa.d \ + * source/importb.d + * --- + * + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/deps.d, makedeps.d) + * Documentation: https://dlang.org/phobos/dmd_deps.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/deps.d + */ +module dmd.deps; + +import core.stdc.stdio : printf; +import core.stdc.string : strcmp; +import dmd.common.outbuffer; +import dmd.dimport : Import; +import dmd.dmodule : Module; +import dmd.globals : Param, Output; +import dmd.hdrgen : visibilityToBuffer; +import dmd.id : Id; +import dmd.utils : escapePath; + +/** + * Add an import expression to module dependencies + * Params: + * moduleDeps = output settings for `-deps` + * makeDeps = output settings for `-makedeps` + * fileNameZ = 0-termminated string containing the import expression's resolved filename + * importString = raw string passed to import exp + * imod = module import exp is in + */ +void addImportExpDep(ref Output moduleDeps, ref Output makeDeps, const(char)[] fileNameZ, const(char)[] importString, Module imod) +{ + if (moduleDeps.buffer !is null) + { + OutBuffer* ob = moduleDeps.buffer; + + if (!moduleDeps.name) + ob.writestring("depsFile "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + if (moduleDeps.name) + ob.writestring("string : "); + ob.write(importString); + ob.writestring(" ("); + escapePath(ob, fileNameZ.ptr); + ob.writestring(")"); + ob.writenl(); + } + if (makeDeps.doOutput) + { + makeDeps.files.push(fileNameZ.ptr); + } +} + +/** + * Add an import statement to module dependencies + * Params: + * moduleDeps = output settings + * imp = import to add + * imod = module that the import is in + */ +void addImportDep(ref Output moduleDeps, Import imp, Module imod) +{ + // object self-imports itself, so skip that + // https://issues.dlang.org/show_bug.cgi?id=7547 + // don't list pseudo modules __entrypoint.d, __main.d + // https://issues.dlang.org/show_bug.cgi?id=11117 + // https://issues.dlang.org/show_bug.cgi?id=11164 + if (moduleDeps.buffer is null || (imp.id == Id.object && imod.ident == Id.object) || + strcmp(imod.ident.toChars(), "__main") == 0) + return; + + OutBuffer* ob = moduleDeps.buffer; + if (!moduleDeps.name) + ob.writestring("depsImport "); + ob.writestring(imod.toPrettyChars()); + ob.writestring(" ("); + escapePath(ob, imod.srcfile.toChars()); + ob.writestring(") : "); + // use visibility instead of sc.visibility because it couldn't be + // resolved yet, see the comment above + visibilityToBuffer(*ob, imp.visibility); + ob.writeByte(' '); + if (imp.isstatic) + { + ob.writestring("static "); + } + ob.writestring(": "); + foreach (pid; imp.packages) + { + ob.printf("%s.", pid.toChars()); + } + ob.writestring(imp.id.toString()); + ob.writestring(" ("); + if (imp.mod) + escapePath(ob, imp.mod.srcfile.toChars()); + else + ob.writestring("???"); + ob.writeByte(')'); + foreach (i, name; imp.names) + { + if (i == 0) + ob.writeByte(':'); + else + ob.writeByte(','); + auto _alias = imp.aliases[i]; + if (!_alias) + { + ob.printf("%s", name.toChars()); + _alias = name; + } + else + ob.printf("%s=%s", _alias.toChars(), name.toChars()); + } + if (imp.aliasId) + ob.printf(" -> %s", imp.aliasId.toChars()); + ob.writenl(); +} diff --git a/gcc/d/dmd/dimport.d b/gcc/d/dmd/dimport.d index 2efdd31..bbd74a4 100644 --- a/gcc/d/dmd/dimport.d +++ b/gcc/d/dmd/dimport.d @@ -1,12 +1,12 @@ /** * A `Dsymbol` representing a renamed import. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d, _dimport.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dimport.d, _dimport.d) * Documentation: https://dlang.org/phobos/dmd_dimport.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dimport.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dimport.d */ module dmd.dimport; @@ -14,7 +14,6 @@ module dmd.dimport; import dmd.arraytypes; import dmd.dmodule; import dmd.dsymbol; -import dmd.errors; import dmd.identifier; import dmd.location; import dmd.visitor; @@ -41,7 +40,7 @@ extern (C++) final class Import : Dsymbol // corresponding AliasDeclarations for alias=name pairs AliasDeclarations aliasdecls; - extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic) + extern (D) this(Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic) { Identifier selectIdent() { @@ -51,19 +50,16 @@ extern (C++) final class Import : Dsymbol // import [aliasId] = std.stdio; return aliasId; } - else if (packages.length > 0) + if (packages.length > 0) { // import [std].stdio; return packages[0]; } - else - { - // import [id]; - return id; - } + // import [id]; + return id; } - super(loc, selectIdent()); + super(DSYM.import_, loc, selectIdent()); assert(id); version (none) @@ -84,16 +80,6 @@ extern (C++) final class Import : Dsymbol this.visibility = Visibility.Kind.private_; // default to private } - extern (D) void addAlias(Identifier name, Identifier _alias) - { - if (isstatic) - .error(loc, "%s `%s` cannot have an import bind list", kind, toPrettyChars); - if (!aliasId) - this.ident = null; // make it an anonymous import - names.push(name); - aliases.push(_alias); - } - override const(char)* kind() const { return isstatic ? "static import" : "import"; @@ -110,9 +96,13 @@ extern (C++) final class Import : Dsymbol assert(!s); auto si = new Import(loc, packages, id, aliasId, isstatic); si.comment = comment; + assert(!(isstatic && names.length)); + if (names.length && !si.aliasId) + si.ident = null; for (size_t i = 0; i < names.length; i++) { - si.addAlias(names[i], aliases[i]); + si.names.push(names[i]); + si.aliases.push(aliases[i]); } return si; } @@ -165,16 +155,10 @@ extern (C++) final class Import : Dsymbol * https://issues.dlang.org/show_bug.cgi?id=5412 */ assert(ident && ident == s.ident); - Import imp; - if (!aliasId && (imp = s.isImport()) !is null && !imp.aliasId) - return true; - else + if (aliasId) return false; - } - - override inout(Import) isImport() inout - { - return this; + const imp = s.isImport(); + return imp && !imp.aliasId; } override void accept(Visitor v) diff --git a/gcc/d/dmd/dinterpret.d b/gcc/d/dmd/dinterpret.d index 5493fc1..13051db 100644 --- a/gcc/d/dmd/dinterpret.d +++ b/gcc/d/dmd/dinterpret.d @@ -3,12 +3,12 @@ * * Specification: ($LINK2 https://dlang.org/spec/function.html#interpretation, Compile Time Function Execution (CTFE)) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d, _dinterpret.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dinterpret.d, _dinterpret.d) * Documentation: https://dlang.org/phobos/dmd_dinterpret.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dinterpret.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dinterpret.d */ module dmd.dinterpret; @@ -50,7 +50,7 @@ import dmd.rootobject; import dmd.root.utf; import dmd.statement; import dmd.tokens; -import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf; +import dmd.typesem : mutableOf, equivalent, pointerTo, sarrayOf, arrayOf, size; import dmd.utils : arrayCastBigEndian; import dmd.visitor; @@ -100,6 +100,10 @@ public Expression ctfeInterpret(Expression e) auto rgnpos = ctfeGlobals.region.savePos(); + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.ctfe); + scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfe, e); + Expression result = interpret(e, null); // Report an error if the expression contained a `ThrowException` and @@ -432,6 +436,27 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd.loc.toChars(), istate, fd.toChars()); } + scope dlg = () { + import dmd.common.outbuffer; + auto strbuf = OutBuffer(20); + strbuf.writestring(fd.toPrettyChars()); + strbuf.write("("); + if (arguments) + { + foreach (i, arg; *arguments) + { + if (i > 0) + strbuf.write(", "); + strbuf.writestring(arg.toChars()); + } + } + strbuf.write(")"); + return strbuf.extractSlice(); + }; + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.ctfeCall); + scope (exit) timeTraceEndEvent(TimeTraceEventType.ctfeCall, fd, dlg); + void fdError(const(char)* msg) { error(fd.loc, "%s `%s` %s", fd.kind, fd.toPrettyChars, msg); @@ -695,9 +720,9 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta } } - if (tf.isref && e.op == EXP.variable && e.isVarExp().var == fd.vthis) + if (tf.isRef && e.op == EXP.variable && e.isVarExp().var == fd.vthis) e = thisarg; - if (tf.isref && fd.hasDualContext() && e.op == EXP.index) + if (tf.isRef && fd.hasDualContext() && e.op == EXP.index) { auto ie = e.isIndexExp(); auto pe = ie.e1.isPtrExp(); @@ -733,7 +758,7 @@ private Expression interpretFunction(UnionExp* pue, FuncDeclaration fd, InterSta } /// used to collect coverage information in ctfe -void incUsageCtfe(InterState* istate, const ref Loc loc) +void incUsageCtfe(InterState* istate, Loc loc) { if (global.params.ctfe_cov && istate) { @@ -974,7 +999,7 @@ Expression interpretStatement(UnionExp* pue, Statement s, InterState* istate) /* If the function returns a ref AND it's been called from an assignment, * we need to return an lvalue. Otherwise, just do an (rvalue) interpret. */ - if (tf.isref) + if (tf.isRef) { result = interpret(pue, s.exp, istate, CTFEGoal.LValue); return; @@ -1860,7 +1885,7 @@ public: // Check for taking an address of a shared variable. // If the shared variable is an array, the offset might not be zero. Type fromType = null; - if (e.var.type.ty == Tarray || e.var.type.ty == Tsarray) + if (e.var.type.isStaticOrDynamicArray()) { fromType = (cast(TypeArray)e.var.type).next; } @@ -1875,7 +1900,7 @@ public: Expression val = getVarExp(e.loc, istate, e.var, goal); if (exceptionOrCant(val)) return; - if (val.type.ty == Tarray || val.type.ty == Tsarray) + if (val.type.isStaticOrDynamicArray()) { // Check for unsupported type painting operations Type elemtype = (cast(TypeArray)val.type).next; @@ -1964,13 +1989,15 @@ public: Declaration decl = ve.var; // We cannot take the address of an imported symbol at compile time - if (decl.isImportedSymbol()) { + if (decl.isImportedSymbol()) + { error(e.loc, "cannot take address of imported symbol `%s` at compile time", decl.toChars()); result = CTFEExp.cantexp; return; } - if (decl.isDataseg()) { + if (decl.isDataseg()) + { // Normally this is already done by optimize() // Do it here in case optimize(WANTvalue) wasn't run before CTFE emplaceExp!(SymOffExp)(pue, e.loc, e.e1.isVarExp().var, 0); @@ -2027,7 +2054,7 @@ public: } } - static Expression getVarExp(const ref Loc loc, InterState* istate, Declaration d, CTFEGoal goal) + static Expression getVarExp(Loc loc, InterState* istate, Declaration d, CTFEGoal goal) { Expression e = CTFEExp.cantexp; if (VarDeclaration v = d.isVarDeclaration()) @@ -2727,7 +2754,7 @@ public: // Create an array literal of type 'newtype' with dimensions given by // 'arguments'[argnum..$] - static Expression recursivelyCreateArrayLiteral(UnionExp* pue, const ref Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum) + static Expression recursivelyCreateArrayLiteral(UnionExp* pue, Loc loc, Type newtype, InterState* istate, Expressions* arguments, int argnum) { Expression lenExpr = interpret(pue, (*arguments)[argnum], istate); if (exceptionOrCantInterpret(lenExpr)) @@ -2769,6 +2796,13 @@ public: printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } + if (e.placement) + { + error(e.placement.loc, "`new ( %s )` PlacementExpression cannot be evaluated at compile time", e.placement.toChars()); + result = CTFEExp.cantexp; + return; + } + Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing); if (exceptionOrCant(epre)) return; @@ -2903,7 +2937,7 @@ public: result = eref; return; } - if (e.newtype.toBasetype().isscalar()) + if (e.newtype.toBasetype().isScalar()) { Expression newval; if (e.arguments && e.arguments.length) @@ -2980,8 +3014,8 @@ public: } } - private alias fp_t = extern (D) UnionExp function(const ref Loc loc, Type, Expression, Expression); - private alias fp2_t = extern (D) bool function(const ref Loc loc, EXP, Expression, Expression); + private alias fp_t = extern (D) UnionExp function(Loc loc, Type, Expression, Expression); + private alias fp2_t = extern (D) bool function(Loc loc, EXP, Expression, Expression); extern (D) private void interpretCommon(BinExp e, fp_t fp) { @@ -3002,7 +3036,7 @@ public: result = pointerDifference(pue, e.loc, e.type, e1, e2); return; } - if (e.e1.type.ty == Tpointer && e.e2.type.isintegral()) + if (e.e1.type.ty == Tpointer && e.e2.type.isIntegral()) { UnionExp ue1 = void; Expression e1 = interpret(&ue1, e.e1, istate); @@ -3015,7 +3049,7 @@ public: result = pointerArithmetic(pue, e.loc, e.op, e.type, e1, e2); return; } - if (e.e2.type.ty == Tpointer && e.e1.type.isintegral() && e.op == EXP.add) + if (e.e2.type.ty == Tpointer && e.e1.type.isIntegral() && e.op == EXP.add) { UnionExp ue1 = void; Expression e1 = interpret(&ue1, e.e1, istate); @@ -3329,7 +3363,7 @@ public: // a[] = e can have const e. So we compare the naked types. Type tdst = e1.type.toBasetype(); Type tsrc = e.e2.type.toBasetype(); - while (tdst.ty == Tsarray || tdst.ty == Tarray) + while (tdst.isStaticOrDynamicArray()) { tdst = (cast(TypeArray)tdst).next.toBasetype(); if (tsrc.equivalent(tdst)) @@ -3611,7 +3645,7 @@ public: newval = (*fp)(e.loc, e.type, oldval, newval).copy(); } - else if (e.e2.type.isintegral() && + else if (e.e2.type.isIntegral() && (e.op == EXP.addAssign || e.op == EXP.minAssign || e.op == EXP.plusPlus || @@ -3844,7 +3878,7 @@ public: if (auto bf = v.isBitFieldDeclaration()) { sinteger_t value = ival.toInteger(); - if (bf.type.isunsigned()) + if (bf.type.isUnsigned()) value &= (1L << bf.fieldWidth) - 1; // zero extra bits else { // sign extend extra bits @@ -4277,7 +4311,7 @@ public: Expression assignTo(ArrayLiteralExp ae, size_t lwr, size_t upr) { Expressions* w = ae.elements; - assert(ae.type.ty == Tsarray || ae.type.ty == Tarray || ae.type.ty == Tpointer); + assert(ae.type.isStaticOrDynamicArray() || ae.type.ty == Tpointer); bool directblk = (cast(TypeNext)ae.type).next.equivalent(newval.type); for (size_t k = lwr; k < upr; k++) { @@ -4830,33 +4864,6 @@ public: return; } - else if (fd.ident == Id._d_arrayappendT || fd.ident == Id._d_arrayappendTTrace) - { - // In expressionsem.d `ea ~= eb` was lowered to `_d_arrayappendT{,Trace}({file, line, funcname}, ea, eb);`. - // The following code will rewrite it back to `ea ~= eb` and then interpret that expression. - Expression lhs, rhs; - - if (fd.ident == Id._d_arrayappendT) - { - assert(e.arguments.length == 2); - lhs = (*e.arguments)[0]; - rhs = (*e.arguments)[1]; - } - else - { - assert(e.arguments.length == 5); - lhs = (*e.arguments)[3]; - rhs = (*e.arguments)[4]; - } - - auto cae = new CatAssignExp(e.loc, lhs, rhs); - cae.type = e.type; - - result = interpretRegion(cae, istate, CTFEGoal.LValue); - return; - } - else if (fd.ident == Id._d_arrayappendcTX) - assert(0, "CTFE cannot interpret _d_arrayappendcTX!"); } else if (auto soe = ecall.isSymOffExp()) { @@ -5042,7 +5049,7 @@ public: auto ce = e.e2.isCallExp(); assert(ce); - auto ne = new NewExp(e.loc, null, e.type, ce.arguments); + auto ne = new NewExp(e.loc, null, null, e.type, ce.arguments); ne.type = e.e1.type; result = interpret(ne, istate); @@ -5806,8 +5813,7 @@ public: auto expTb = exp.type.toBasetype(); if (exp.type.implicitConvTo(tbNext) >= MATCH.convert && - (tb.ty == Tarray || tb.ty == Tsarray) && - (expTb.ty == Tarray || expTb.ty == Tsarray)) + tb.isStaticOrDynamicArray() && expTb.isStaticOrDynamicArray()) return new ArrayLiteralExp(exp.loc, e.type, exp); return exp; } @@ -5923,7 +5929,7 @@ public: bool castToSarrayPointer = false; bool castBackFromVoid = false; - if (e1.type.ty == Tarray || e1.type.ty == Tsarray || e1.type.ty == Tpointer) + if (e1.type.isStaticOrDynamicArray() || e1.type.ty == Tpointer) { // Check for unsupported type painting operations // For slices, we need the type being sliced, @@ -6103,11 +6109,11 @@ public: // Disallow array type painting, except for conversions between built-in // types of identical size. - if ((e.to.ty == Tsarray || e.to.ty == Tarray) && (e1.type.ty == Tsarray || e1.type.ty == Tarray) && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf())) + if (e.to.isStaticOrDynamicArray() && e1.type.isStaticOrDynamicArray() && !isSafePointerCast(e1.type.nextOf(), e.to.nextOf())) { auto se = e1.isStringExp(); // Allow casting a hex string literal to short[], int[] or long[] - if (se && se.hexString && se.postfix == StringExp.NoPostfix) + if (se && se.hexString && se.postfix == StringExp.NoPostfix && e.to.nextOf().isIntegral) { const sz = cast(size_t) e.to.nextOf().size; if ((se.len % sz) != 0) @@ -6126,8 +6132,7 @@ public: } error(e.loc, "array cast from `%s` to `%s` is not supported at compile time", e1.type.toChars(), e.to.toChars()); if (se && se.hexString && se.postfix != StringExp.NoPostfix) - errorSupplemental(e.loc, "perhaps remove postfix `%s` from hex string", - (cast(char) se.postfix ~ "\0").ptr); + errorSupplemental(e.loc, "perhaps remove postfix `%.*s` from hex string", 1, &se.postfix); result = CTFEExp.cantexp; return; @@ -6144,9 +6149,9 @@ public: } else if (tobt.isTypeBasic() && e1.op == EXP.null_) { - if (tobt.isintegral()) + if (tobt.isIntegral()) emplaceExp!(IntegerExp)(pue, e.loc, 0, e.to); - else if (tobt.isreal()) + else if (tobt.isReal()) emplaceExp!(RealExp)(pue, e.loc, CTFloat.zero, e.to); result = pue.exp(); return; @@ -6534,7 +6539,7 @@ public: /// Interpret `throw <exp>` found at the specified location `loc` private -void interpretThrow(ref Expression result, Expression exp, const ref Loc loc, InterState* istate) +void interpretThrow(ref Expression result, Expression exp, Loc loc, InterState* istate) { incUsageCtfe(istate, loc); @@ -6675,7 +6680,7 @@ Expressions* copyArrayOnWrite(Expressions* exps, Expressions* original) true if it is safe to return, false if an error was generated. */ private -bool stopPointersEscaping(const ref Loc loc, Expression e) +bool stopPointersEscaping(Loc loc, Expression e) { import dmd.typesem : hasPointers; if (!e.type.hasPointers()) @@ -6725,7 +6730,7 @@ bool stopPointersEscaping(const ref Loc loc, Expression e) // Check all elements of an array for escaping local variables. Return false if error private -bool stopPointersEscapingFromArray(const ref Loc loc, Expressions* elems) +bool stopPointersEscapingFromArray(Loc loc, Expressions* elems) { foreach (e; *elems) { @@ -6789,7 +6794,7 @@ ThrownExceptionExp chainExceptions(ThrownExceptionExp oldest, ThrownExceptionExp * 1. all slices must be resolved. * 2. all .ownedByCtfe set to OwnedBy.code */ -private Expression scrubReturnValue(const ref Loc loc, Expression e) +private Expression scrubReturnValue(Loc loc, Expression e) { /* Returns: true if e is void, * or is an array literal or struct literal of void elements. @@ -6855,10 +6860,10 @@ private Expression scrubReturnValue(const ref Loc loc, Expression e) Expression scrubSE(StructLiteralExp sle) { sle.ownedByCtfe = OwnedBy.code; - if (!(sle.stageflags & stageScrub)) + if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion if (auto ex = scrubArray(sle.elements, true)) return ex; sle.stageflags = old; @@ -6935,10 +6940,10 @@ private Expression scrubCacheValue(Expression e) Expression scrubSE(StructLiteralExp sle) { sle.ownedByCtfe = OwnedBy.cache; - if (!(sle.stageflags & stageScrub)) + if (!(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion if (auto ex = scrubArrayCache(sle.elements)) return ex; sle.stageflags = old; @@ -7012,10 +7017,10 @@ private Expression copyRegionExp(Expression e) static void copySE(StructLiteralExp sle) { - if (1 || !(sle.stageflags & stageScrub)) + if (1 || !(sle.stageflags & StructLiteralExp.StageFlags.scrub)) { const old = sle.stageflags; - sle.stageflags |= stageScrub; // prevent infinite recursion + sle.stageflags |= StructLiteralExp.StageFlags.scrub; // prevent infinite recursion copyArray(sle.elements); sle.stageflags = old; } @@ -7509,7 +7514,7 @@ private Expression foreachApplyUtf(UnionExp* pue, InterState* istate, Expression /* If this is a built-in function, return the interpreted result, * Otherwise, return NULL. */ -private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, const ref Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis) +private Expression evaluateIfBuiltin(UnionExp* pue, InterState* istate, Loc loc, FuncDeclaration fd, Expressions* arguments, Expression pthis) { Expression e = null; size_t nargs = arguments ? arguments.length : 0; diff --git a/gcc/d/dmd/dmacro.d b/gcc/d/dmd/dmacro.d index c04fbec..307b43f 100644 --- a/gcc/d/dmd/dmacro.d +++ b/gcc/d/dmd/dmacro.d @@ -1,12 +1,12 @@ /** * Text macro processor for Ddoc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmacro.d, _dmacro.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmacro.d, _dmacro.d) * Documentation: https://dlang.org/phobos/dmd_dmacro.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmacro.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dmacro.d */ module dmd.dmacro; @@ -16,8 +16,6 @@ import core.stdc.string; import dmd.common.outbuffer; import dmd.root.rmem; -@trusted: - struct MacroTable { /********************************** @@ -303,7 +301,7 @@ struct Macro * copy allocated with mem.xmalloc() */ -char[] memdup(const(char)[] p) nothrow pure +char[] memdup(const(char)[] p) nothrow pure @trusted { size_t len = p.length; return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len]; @@ -318,7 +316,7 @@ char[] memdup(const(char)[] p) nothrow pure * 1..9: get nth argument * -1: get 2nd through end */ -size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure +size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothrow pure @safe { /* Scan forward for matching right parenthesis. * Nest parentheses. @@ -334,7 +332,7 @@ size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n) @nogc nothr uint inexp = 0; uint argn = 0; size_t v = 0; - const p = buf.ptr; + const p = buf; const end = buf.length; Largstart: // Skip first space, if any, to find the start of the macro argument diff --git a/gcc/d/dmd/dmodule.d b/gcc/d/dmd/dmodule.d index 58bf3fd..0e0070a 100644 --- a/gcc/d/dmd/dmodule.d +++ b/gcc/d/dmd/dmodule.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/module.html, Modules) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d, _dmodule.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dmodule.d, _dmodule.d) * Documentation: https://dlang.org/phobos/dmd_dmodule.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmodule.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dmodule.d */ module dmd.dmodule; @@ -24,12 +24,13 @@ import dmd.astenums; import dmd.common.outbuffer; import dmd.compiler; import dmd.cparse; +import dmd.declaration; import dmd.dimport; import dmd.dmacro; import dmd.doc; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, importAll, load, include; import dmd.errors; import dmd.errorsink; import dmd.expression; @@ -57,12 +58,10 @@ import dmd.visitor; version (Windows) { - import core.sys.windows.winbase : getpid = GetCurrentProcessId; enum PathSeparator = '\\'; } else version (Posix) { - import core.sys.posix.unistd : getpid; enum PathSeparator = '/'; } else @@ -174,11 +173,12 @@ extern (C++) class Package : ScopeDsymbol uint tag; // auto incremented tag, used to mask package tree in scopes Module mod; // !=null if isPkgMod == PKG.module_ - final extern (D) this(const ref Loc loc, Identifier ident) nothrow + final extern (D) this(Loc loc, Identifier ident) nothrow { super(loc, ident); __gshared uint packageTag; this.tag = packageTag++; + this.dsym = DSYM.package_; } override const(char)* kind() const nothrow @@ -252,11 +252,6 @@ extern (C++) class Package : ScopeDsymbol return dst; } - override final inout(Package) isPackage() inout - { - return this; - } - /** * Checks if pkg is a sub-package of this * @@ -309,8 +304,9 @@ extern (C++) class Package : ScopeDsymbol packages ~= s.ident; reverse(packages); - if (Module.find(getFilename(packages, ident))) - Module.load(Loc.initial, packages, this.ident); + ImportPathInfo pathThatFoundThis; + if (Module.find(getFilename(packages, ident), pathThatFoundThis)) + Module.load(Loc.initial, packages, this.ident, pathThatFoundThis); else isPkgMod = PKG.package_; } @@ -348,8 +344,8 @@ extern (C++) final class Module : Package const(char)[] arg; // original argument name ModuleDeclaration* md; // if !=null, the contents of the ModuleDeclaration declaration const FileName srcfile; // input source file - const FileName objfile; // output .obj file - const FileName hdrfile; // 'header' file + FileName objfile; // output .obj file + FileName hdrfile; // 'header' file FileName docfile; // output documentation file const(ubyte)[] src; /// Raw content of the file uint errors; // if any errors in file @@ -357,6 +353,7 @@ extern (C++) final class Module : Package FileType filetype; // source file type bool hasAlwaysInlines; // contains references to functions that must be inlined bool isPackageFile; // if it is a package.d + Edition edition; // language edition that this module is compiled with Package pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; @@ -427,11 +424,9 @@ extern (C++) final class Module : Package Modules aimports; // all imported modules - uint debuglevel; // debug level Identifiers* debugids; // debug identifiers Identifiers* debugidsNot; // forward referenced debug identifiers - uint versionlevel; // version level Identifiers* versionids; // version identifiers Identifiers* versionidsNot; // forward referenced version identifiers @@ -441,9 +436,10 @@ extern (C++) final class Module : Package size_t nameoffset; // offset of module name from start of ModuleInfo size_t namelen; // length of module name in characters - extern (D) this(const ref Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) + extern (D) this(Loc loc, const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) { super(loc, ident); + this.dsym = DSYM.module_; const(char)[] srcfilename; //printf("Module::Module(filename = '%.*s', ident = '%s')\n", cast(int)filename.length, filename.ptr, ident.toChars()); this.arg = filename; @@ -454,7 +450,7 @@ extern (C++) final class Module : Package FileName.exists(filename) == 1) { FileName.free(srcfilename.ptr); - srcfilename = FileName.removeExt(filename); // just does a mem.strdup(filename) + srcfilename = FileName.sansExt(filename); } else if (!FileName.equalsExt(srcfilename, mars_ext) && !FileName.equalsExt(srcfilename, hdr_ext) && @@ -476,6 +472,8 @@ extern (C++) final class Module : Package setDocfile(); if (doHdrGen) hdrfile = setOutfilename(global.params.dihdr.name, global.params.dihdr.dir, arg, hdr_ext); + + this.edition = Edition.legacy; } extern (D) this(const(char)[] filename, Identifier ident, int doDocComment, int doHdrGen) @@ -495,20 +493,26 @@ extern (C++) final class Module : Package static const(char)* find(const(char)* filename) { - return find(filename.toDString).ptr; + ImportPathInfo pathThatFoundThis; // is this needed? In fact is this function needed still??? + return find(filename.toDString, pathThatFoundThis).ptr; } - extern (D) static const(char)[] find(const(char)[] filename) + extern (D) static const(char)[] find(const(char)[] filename, out ImportPathInfo pathThatFoundThis) { - return global.fileManager.lookForSourceFile(filename, global.path[]); + ptrdiff_t whichPathFoundThis; + const(char)[] ret = global.fileManager.lookForSourceFile(filename, global.path[], whichPathFoundThis); + + if (whichPathFoundThis >= 0) + pathThatFoundThis = global.path[whichPathFoundThis]; + return ret; } - extern (C++) static Module load(const ref Loc loc, Identifiers* packages, Identifier ident) + extern (C++) static Module load(Loc loc, Identifiers* packages, Identifier ident) { return load(loc, packages ? (*packages)[] : null, ident); } - extern (D) static Module load(const ref Loc loc, Identifier[] packages, Identifier ident) + extern (D) static Module load(Loc loc, Identifier[] packages, Identifier ident, ImportPathInfo pathInfo = ImportPathInfo.init) { //printf("Module::load(ident = '%s')\n", ident.toChars()); // Build module filename by turning: @@ -517,11 +521,17 @@ extern (C++) final class Module : Package // foo\bar\baz const(char)[] filename = getFilename(packages, ident); // Look for the source file - if (const result = find(filename)) + ImportPathInfo importPathThatFindUs; + if (const result = find(filename, importPathThatFindUs)) + { filename = result; // leaks + pathInfo = importPathThatFindUs; + } auto m = new Module(loc, filename, ident, 0, 0); + // TODO: apply import path information (pathInfo) on to module + if (!m.read(loc)) return null; if (global.params.v.verbose) @@ -565,16 +575,26 @@ extern (C++) final class Module : Package else { const(char)[] argdoc; - OutBuffer buf; - if (arg == "__stdin.d") - { - buf.printf("__stdin_%d.d", getpid()); - arg = buf[]; - } if (global.params.preservePaths) argdoc = arg; else argdoc = FileName.name(arg); + + if (global.params.fullyQualifiedObjectFiles) + { + const fqn = md ? md.toString() : toString(); + argdoc = FileName.replaceName(argdoc, fqn); + + // add ext, otherwise forceExt will make nested.module into nested.<ext> + const bufferLength = argdoc.length + 1 + ext.length + /* null terminator */ 1; + char[] s = new char[bufferLength]; + s[0 .. argdoc.length] = argdoc[]; + s[argdoc.length] = '.'; + s[$-1-ext.length .. $-1] = ext[]; + s[$-1] = 0; + argdoc = s; + } + // If argdoc doesn't have an absolute path, make it relative to dir if (!FileName.absolute(argdoc)) { @@ -607,43 +627,32 @@ extern (C++) final class Module : Package * Params: * loc = The location at which the file read originated (e.g. import) */ - private void onFileReadError(const ref Loc loc) + private void onFileReadError(Loc loc) { - if (FileName.equals(srcfile.toString(), "object.d")) + const name = srcfile.toString(); + if (FileName.equals(name, "object.d")) { - .error(loc, "cannot find source code for runtime library file 'object.d'"); - version (IN_LLVM) - { - errorSupplemental(loc, "ldc2 might not be correctly installed."); - errorSupplemental(loc, "Please check your ldc2.conf configuration file."); - errorSupplemental(loc, "Installation instructions can be found at http://wiki.dlang.org/LDC."); - } - version (MARS) - { - errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); - const dmdConfFile = global.inifilename.length ? FileName.canonicalName(global.inifilename) : "not found"; - errorSupplemental(loc, "config file: %.*s", cast(int)dmdConfFile.length, dmdConfFile.ptr); - } + ObjectNotFound(Loc.initial, ident); } else if (FileName.ext(this.arg) || !loc.isValid()) { // Modules whose original argument name has an extension, or do not // have a valid location come from the command-line. // Error that their file cannot be found and return early. - .error(loc, "cannot find input file `%s`", srcfile.toChars()); + .error(loc, "cannot find input file `%.*s`", cast(int)name.length, name.ptr); } 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) && isPackageFileName(srcfile); if (isPackageMod) - .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%s'", toChars(), srcfile.toChars()); + .error(loc, "importing package '%s' requires a 'package.d' file which cannot be found in '%.*s'", toChars(), cast(int)name.length, name.ptr); else { .error(loc, "unable to read module `%s`", toChars()); - const pkgfile = FileName.combine(FileName.removeExt(srcfile.toString()), package_d); - .errorSupplemental(loc, "Expected '%s' or '%s' in one of the following import paths:", - srcfile.toChars(), pkgfile.ptr); + const pkgfile = FileName.combine(FileName.sansExt(name), package_d); + .errorSupplemental(loc, "Expected '%.*s' or '%.*s' in one of the following import paths:", + cast(int)name.length, name.ptr, cast(int)pkgfile.length, pkgfile.ptr); } } if (!global.gag) @@ -653,11 +662,11 @@ extern (C++) final class Module : Package if (global.path.length) { foreach (i, p; global.path[]) - fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p); + fprintf(stderr, "import path[%llu] = %s\n", cast(ulong)i, p.path); } else { - fprintf(stderr, "Specify path to file '%s' with -I switch\n", srcfile.toChars()); + fprintf(stderr, "Specify path to file '%.*s' with -I switch\n", cast(int)name.length, name.ptr); } removeHdrFilesAndFail(global.params, Module.amodules); @@ -675,7 +684,7 @@ extern (C++) final class Module : Package * * Returns: `true` if successful */ - bool read(const ref Loc loc) + bool read(Loc loc) { if (this.src) return true; // already read @@ -722,6 +731,11 @@ extern (C++) final class Module : Package { const(char)* srcname = srcfile.toChars(); //printf("Module::parse(srcname = '%s')\n", srcname); + + import dmd.timetrace; + timeTraceBeginEvent(TimeTraceEventType.parse); + scope (exit) timeTraceEndEvent(TimeTraceEventType.parse, this); + isPackageFile = isPackageFileName(srcfile); const(char)[] buf = processSource(src, this); // an error happened on UTF conversion @@ -773,20 +787,19 @@ extern (C++) final class Module : Package { filetype = FileType.c; - global.compileEnv.masm = target.os == Target.OS.Windows && !target.omfobj; // Microsoft inline assembler format + global.compileEnv.masm = target.os == Target.OS.Windows; // Microsoft inline assembler format scope p = new CParser!AST(this, buf, cast(bool) docfile, global.errorSink, target.c, &defines, &global.compileEnv); global.compileEnv.masm = false; p.nextToken(); checkCompiledImport(); members = p.parseModule(); assert(!p.md); // C doesn't have module declarations - numlines = p.scanloc.linnum; + numlines = p.linnum; } else { const bool doUnittests = global.params.parsingUnittestsRequired(); scope p = new Parser!AST(this, buf, cast(bool) docfile, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; p.nextToken(); p.parseModuleDeclaration(); md = p.md; @@ -805,7 +818,7 @@ extern (C++) final class Module : Package checkCompiledImport(); members = p.parseModuleContent(); - numlines = p.scanloc.linnum; + numlines = p.linnum; } /* The symbol table into which the module is to be inserted. @@ -945,7 +958,7 @@ extern (C++) final class Module : Package * sc = the scope into which we are imported * loc = the location of the import statement */ - void checkImportDeprecation(const ref Loc loc, Scope* sc) + void checkImportDeprecation(Loc loc, Scope* sc) { if (md && md.isdeprecated && !sc.isDeprecated) { @@ -1154,11 +1167,6 @@ extern (C++) final class Module : Package uint[uint] ctfe_cov; /// coverage information from ctfe execution_count[line] - override inout(Module) isModule() inout nothrow - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1270,7 +1278,7 @@ extern (C++) struct ModuleDeclaration bool isdeprecated; // if it is a deprecated module Expression msg; - extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated) @safe + extern (D) this(Loc loc, Identifier[] packages, Identifier id, Expression msg, bool isdeprecated) @safe { this.loc = loc; this.packages = packages; @@ -1394,7 +1402,7 @@ private const(char)[] processSource (const(ubyte)[] src, Module mod) { enum SourceEncoding { utf16, utf32} enum Endian { little, big} - immutable loc = mod.getLoc(); + immutable loc = mod.loc; /* * Convert a buffer from UTF32 to UTF8 diff --git a/gcc/d/dmd/doc.d b/gcc/d/dmd/doc.d index c00c1cc..dc4a0b9 100644 --- a/gcc/d/dmd/doc.d +++ b/gcc/d/dmd/doc.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/ddoc.html, Documentation Generator) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/doc.d, _doc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/doc.d, _doc.d) * Documentation: https://dlang.org/phobos/dmd_doc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/doc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/doc.d */ module dmd.doc; @@ -433,10 +433,7 @@ void gendocfile(Module m, const char[] ddoctext, const char* datetime, ErrorSink OutBuffer buf; if (m.filetype == FileType.ddoc) { - const ploc = m.md ? &m.md.loc : &m.loc; - Loc loc = *ploc; - if (!loc.filename) - loc.filename = srcfilename.ptr; + Loc loc = m.md ? m.md.loc : m.loc; size_t commentlen = m.comment ? strlen(cast(char*)m.comment) : 0; Dsymbols a; @@ -477,7 +474,7 @@ void gendocfile(Module m, const char[] ddoctext, const char* datetime, ErrorSink auto p = slice.ptr; for (size_t j = 0; j < slice.length; j++) { - char c = p[j]; + const c = p[j]; if (c == 0xFF && j + 1 < slice.length) { j++; @@ -510,7 +507,7 @@ void escapeDdocString(ref OutBuffer buf, size_t start) { for (size_t u = start; u < buf.length; u++) { - char c = buf[u]; + const c = buf[u]; switch (c) { case '$': @@ -637,7 +634,7 @@ private void escapeStrayParenthesis(Loc loc, ref OutBuffer buf, size_t start, bo for (size_t u = buf.length; u > start;) { u--; - char c = buf[u]; + const c = buf[u]; switch (c) { case ')': @@ -694,9 +691,7 @@ bool emitAnchorName(ref OutBuffer buf, Dsymbol s, Scope* sc, bool includeParent) } else { - /* We just want the identifier, not overloads like TemplateDeclaration::toChars. - * We don't want the template parameter list and constraints. */ - buf.writestring(s.Dsymbol.toChars()); + buf.writestring(s.ident ? s.ident.toString : "__anonymous"); } return true; } @@ -799,7 +794,11 @@ void emitAnchor(ref OutBuffer buf, Dsymbol s, Scope* sc, bool forHeader = false) } else { - auto symbolName = ident.toString(); + // buf.writestring("<<<"); + // buf.writestring(typeof(ident).stringof); + // buf.writestring(">>>"); + // auto symbolName = ident.toString(); + auto symbolName = ident.toChars().toDString(); buf.printf("$(%.*s %.*s", cast(int) macroName.length, macroName.ptr, cast(int) symbolName.length, symbolName.ptr); @@ -2062,9 +2061,9 @@ string toLowercase(string s) pure @safe // TODO: maybe unicode lowercase, somehow if (c >= 'A' && c <= 'Z') { - if (!lower.length) { + if (!lower.length) lower.reserve(s.length); - } + lower ~= s[lower.length..i]; c += 'a' - 'A'; lower ~= c; @@ -2107,42 +2106,12 @@ int getMarkdownIndent(ref OutBuffer buf, size_t from, size_t to) @safe } /************************************************ - * Scan forward to one of: - * start of identifier - * beginning of next line - * end of buf - */ -size_t skiptoident(ref OutBuffer buf, size_t i) @safe -{ - const slice = buf[]; - while (i < slice.length) - { - dchar c; - size_t oi = i; - if (utf_decodeChar(slice, i, c)) - { - /* Ignore UTF errors, but still consume input - */ - break; - } - if (c >= 0x80) - { - if (!isUniAlpha(c)) - continue; - } - else if (!(isalpha(c) || c == '_' || c == '\n')) - continue; - i = oi; - break; - } - return i; -} - -/************************************************ * Scan forward past end of identifier. */ size_t skippastident(ref OutBuffer buf, size_t i) @safe { + import dmd.common.charactertables; + const slice = buf[]; while (i < slice.length) { @@ -2156,7 +2125,8 @@ size_t skippastident(ref OutBuffer buf, size_t i) @safe } if (c >= 0x80) { - if (isUniAlpha(c)) + // we don't care if it is start/continue here + if (isAnyIdentifierCharacter(c)) continue; } else if (isalnum(c) || c == '_') @@ -2173,6 +2143,8 @@ size_t skippastident(ref OutBuffer buf, size_t i) @safe */ size_t skipPastIdentWithDots(ref OutBuffer buf, size_t i) @safe { + import dmd.common.charactertables; + const slice = buf[]; bool lastCharWasDot; while (i < slice.length) @@ -2203,7 +2175,8 @@ size_t skipPastIdentWithDots(ref OutBuffer buf, size_t i) @safe { if (c >= 0x80) { - if (isUniAlpha(c)) + // we don't care if it is start/continue here + if (isAnyIdentifierCharacter(c)) { lastCharWasDot = false; continue; @@ -2300,10 +2273,9 @@ void removeBlankLineMacro(ref OutBuffer buf, ref size_t iAt, ref size_t i) * thematic break. If the replacement is made `i` changes to * point to the closing parenthesis of the `$(HR)` macro. * iLineStart = the index within `buf` that the thematic break's line starts at - * loc = the current location within the file * Returns: whether a thematic break was replaced */ -bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart, const ref Loc loc) +bool replaceMarkdownThematicBreak(ref OutBuffer buf, ref size_t i, size_t iLineStart) { const slice = buf[]; @@ -2404,11 +2376,10 @@ void removeAnyAtxHeadingSuffix(ref OutBuffer buf, size_t i) * iEnd = the index within `buf` of the character after the last * heading character. Is incremented by the length of the * inserted heading macro when this function ends. - * loc = the location of the Ddoc within the file * headingLevel = the level (1-6) of heading to end. Is set to `0` when this * function ends. */ -void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, const ref Loc loc, ref int headingLevel) +void endMarkdownHeading(ref OutBuffer buf, size_t iStart, ref size_t iEnd, ref int headingLevel) { char[5] heading = "$(H0 "; heading[3] = cast(char) ('0' + headingLevel); @@ -2465,12 +2436,11 @@ size_t endAllListsAndQuotes(ref OutBuffer buf, ref size_t i, ref MarkdownList[] * e.g. `*very* **nice**` becomes `$(EM very) $(STRONG nice)`. * Params: * buf = an OutBuffer containing the DDoc - * loc = the current location within the file * inlineDelimiters = the collection of delimiters found within a paragraph. When this function returns its length will be reduced to `downToLevel`. * downToLevel = the length within `inlineDelimiters`` to reduce emphasis to * Returns: the number of characters added to the buffer by the replacements */ -size_t replaceMarkdownEmphasis(ref OutBuffer buf, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0) +size_t replaceMarkdownEmphasis(ref OutBuffer buf, ref MarkdownDelimiter[] inlineDelimiters, int downToLevel = 0) { size_t replaceEmphasisPair(ref MarkdownDelimiter start, ref MarkdownDelimiter end) { @@ -2613,7 +2583,7 @@ TypeFunction isTypeFunction(Dsymbol s) @safe { Type t = f.originalType ? f.originalType : f.type; if (t.ty == Tfunction) - return cast(TypeFunction)t; + return (() @trusted => cast(TypeFunction)t)(); } return null; } @@ -2653,7 +2623,7 @@ Parameter isFunctionParameter(Dsymbols* a, const(char)[] p) @safe /**************************************************** */ -Parameter isEponymousFunctionParameter(Dsymbols *a, const(char)[] p) @safe +Parameter isEponymousFunctionParameter(Dsymbols* a, const(char)[] p) @safe { foreach (Dsymbol dsym; *a) { @@ -2849,10 +2819,9 @@ struct MarkdownList * i = the index within `buf` of the list item. If this function succeeds `i` will be adjusted to fit the inserted macro. * iPrecedingBlankLine = the index within `buf` of the preceeding blank line. If non-zero and a new list was started, the preceeding blank line is removed and this value is set to `0`. * nestedLists = a set of nested lists. If this function succeeds it may contain a new nested list. - * loc = the location of the Ddoc within the file * Returns: `true` if a list was created */ - bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists, const ref Loc loc) + bool startItem(ref OutBuffer buf, ref size_t iLineStart, ref size_t i, ref size_t iPrecedingBlankLine, ref MarkdownList[] nestedLists) { buf.remove(iStart, iContentStart - iStart); @@ -3027,14 +2996,13 @@ struct MarkdownLink * buf = an OutBuffer containing the DDoc * i = the index within `buf` that points to the `]` character of the potential link. * If this function succeeds it will be adjusted to fit the inserted link macro. - * loc = the current location within the file * inlineDelimiters = previously parsed Markdown delimiters, including emphasis and link/image starts * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter * linkReferences = previously parsed link references. When this function returns it may contain * additional previously unparsed references. * Returns: whether a reference link was found and replaced at `i` */ - static bool replaceLink(ref OutBuffer buf, ref size_t i, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) + static bool replaceLink(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) { const delimiter = inlineDelimiters[delimiterIndex]; MarkdownLink link; @@ -3043,7 +3011,7 @@ struct MarkdownLink if (iEnd > i) { i = delimiter.iStart; - link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc); + link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences); inlineDelimiters.length = delimiterIndex; return true; } @@ -3055,7 +3023,7 @@ struct MarkdownLink if (iEnd > i) { const label = link.label; - link = linkReferences.lookupReference(label, buf, i, loc); + link = linkReferences.lookupReference(label, buf, i); // check rightFlanking to avoid replacing things like int[string] if (!link.href.length && !delimiter.rightFlanking) link = linkReferences.lookupSymbol(label); @@ -3067,7 +3035,7 @@ struct MarkdownLink if (iEnd == i) return false; - immutable delta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, delimiterIndex); + immutable delta = replaceMarkdownEmphasis(buf, inlineDelimiters, delimiterIndex); iEnd += delta; i += delta; link.replaceLink(buf, i, iEnd, delimiter); @@ -3084,10 +3052,9 @@ struct MarkdownLink * delimiterIndex = the index within `inlineDelimiters` of the nearest link/image starting delimiter * linkReferences = previously parsed link references. When this function returns it may contain * additional previously unparsed references. - * loc = the current location in the file * Returns: whether a reference link was found and replaced at `i` */ - static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences, const ref Loc loc) + static bool replaceReferenceDefinition(ref OutBuffer buf, ref size_t i, ref MarkdownDelimiter[] inlineDelimiters, int delimiterIndex, ref MarkdownLinkReferences linkReferences) { const delimiter = inlineDelimiters[delimiterIndex]; MarkdownLink link; @@ -3096,7 +3063,7 @@ struct MarkdownLink return false; i = delimiter.iStart; - link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences, loc); + link.storeAndReplaceDefinition(buf, i, iEnd, linkReferences); inlineDelimiters.length = delimiterIndex; return true; } @@ -3469,9 +3436,8 @@ struct MarkdownLink * iEnd = the index within `buf` that points just after the end of the definition * linkReferences = previously parsed link references. When this function returns it may contain * an additional reference. - * loc = the current location in the file */ - private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences, const ref Loc loc) + private void storeAndReplaceDefinition(ref OutBuffer buf, ref size_t i, size_t iEnd, ref MarkdownLinkReferences linkReferences) { // Remove the definition and trailing whitespace iEnd = skipChars(buf, iEnd, " \t\r\n"); @@ -3604,14 +3570,13 @@ struct MarkdownLinkReferences * label = the label to find the reference for * buf = an OutBuffer containing the DDoc * i = the index within `buf` to start searching for references at - * loc = the current location in the file * Returns: a link. If the `href` member has a value then the reference is valid. */ - MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i, const ref Loc loc) + MarkdownLink lookupReference(string label, ref OutBuffer buf, size_t i) { const lowercaseLabel = label.toLowercase(); if (lowercaseLabel !in references) - extractReferences(buf, i, loc); + extractReferences(buf, i); if (lowercaseLabel in references) return references[lowercaseLabel]; @@ -3637,7 +3602,7 @@ struct MarkdownLinkReferences auto id = Identifier.lookup(ids[0].ptr, ids[0].length); if (id) { - auto loc = Loc(); + auto loc = Loc.initial; Dsymbol pscopesym; auto symbol = _scope.search(loc, id, pscopesym, SearchOpt.ignoreErrors); for (size_t i = 1; symbol && i < ids.length; ++i) @@ -3659,10 +3624,9 @@ struct MarkdownLinkReferences * Params: * buf = an OutBuffer containing the DDoc * i = the index within `buf` to start looking at - * loc = the current location in the file * Returns: whether a reference was extracted */ - private void extractReferences(ref OutBuffer buf, size_t i, const ref Loc loc) + private void extractReferences(ref OutBuffer buf, size_t i) { static bool isFollowedBySpace(ref OutBuffer buf, size_t i) { @@ -3750,7 +3714,7 @@ struct MarkdownLinkReferences break; case ']': if (delimiters.length && !inCode && - MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this, loc)) + MarkdownLink.replaceReferenceDefinition(buf, i, delimiters, cast(int) delimiters.length - 1, this)) --i; break; default: @@ -3923,19 +3887,18 @@ size_t parseTableDelimiterRow(ref OutBuffer buf, const size_t iStart, bool inQuo * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table header row starts at, inclusive * iEnd = the index within `buf` that the table header row ends at, exclusive - * loc = the current location in the file * inQuote = whether the table is inside a quote * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = the parsed alignments for each column * Returns: the number of characters added by starting the table, or `0` if unchanged */ -size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments) +size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, bool inQuote, ref MarkdownDelimiter[] inlineDelimiters, out TableColumnAlignment[] columnAlignments) { const iDelimiterRowEnd = parseTableDelimiterRow(buf, iEnd + 1, inQuote, columnAlignments); if (iDelimiterRowEnd) { size_t delta; - if (replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, true, delta)) + if (replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, true, delta)) { buf.remove(iEnd + delta, iDelimiterRowEnd - iEnd); buf.insert(iEnd + delta, "$(TBODY "); @@ -3956,7 +3919,6 @@ size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc l * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table row starts at, inclusive * iEnd = the index within `buf` that the table row ends at, exclusive - * loc = the current location in the file * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = alignments for each column * headerRow = if `true` then the number of columns will be enforced to match @@ -3965,7 +3927,7 @@ size_t startTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc l * delta = the number of characters added by replacing the row, or `0` if unchanged * Returns: `true` if a table row was found and replaced */ -bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta) +bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, TableColumnAlignment[] columnAlignments, bool headerRow, out size_t delta) { delta = 0; @@ -3991,7 +3953,7 @@ bool replaceTableRow(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Lo void replaceTableCell(size_t iCellStart, size_t iCellEnd, int cellIndex, int di) { - const eDelta = replaceMarkdownEmphasis(buf, loc, inlineDelimiters, di); + const eDelta = replaceMarkdownEmphasis(buf, inlineDelimiters, di); delta += eDelta; iCellEnd += eDelta; @@ -4109,15 +4071,14 @@ size_t endTable(ref OutBuffer buf, size_t i, ref TableColumnAlignment[] columnAl * buf = an OutBuffer containing the DDoc * iStart = the index within `buf` that the table row starts at, inclusive * iEnd = the index within `buf` that the table row ends at, exclusive - * loc = the current location in the file * inlineDelimiters = delimiters containing columns separators and any inline emphasis * columnAlignments = alignments for each column; upon return is set to length `0` * Returns: the number of characters added by replacing the row, or `0` if unchanged */ -size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref Loc loc, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments) +size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, ref MarkdownDelimiter[] inlineDelimiters, ref TableColumnAlignment[] columnAlignments) { size_t delta; - replaceTableRow(buf, iStart, iEnd, loc, inlineDelimiters, columnAlignments, false, delta); + replaceTableRow(buf, iStart, iEnd, inlineDelimiters, columnAlignments, false, delta); delta += endTable(buf, iEnd + delta, columnAlignments); return delta; } @@ -4134,9 +4095,8 @@ size_t endRowAndTable(ref OutBuffer buf, size_t iStart, size_t iEnd, const ref L */ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t offset) { - const incrementLoc = loc.linnum == 0 ? 1 : 0; - loc.linnum = loc.linnum + incrementLoc; - loc.charnum = 0; + loc.nextLine(); + //printf("highlightText()\n"); bool leadingBlank = true; size_t iParagraphStart = offset; @@ -4187,19 +4147,19 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } if (headingLevel) { - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); ++i; iParagraphStart = skipChars(buf, i, " \t\r\n"); } if (tableRowDetected && !columnAlignments.length) - i += startTable(buf, iLineStart, i, loc, lineQuoted, inlineDelimiters, columnAlignments); + i += startTable(buf, iLineStart, i, lineQuoted, inlineDelimiters, columnAlignments); else if (columnAlignments.length) { size_t delta; - if (replaceTableRow(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments, false, delta)) + if (replaceTableRow(buf, iLineStart, i, inlineDelimiters, columnAlignments, false, delta)) i += delta; else i += endTable(buf, i, columnAlignments); @@ -4214,7 +4174,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of i += endTable(buf, i, columnAlignments); if (!lineQuoted && quoteLevel) endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); // if we don't already know about this paragraph break then // insert a blank line and record the paragraph break @@ -4240,7 +4200,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of lineQuoted = false; tableRowDetected = false; iLineStart = i + 1; - loc.linnum = loc.linnum + incrementLoc; + loc.nextLine(); // update the paragraph start if we just entered a macro if (previousMacroLevel < macroLevel && iParagraphStart < iLineStart) @@ -4330,7 +4290,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (quoteLevel < lineQuoteLevel) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (nestedLists.length) { const indent = getMarkdownIndent(buf, iLineStart, i); @@ -4453,7 +4413,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (!headingLevel) break; - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4494,7 +4454,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of const list = MarkdownList.parseItem(buf, iLineStart, i); if (list.isValid) { - if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (replaceMarkdownThematicBreak(buf, i, iLineStart)) { removeBlankLineMacro(buf, iPrecedingBlankLine, i); iParagraphStart = skipChars(buf, i+1, " \t\r\n"); @@ -4618,7 +4578,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } else { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) { const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4650,9 +4610,9 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of case '_': { - if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (leadingBlank && !inCode && replaceMarkdownThematicBreak(buf, i, iLineStart)) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); @@ -4680,7 +4640,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of break; } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) { const delta = endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); @@ -4690,7 +4650,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } list.macroLevel = macroLevel; - list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists, loc); + list.startItem(buf, iLineStart, i, iPrecedingBlankLine, nestedLists); break; } } @@ -4709,9 +4669,9 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (leadingBlank) { // Check for a thematic break - if (replaceMarkdownThematicBreak(buf, i, iLineStart, loc)) + if (replaceMarkdownThematicBreak(buf, i, iLineStart)) { - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); if (!lineQuoted && quoteLevel) i += endAllListsAndQuotes(buf, iLineStart, nestedLists, quoteLevel, quoteMacroLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); @@ -4790,7 +4750,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of if (delimiter.type == '[' || delimiter.type == '!') { if (delimiter.isValid && - MarkdownLink.replaceLink(buf, i, loc, inlineDelimiters, d, linkReferences)) + MarkdownLink.replaceLink(buf, i, inlineDelimiters, d, linkReferences)) { // if we removed a reference link then we're at line start if (i <= delimiter.iStart) @@ -4888,10 +4848,10 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of --downToLevel; if (headingLevel && headingMacroLevel >= macroLevel) { - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); while (nestedLists.length && nestedLists[$-1].macroLevel >= macroLevel) { i = buf.insert(i, ")\n)"); @@ -4899,7 +4859,7 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of } if (quoteLevel && quoteMacroLevel >= macroLevel) i += endAllMarkdownQuotes(buf, i, quoteLevel); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters, downToLevel); + i += replaceMarkdownEmphasis(buf, inlineDelimiters, downToLevel); --macroLevel; quoteMacroLevel = 0; @@ -4975,11 +4935,11 @@ void highlightText(Scope* sc, Dsymbols* a, Loc loc, ref OutBuffer buf, size_t of size_t i = buf.length; if (headingLevel) { - endMarkdownHeading(buf, iParagraphStart, i, loc, headingLevel); + endMarkdownHeading(buf, iParagraphStart, i, headingLevel); removeBlankLineMacro(buf, iPrecedingBlankLine, i); } - i += endRowAndTable(buf, iLineStart, i, loc, inlineDelimiters, columnAlignments); - i += replaceMarkdownEmphasis(buf, loc, inlineDelimiters); + i += endRowAndTable(buf, iLineStart, i, inlineDelimiters, columnAlignments); + i += replaceMarkdownEmphasis(buf, inlineDelimiters); endAllListsAndQuotes(buf, i, nestedLists, quoteLevel, quoteMacroLevel); } @@ -5249,6 +5209,8 @@ bool isCVariadicArg(const(char)[] p) @nogc nothrow pure @safe @trusted bool isIdStart(const(char)* p) @nogc nothrow pure { + import dmd.common.charactertables; + dchar c = *p; if (isalpha(c) || c == '_') return true; @@ -5257,7 +5219,7 @@ bool isIdStart(const(char)* p) @nogc nothrow pure size_t i = 0; if (utf_decodeChar(p[0 .. 4], i, c)) return false; // ignore errors - if (isUniAlpha(c)) + if (isAnyStart(c)) return true; } return false; @@ -5269,6 +5231,8 @@ bool isIdStart(const(char)* p) @nogc nothrow pure @trusted bool isIdTail(const(char)* p) @nogc nothrow pure { + import dmd.common.charactertables; + dchar c = *p; if (isalnum(c) || c == '_') return true; @@ -5277,7 +5241,7 @@ bool isIdTail(const(char)* p) @nogc nothrow pure size_t i = 0; if (utf_decodeChar(p[0 .. 4], i, c)) return false; // ignore errors - if (isUniAlpha(c)) + if (isAnyContinue(c)) return true; } return false; diff --git a/gcc/d/dmd/doc.h b/gcc/d/dmd/doc.h index 1775e35..61a51a0 100644 --- a/gcc/d/dmd/doc.h +++ b/gcc/d/dmd/doc.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/dscope.d b/gcc/d/dmd/dscope.d index 76a26a2..725a55e 100644 --- a/gcc/d/dmd/dscope.d +++ b/gcc/d/dmd/dscope.d @@ -3,12 +3,12 @@ * * Not to be confused with the `scope` storage class. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d, _dscope.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dscope.d, _dscope.d) * Documentation: https://dlang.org/phobos/dmd_dscope.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dscope.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dscope.d */ module dmd.dscope; @@ -24,6 +24,7 @@ import dmd.dclass; import dmd.declaration; import dmd.dmodule; import dmd.doc; +import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; @@ -45,37 +46,80 @@ import dmd.tokens; //version=LOGSEARCH; +/// What kind of contract function we're in, if any +enum Contract : ubyte +{ + none = 0, + invariant_ = 1, + require = 2, // in contract + ensure = 3, // out contract +} -// List of flags that can be applied to this `Scope` -enum SCOPE +private extern (D) struct BitFields { - ctor = 0x0001, /// constructor type - noaccesscheck = 0x0002, /// don't do access checks - condition = 0x0004, /// inside static if/assert condition - debug_ = 0x0008, /// inside debug conditional - constraint = 0x0010, /// inside template constraint - invariant_ = 0x0020, /// inside invariant code - require = 0x0040, /// inside in contract code - ensure = 0x0060, /// inside out contract code - contract = 0x0060, /// [mask] we're inside contract code - ctfe = 0x0080, /// inside a ctfe-only expression - compile = 0x0100, /// inside __traits(compile) - ignoresymbolvisibility = 0x0200, /// ignore symbol visibility - /// https://issues.dlang.org/show_bug.cgi?id=15907 - Cfile = 0x0800, /// C semantics apply - free = 0x8000, /// is on free list - - fullinst = 0x10000, /// fully instantiate templates - ctfeBlock = 0x20000, /// inside a `if (__ctfe)` block - dip1000 = 0x40000, /// dip1000 errors enabled for this scope - dip25 = 0x80000, /// dip25 errors enabled for this scope + bool ctor; /// constructor type + bool noAccessCheck; /// don't do access checks + bool condition; /// inside static if/assert condition + bool debug_; /// inside debug conditional + bool inTemplateConstraint; /// inside template constraint + Contract contract; + bool ctfe; /// inside a ctfe-only expression + bool traitsCompiles; /// inside __traits(compile) + /// ignore symbol visibility + /// https://issues.dlang.org/show_bug.cgi?id=15907 + bool ignoresymbolvisibility; + bool inCfile; /// C semantics apply + bool canFree; /// is on free list + bool fullinst; /// fully instantiate templates + bool ctfeBlock; /// inside a `if (__ctfe)` block } -/// Flags that are carried along with a scope push() -private enum PersistentFlags = - SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint | - SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility | - SCOPE.Cfile | SCOPE.ctfeBlock | SCOPE.dip1000 | SCOPE.dip25; +/// State of -preview switches +/// +/// By making them part of a Scope, we reduce reliance on dmd.globals, +/// and can enable/disable them per module / edition. +private struct Previews +{ + // Run `dmd -preview=h` for the meaning of these switches + private extern (D) static struct BitFields + { + bool bitfields; + bool dip1000; + bool dip1008; + bool dip1021; + bool dip25; + bool fieldwise; + bool fixAliasThis; + bool fixImmutableConv; + bool in_; + bool inclusiveInContracts; + bool noSharedAccess; + bool rvalueRefParam; + bool safer; + FeatureState systemVariables; + } + + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ushort)); + + void setFromParams(ref Param params) @nogc nothrow pure @safe + { + this.bitfields = params.bitfields; + this.dip1000 = params.useDIP1000 == FeatureState.enabled; + this.dip1008 = params.ehnogc; + this.dip1021 = params.useDIP1021; // == FeatureState.enabled; + this.dip25 = params.useDIP25 == FeatureState.enabled; + this.fixAliasThis = params.fixAliasThis; + this.fixImmutableConv = params.fixImmutableConv; + this.in_ = params.previewIn; + this.inclusiveInContracts = params.inclusiveInContracts; + this.noSharedAccess = params.noSharedAccess == FeatureState.enabled; + this.rvalueRefParam = params.rvalueRefParam == FeatureState.enabled; + this.safer = params.safer == FeatureState.enabled; + this.systemVariables = params.systemVariables; + this.fieldwise = params.fieldwise == FeatureState.enabled; + } +} extern (C++) struct Scope { @@ -87,10 +131,10 @@ extern (C++) struct Scope VarDeclaration varDecl; /// variable we are in during semantic2 Dsymbol parent; /// parent to use LabelStatement slabel; /// enclosing labelled statement - SwitchStatement sw; /// enclosing switch statement + SwitchStatement switchStatement;/// enclosing switch statement Statement tryBody; /// enclosing _body of TryCatchStatement or TryFinallyStatement - TryFinallyStatement tf; /// enclosing try finally statement - ScopeGuardStatement os; /// enclosing scope(xxx) statement + TryFinallyStatement tryFinally; /// enclosing try finally statement + ScopeGuardStatement scopeGuard; /// enclosing scope(xxx) statement Statement sbreak; /// enclosing statement that supports "break" Statement scontinue; /// enclosing statement that supports "continue" ForeachStatement fes; /// if nested function for ForeachStatement, this is it @@ -132,11 +176,14 @@ extern (C++) struct Scope Visibility visibility = Visibility(Visibility.Kind.public_); int explicitVisibility; /// set if in an explicit visibility attribute - StorageClass stc; /// storage class + STC stc; /// storage class DeprecatedDeclaration depdecl; /// customized deprecation message - uint flags; + import dmd.common.bitfields : generateBitFields; + mixin(generateBitFields!(BitFields, ushort)); + + Previews previews; // user defined attributes UserAttributeDeclaration userAttribDecl; @@ -147,6 +194,7 @@ extern (C++) struct Scope AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value, /// do not set wasRead for it + StructDeclaration argStruct; /// elimiate recursion when looking for rvalue construction extern (D) __gshared Scope* freelist; @@ -157,8 +205,8 @@ extern (C++) struct Scope Scope* s = freelist; freelist = s.enclosing; //printf("freelist %p\n", s); - assert(s.flags & SCOPE.free); - s.flags &= ~SCOPE.free; + assert(s.canFree); + s.canFree = false; return s; } return new Scope(); @@ -180,12 +228,10 @@ extern (C++) struct Scope m = m.parent; m.addMember(null, sc.scopesym); m.parent = null; // got changed by addMember() - if (global.params.useDIP1000 == FeatureState.enabled) - sc.flags |= SCOPE.dip1000; - if (global.params.useDIP25 == FeatureState.enabled) - sc.flags |= SCOPE.dip25; + sc.previews.setFromParams(global.params); + if (_module.filetype == FileType.c) - sc.flags |= SCOPE.Cfile; + sc.inCfile = true; // Create the module scope underneath the global scope sc = sc.push(_module); sc.parent = _module; @@ -207,13 +253,13 @@ extern (C++) struct Scope { Scope* s = copy(); //printf("Scope::push(this = %p) new = %p\n", this, s); - assert(!(flags & SCOPE.free)); + assert(!this.canFree); s.scopesym = null; s.enclosing = &this; debug { if (enclosing) - assert(!(enclosing.flags & SCOPE.free)); + assert(!enclosing.canFree); if (s == enclosing) { printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing); @@ -223,12 +269,36 @@ extern (C++) struct Scope s.slabel = null; s.nofree = false; s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup; - s.flags = (flags & PersistentFlags); + + // Only keep persistent flags + s.resetAllFlags(); + s.contract = this.contract; + s.debug_ = this.debug_; + s.ctfe = this.ctfe; + s.traitsCompiles = this.traitsCompiles; + s.inTemplateConstraint = this.inTemplateConstraint; + s.noAccessCheck = this.noAccessCheck; + s.ignoresymbolvisibility = this.ignoresymbolvisibility; + s.inCfile = this.inCfile; + s.ctfeBlock = this.ctfeBlock; + s.previews = this.previews; s.lastdc = null; assert(&this != s); return s; } + /// Copy flags from scope `other` + extern(D) void copyFlagsFrom(Scope* other) @safe + { + this.bitFields = other.bitFields; + } + + /// Set all scope flags to their initial value + extern(D) void resetAllFlags() @safe + { + this.bitFields = 0; + } + extern (D) Scope* push(ScopeDsymbol ss) { //printf("Scope::push(%s)\n", ss.toChars()); @@ -251,7 +321,7 @@ extern (C++) struct Scope this = this.init; enclosing = freelist; freelist = &this; - flags |= SCOPE.free; + this.canFree = true; } return enc; } @@ -270,7 +340,8 @@ extern (C++) struct Scope extern (D) Scope* startCTFE() { Scope* sc = this.push(); - sc.flags = this.flags | SCOPE.ctfe; + sc.copyFlagsFrom(&this); + sc.ctfe = true; version (none) { /* TODO: Currently this is not possible, because we need to @@ -300,7 +371,7 @@ extern (C++) struct Scope extern (D) Scope* endCTFE() { - assert(flags & SCOPE.ctfe); + assert(this.ctfe); return pop(); } @@ -311,30 +382,29 @@ extern (C++) struct Scope * loc = for error messages * ctorflow = flow results to merge in */ - extern (D) void merge(const ref Loc loc, const ref CtorFlow ctorflow) + extern (D) void merge(Loc loc, const ref CtorFlow ctorflow) { if (!mergeCallSuper(this.ctorflow.callSuper, ctorflow.callSuper)) error(loc, "one path skips constructor"); const fies = ctorflow.fieldinit; - if (this.ctorflow.fieldinit.length && fies.length) + if (!this.ctorflow.fieldinit.length || !fies.length) + return; + FuncDeclaration f = func; + if (fes) + f = fes.func; + auto ad = f.isMemberDecl(); + assert(ad); + foreach (i, v; ad.fields) { - FuncDeclaration f = func; - if (fes) - f = fes.func; - auto ad = f.isMemberDecl(); - assert(ad); - foreach (i, v; ad.fields) + bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); + auto fieldInit = &this.ctorflow.fieldinit[i]; + const fiesCurrent = fies[i]; + if (fieldInit.loc is Loc.init) + fieldInit.loc = fiesCurrent.loc; + if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) { - bool mustInit = (v.storage_class & STC.nodefaultctor || v.type.needsNested()); - auto fieldInit = &this.ctorflow.fieldinit[i]; - const fiesCurrent = fies[i]; - if (fieldInit.loc is Loc.init) - fieldInit.loc = fiesCurrent.loc; - if (!mergeFieldInit(this.ctorflow.fieldinit[i].csx, fiesCurrent.csx) && mustInit) - { - error(loc, "one path skips field `%s`", v.toChars()); - } + error(loc, "one path skips field `%s`", v.toChars()); } } } @@ -352,7 +422,7 @@ extern (C++) struct Scope * Returns: * symbol if found, null if not */ - extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all) + extern (C++) Dsymbol search(Loc loc, Identifier ident, out Dsymbol pscopesym, SearchOptFlags flags = SearchOpt.all) { version (LOGSEARCH) { @@ -470,7 +540,7 @@ extern (C++) struct Scope if (sc.scopesym.isModule()) flags |= SearchOpt.unqualifiedModule; // tell Module.search() that SearchOpt.localsOnly is to be obeyed - else if (sc.flags & SCOPE.Cfile && sc.scopesym.isStructDeclaration()) + else if (sc.inCfile && sc.scopesym.isStructDeclaration()) continue; // C doesn't have struct scope if (Dsymbol s = sc.scopesym.search(loc, ident, flags)) @@ -492,11 +562,10 @@ extern (C++) struct Scope } NotFound: - if (global.params.fixAliasThis) + if (sc.previews.fixAliasThis) { Expression exp = new ThisExp(loc); - Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp); - if (aliasSym) + if (Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp)) { //printf("found aliassym: %s\n", aliasSym.toChars()); pscopesym = new ExpressionDsymbol(exp); @@ -511,7 +580,7 @@ extern (C++) struct Scope return null; } - if (this.flags & SCOPE.ignoresymbolvisibility) + if (this.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; // First look in local scopes @@ -658,7 +727,7 @@ extern (C++) struct Scope //printf("\t\tscopesym = %p\n", scopesym); if (!scopesym.symtab) scopesym.symtab = new DsymbolTable(); - if (!(flags & SCOPE.Cfile)) + if (!this.inCfile) return scopesym.symtabInsert(s); // ImportC insert @@ -753,7 +822,7 @@ extern (C++) struct Scope { //printf("\tsc = %p\n", sc); sc.nofree = true; - assert(!(flags & SCOPE.free)); + assert(!this.canFree); //assert(sc != sc.enclosing); //assert(!sc.enclosing || sc != sc.enclosing.enclosing); //if (++i == 10) @@ -814,7 +883,7 @@ extern (C++) struct Scope */ extern (D) bool isFromSpeculativeSemanticContext() scope { - return this.intypeof || this.flags & SCOPE.compile; + return this.intypeof || this.traitsCompiles; } @@ -824,18 +893,24 @@ extern (C++) struct Scope */ extern (D) bool needsCodegen() { - return (flags & (SCOPE.ctfe | SCOPE.ctfeBlock | SCOPE.compile)) == 0; + return !this.ctfe && !this.ctfeBlock && !this.traitsCompiles; } /// Returns: whether to raise DIP1000 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP1000() { - return (flags & SCOPE.dip1000) ? FeatureState.enabled : FeatureState.disabled; + return (this.previews.dip1000 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; } /// Returns: whether to raise DIP25 warnings (FeatureStabe.default) or errors (FeatureState.enabled) extern (D) FeatureState useDIP25() { - return (flags & SCOPE.dip25) ? FeatureState.enabled : FeatureState.disabled; + return (this.previews.dip25 || hasEdition(Edition.v2024)) ? FeatureState.enabled : FeatureState.disabled; + } + + /// Returns: whether this scope compiles with `edition` or later + extern (D) bool hasEdition(Edition edition) + { + return _module && _module.edition >= edition; } } diff --git a/gcc/d/dmd/dstruct.d b/gcc/d/dmd/dstruct.d index df4d07a..d07be2f 100644 --- a/gcc/d/dmd/dstruct.d +++ b/gcc/d/dmd/dstruct.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d, _dstruct.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dstruct.d, _dstruct.d) * Documentation: https://dlang.org/phobos/dmd_dstruct.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dstruct.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dstruct.d */ module dmd.dstruct; @@ -23,11 +23,12 @@ import dmd.declaration; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : search, setFieldOffset; import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; @@ -36,7 +37,7 @@ import dmd.mtype; import dmd.opover; import dmd.target; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : isZeroInit, merge, size, hasPointers; import dmd.typinf; import dmd.visitor; @@ -65,126 +66,6 @@ FuncDeclaration search_toString(StructDeclaration sd) return fd; } -/*************************************** - * Request additional semantic analysis for TypeInfo generation. - * Params: - * sc = context - * t = type that TypeInfo is being generated for - */ -extern (D) void semanticTypeInfo(Scope* sc, Type t) -{ - if (sc) - { - if (sc.intypeof) - return; - if (!sc.needsCodegen()) - return; - } - - if (!t) - return; - - void visitVector(TypeVector t) - { - semanticTypeInfo(sc, t.basetype); - } - - void visitAArray(TypeAArray t) - { - semanticTypeInfo(sc, t.index); - semanticTypeInfo(sc, t.next); - } - - void visitStruct(TypeStruct t) - { - //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); - StructDeclaration sd = t.sym; - - /* Step 1: create TypeInfoDeclaration - */ - if (!sc) // inline may request TypeInfo. - { - Scope scx; - scx.eSink = global.errorSink; - scx._module = sd.getModule(); - getTypeInfoType(sd.loc, t, &scx); - sd.requestTypeInfo = true; - } - else if (!sc.minst) - { - // don't yet have to generate TypeInfo instance if - // the typeid(T) expression exists in speculative scope. - } - else - { - getTypeInfoType(sd.loc, t, sc); - sd.requestTypeInfo = true; - - // https://issues.dlang.org/show_bug.cgi?id=15149 - // if the typeid operand type comes from a - // result of auto function, it may be yet speculative. - // unSpeculative(sc, sd); - } - - /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. - * This should be done even if typeid(T) exists in speculative scope. - * Because it may appear later in non-speculative scope. - */ - if (!sd.members) - return; // opaque struct - if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) - return; // none of TypeInfo-specific members - - // If the struct is in a non-root module, run semantic3 to get - // correct symbols for the member function. - if (sd.semanticRun >= PASS.semantic3) - { - // semantic3 is already done - } - else if (TemplateInstance ti = sd.isInstantiated()) - { - if (ti.minst && !ti.minst.isRoot()) - Module.addDeferredSemantic3(sd); - } - else - { - if (sd.inNonRoot()) - { - //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); - Module.addDeferredSemantic3(sd); - } - } - } - - void visitTuple(TypeTuple t) - { - if (t.arguments) - { - foreach (arg; *t.arguments) - { - semanticTypeInfo(sc, arg.type); - } - } - } - - /* Note structural similarity of this Type walker to that in isSpeculativeType() - */ - - Type tb = t.toBasetype(); - switch (tb.ty) - { - case Tvector: visitVector(tb.isTypeVector()); break; - case Taarray: visitAArray(tb.isTypeAArray()); break; - case Tstruct: visitStruct(tb.isTypeStruct()); break; - case Ttuple: visitTuple (tb.isTypeTuple()); break; - - case Tclass: - case Tenum: break; - - default: semanticTypeInfo(sc, tb.nextOf()); break; - } -} - enum StructFlags : int { none = 0x0, @@ -220,9 +101,10 @@ extern (C++) class StructDeclaration : AggregateDeclaration bool hasIdentityEquals; // true if has identity opEquals bool hasNoFields; // has no fields bool hasCopyCtor; // copy constructor + bool hasMoveCtor; // move constructor bool hasPointerField; // members with indirections bool hasVoidInitPointers; // void-initialized unsafe fields - bool hasSystemFields; // @system members + bool hasUnsafeBitpatterns; // @system members, pointers, bool bool hasFieldWithInvariant; // invariants bool computedTypeProperties;// the above 3 fields are computed // Even if struct is defined as non-root symbol, some built-in operations @@ -234,9 +116,10 @@ extern (C++) class StructDeclaration : AggregateDeclaration import dmd.common.bitfields : generateBitFields; mixin(generateBitFields!(BitFields, ushort)); - extern (D) this(const ref Loc loc, Identifier id, bool inObject) + extern (D) this(Loc loc, Identifier id, bool inObject) { super(loc, id); + this.dsym = DSYM.structDeclaration; zeroInit = false; // assume false until we do semantic processing ispod = ThreeState.none; // For forward references @@ -249,7 +132,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration } } - static StructDeclaration create(const ref Loc loc, Identifier id, bool inObject) + static StructDeclaration create(Loc loc, Identifier id, bool inObject) { return new StructDeclaration(loc, id, inObject); } @@ -290,11 +173,12 @@ extern (C++) class StructDeclaration : AggregateDeclaration { Dsymbol s = (*members)[i]; s.setFieldOffset(this, &fieldState, isunion); - } - if (type.ty == Terror) - { - errors = true; - return; + if (type.ty == Terror) + { + errorSupplemental(s.loc, "error on member `%s`", s.toPrettyChars); + errors = true; + return; + } } if (structsize == 0) @@ -319,11 +203,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration */ structsize = 4; } - else if (target.c.bitFieldStyle == TargetC.BitFieldStyle.DM) - { - structsize = 0; - alignsize = 0; - } else structsize = 0; break; @@ -354,8 +233,18 @@ extern (C++) class StructDeclaration : AggregateDeclaration // Determine if struct is all zeros or not zeroInit = true; + auto lastOffset = -1; foreach (vd; fields) { + // First skip zero sized fields + if (vd.type.size(vd.loc) == 0) + continue; + + // only consider first sized member of an (anonymous) union + if (vd.overlapped && vd.offset == lastOffset) + continue; + lastOffset = vd.offset; + if (vd._init) { if (vd._init.isVoidInitializer()) @@ -364,10 +253,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration */ continue; - // Zero size fields are zero initialized - if (vd.type.size(vd.loc) == 0) - continue; - // Examine init to see if it is all 0s. auto exp = vd.getConstInitializer(); if (!exp || !_isZeroInit(exp)) @@ -395,13 +280,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration foreach (vd; fields) { if (vd.storage_class & STC.ref_ || vd.hasPointers()) + { hasPointerField = true; + hasUnsafeBitpatterns = true; + } if (vd._init && vd._init.isVoidInitializer() && vd.type.hasPointers()) hasVoidInitPointers = true; - if (vd.storage_class & STC.system || vd.type.hasSystemFields()) - hasSystemFields = true; + if (vd.storage_class & STC.system || vd.type.hasUnsafeBitpatterns()) + hasUnsafeBitpatterns = true; if (!vd._init && vd.type.hasVoidInitPointers()) hasVoidInitPointers = true; @@ -432,13 +320,22 @@ extern (C++) class StructDeclaration : AggregateDeclaration if (ispod != ThreeState.none) return (ispod == ThreeState.yes); - ispod = ThreeState.yes; - import dmd.clone; - bool hasCpCtorLocal; - needCopyCtor(this, hasCpCtorLocal); - if (enclosing || search(this, loc, Id.postblit) || search(this, loc, Id.dtor) || hasCpCtorLocal) + bool hasCpCtorLocal; + bool hasMoveCtorLocal; + bool needCopyCtor; + bool needMoveCtor; + needCopyOrMoveCtor(this, hasCpCtorLocal, hasMoveCtorLocal, needCopyCtor, needMoveCtor); + + if (enclosing || // is nested + search(this, loc, Id.postblit) || // has postblit + search(this, loc, Id.dtor) || // has destructor + /* This is commented out because otherwise buildkite vibe.d: + `canCAS!Task` fails to compile + */ + //hasMoveCtorLocal || // has move constructor + hasCpCtorLocal) // has copy constructor { ispod = ThreeState.no; return false; @@ -454,12 +351,9 @@ extern (C++) class StructDeclaration : AggregateDeclaration return false; } - Type tv = v.type.baseElemOf(); - if (tv.ty == Tstruct) + if (auto ts = v.type.baseElemOf().isTypeStruct()) { - TypeStruct ts = cast(TypeStruct)tv; - StructDeclaration sd = ts.sym; - if (!sd.isPOD()) + if (!ts.sym.isPOD()) { ispod = ThreeState.no; return false; @@ -467,7 +361,8 @@ extern (C++) class StructDeclaration : AggregateDeclaration } } - return (ispod == ThreeState.yes); + ispod = ThreeState.yes; + return true; } /*************************************** @@ -480,11 +375,6 @@ extern (C++) class StructDeclaration : AggregateDeclaration return postblit || hasCopyCtor; } - override final inout(StructDeclaration) isStructDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -609,7 +499,7 @@ bool _isZeroInit(Expression exp) case EXP.string_: { - StringExp se = cast(StringExp)exp; + auto se = cast(StringExp)exp; if (se.type.toBasetype().ty == Tarray) // if initializing a dynamic array return se.len == 0; @@ -646,9 +536,10 @@ bool _isZeroInit(Expression exp) */ extern (C++) final class UnionDeclaration : StructDeclaration { - extern (D) this(const ref Loc loc, Identifier id) + extern (D) this(Loc loc, Identifier id) { super(loc, id, false); + this.dsym = DSYM.unionDeclaration; } override UnionDeclaration syntaxCopy(Dsymbol s) @@ -664,11 +555,6 @@ extern (C++) final class UnionDeclaration : StructDeclaration return "union"; } - override inout(UnionDeclaration) isUnionDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/dsymbol.d b/gcc/d/dmd/dsymbol.d index b831c32..74ca9cb 100644 --- a/gcc/d/dmd/dsymbol.d +++ b/gcc/d/dmd/dsymbol.d @@ -1,12 +1,12 @@ /** * The base class for a D symbol, which can be a module, variable, function, enum, etc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d, _dsymbol.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbol.d, _dsymbol.d) * Documentation: https://dlang.org/phobos/dmd_dsymbol.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbol.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dsymbol.d */ module dmd.dsymbol; @@ -51,11 +51,12 @@ import dmd.statement; import dmd.staticassert; import dmd.tokens; import dmd.visitor; +import dmd.dsymbolsem; import dmd.common.outbuffer; /*************************************** - * Calls dg(Dsymbol *sym) for each Dsymbol. + * Calls dg(Dsymbol* sym) for each Dsymbol. * If dg returns !=0, stops and returns that value else returns 0. * Params: * symbols = Dsymbols @@ -84,7 +85,7 @@ int foreachDsymbol(Dsymbols* symbols, scope int delegate(Dsymbol) dg) } /*************************************** - * Calls dg(Dsymbol *sym) for each Dsymbol. + * Calls dg(Dsymbol* sym) for each Dsymbol. * Params: * symbols = Dsymbols * dg = delegate to call for each Dsymbol @@ -258,6 +259,76 @@ private struct DsymbolAttributes UserAttributeDeclaration userAttribDecl; } +enum DSYM : ubyte +{ + none, + dsymbol, + linkDeclaration, + cppMangleDeclaration, + alignDeclaration, + pragmaDeclaration, + conditionalDeclaration, + staticForeachDeclaration, + userAttributeDeclaration, + labelDsymbol, + aliasThis, + package_, + module_, + enumMember, + templateDeclaration, + templateInstance, + templateMixin, + forwardingAttribDeclaration, + nspace, + declaration, + storageClassDeclaration, + expressionDsymbol, + aliasAssign, + thisDeclaration, + bitFieldDeclaration, + typeInfoDeclaration, + tupleDeclaration, + aliasDeclaration, + aggregateDeclaration, + funcDeclaration, + funcAliasDeclaration, + overDeclaration, + funcLiteralDeclaration, + ctorDeclaration, + postBlitDeclaration, + dtorDeclaration, + staticCtorDeclaration, + staticDtorDeclaration, + sharedStaticCtorDeclaration, + sharedStaticDtorDeclaration, + invariantDeclaration, + unitTestDeclaration, + newDeclaration, + varDeclaration, + versionSymbol, + debugSymbol, + classDeclaration, + structDeclaration, + unionDeclaration, + interfaceDeclaration, + scopeDsymbol, + forwardingScopeDsymbol, + withScopeSymbol, + arrayScopeSymbol, + import_, + enumDeclaration, + symbolDeclaration, + attribDeclaration, + anonDeclaration, + cppNamespaceDeclaration, + visibilityDeclaration, + overloadSet, + mixinDeclaration, + staticAssert, + staticIfDeclaration, + cAsmDeclaration +} + /*********************************************************** */ extern (C++) class Dsymbol : ASTNode @@ -265,42 +336,51 @@ extern (C++) class Dsymbol : ASTNode Identifier ident; Dsymbol parent; Symbol* csym; // symbol for code generator - const Loc loc; // where defined Scope* _scope; // !=null means context to use for semantic() - const(char)* prettystring; // cached value of toPrettyChars() private DsymbolAttributes* atts; /// attached attribute declarations - bool errors; // this symbol failed to pass semantic() - PASS semanticRun = PASS.initial; + const Loc loc; // where defined ushort localNum; /// perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab + static struct BitFields + { + bool errors; // this symbol failed to pass semantic() + PASS semanticRun = PASS.initial; + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); + DSYM dsym; - final extern (D) this() nothrow @safe + final extern (D) this(DSYM tag) nothrow @safe { //printf("Dsymbol::Dsymbol(%p)\n", this); - loc = Loc(null, 0, 0); + this.dsym = tag; + loc = Loc.initial; } - final extern (D) this(Identifier ident) nothrow @safe + final extern (D) this(DSYM tag, Identifier ident) nothrow @safe { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); - this.loc = Loc(null, 0, 0); + this.dsym = tag; + this.loc = Loc.initial; this.ident = ident; } - final extern (D) this(const ref Loc loc, Identifier ident) nothrow @safe + final extern (D) this(DSYM tag, Loc loc, Identifier ident) nothrow @safe { //printf("Dsymbol::Dsymbol(%p, ident)\n", this); + this.dsym = tag; this.loc = loc; this.ident = ident; } static Dsymbol create(Identifier ident) nothrow @safe { - return new Dsymbol(ident); + return new Dsymbol(DSYM.dsymbol, ident); } - override const(char)* toChars() const + final override const(char)* toChars() const { - return ident ? ident.toChars() : "__anonymous"; + import dmd.hdrgen : toChars; + return toChars(this); } // Getters / setters for fields stored in `DsymbolAttributes` @@ -345,19 +425,6 @@ extern (C++) class Dsymbol : ASTNode return toChars(); } - final const(Loc) getLoc() - { - if (!loc.isValid()) // avoid bug 5861. - if (const m = getModule()) - return Loc(m.srcfile.toChars(), 0, 0); - return loc; - } - - final const(char)* locToChars() - { - return getLoc().toChars(); - } - override bool equals(const RootObject o) const { if (this == o) @@ -396,8 +463,7 @@ extern (C++) class Dsymbol : ASTNode while (s) { //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); - Module m = s.isModule(); - if (m) + if (Module m = s.isModule()) return m; s = s.parent; } @@ -428,8 +494,7 @@ extern (C++) class Dsymbol : ASTNode while (s) { //printf("\ts = %s '%s'\n", s.kind(), s.toPrettyChars()); - Module m = s.isModule(); - if (m) + if (Module m = s.isModule()) return m; TemplateInstance ti = s.isTemplateInstance(); if (ti && ti.enclosing) @@ -453,7 +518,7 @@ extern (C++) class Dsymbol : ASTNode * * See also `parent`, `toParent` and `toParent2`. */ - final inout(Dsymbol) pastMixin() inout + final inout(Dsymbol) pastMixin() inout @safe { //printf("Dsymbol::pastMixin() %s\n", toChars()); if (!isTemplateMixin() && !isForwardingAttribDeclaration() && !isForwardingScopeDsymbol()) @@ -503,13 +568,13 @@ extern (C++) class Dsymbol : ASTNode * // s.toParentLocal() == FuncDeclaration('mod.test') * --- */ - final inout(Dsymbol) toParent() inout + final inout(Dsymbol) toParent() inout @safe { return parent ? parent.pastMixin() : null; } /// ditto - final inout(Dsymbol) toParent2() inout + final inout(Dsymbol) toParent2() inout @safe { if (!parent || !parent.isTemplateInstance && !parent.isForwardingAttribDeclaration() && !parent.isForwardingScopeDsymbol()) return parent; @@ -593,7 +658,7 @@ extern (C++) class Dsymbol : ASTNode continue; if (sa == p1) return true; - else if (p2 && sa == p2) + if (p2 && sa == p2) return true; } outer = ti.tempdecl.toParent(); @@ -621,7 +686,7 @@ extern (C++) class Dsymbol : ASTNode final Ungag ungagSpeculative() const { - uint oldgag = global.gag; + const oldgag = global.gag; if (global.gag && !isSpeculative() && !toParent2().isFuncDeclaration()) global.gag = 0; return Ungag(oldgag); @@ -657,15 +722,10 @@ extern (C++) class Dsymbol : ASTNode const(char)* toPrettyChars(bool QualifyTypes = false) { - if (prettystring && !QualifyTypes) - return prettystring; // value cached for speed - //printf("Dsymbol::toPrettyChars() '%s'\n", toChars()); if (!parent) { auto s = toChars(); - if (!QualifyTypes) - prettystring = s; return s; } @@ -685,8 +745,6 @@ extern (C++) class Dsymbol : ASTNode addQualifiers(this); auto s = buf.extractSlice(true).ptr; - if (!QualifyTypes) - prettystring = s; return s; } @@ -723,7 +781,7 @@ extern (C++) class Dsymbol : ASTNode * Returns: * SIZE_INVALID when the size cannot be determined */ - uinteger_t size(const ref Loc loc) + uinteger_t size(Loc loc) { .error(loc, "%s `%s` symbol `%s` has no size", kind, toPrettyChars, toChars()); return SIZE_INVALID; @@ -842,20 +900,6 @@ extern (C++) class Dsymbol : ASTNode assert(0); } - /************************************** - * Determine if this symbol is only one. - * Returns: - * false, ps = null: There are 2 or more symbols - * true, ps = null: There are zero symbols - * true, ps = symbol: The one and only one symbol - */ - bool oneMember(out Dsymbol ps, Identifier ident) - { - //printf("Dsymbol::oneMember()\n"); - ps = this; - return true; - } - /***************************************** * Same as Dsymbol::oneMember(), but look at an array of Dsymbols. */ @@ -872,7 +916,7 @@ extern (C++) class Dsymbol : ASTNode for (size_t i = 0; i < members.length; i++) { Dsymbol sx = (*members)[i]; - bool x = sx.oneMember(ps, ident); + bool x = sx.oneMember(ps, ident); //MYTODO: this temporarily creates a new dependency to dsymbolsem, will need to extract oneMembers() later //printf("\t[%d] kind %s = %d, s = %p\n", i, sx.kind(), x, *ps); if (!x) { @@ -928,42 +972,18 @@ extern (C++) class Dsymbol : ASTNode return false; } - bool hasStaticCtorOrDtor() - { - //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); - return false; - } - void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories) { } - void checkCtorConstInit() - { - } - /**************************************** * Add documentation comment to Dsymbol. * Ignore NULL comments. */ void addComment(const(char)* comment) { - if (!comment || !*comment) - return; - - //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); - void* h = cast(void*)this; // just the pointer is the key - auto p = h in commentHashTable; - if (!p) - { - commentHashTable[h] = comment; - return; - } - if (strcmp(*p, comment) != 0) - { - // Concatenate the two - *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); - } + import dmd.dsymbolsem; + dmd.dsymbolsem.addComment(this, comment); } /// get documentation comment for this Dsymbol @@ -981,7 +1001,7 @@ extern (C++) class Dsymbol : ASTNode /* Shell around addComment() to avoid disruption for the moment */ final void comment(const(char)* comment) { addComment(comment); } - private extern (D) __gshared const(char)*[void*] commentHashTable; + extern (D) __gshared const(char)*[void*] commentHashTable; /********************************** @@ -1048,64 +1068,181 @@ extern (C++) class Dsymbol : ASTNode v.visit(this); } - pure nothrow @safe @nogc: - - // Eliminate need for dynamic_cast - inout(Package) isPackage() inout { return null; } - inout(Module) isModule() inout { return null; } - inout(EnumMember) isEnumMember() inout { return null; } - inout(TemplateDeclaration) isTemplateDeclaration() inout { return null; } - inout(TemplateInstance) isTemplateInstance() inout { return null; } - inout(TemplateMixin) isTemplateMixin() inout { return null; } - inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return null; } - inout(Nspace) isNspace() inout { return null; } - inout(Declaration) isDeclaration() inout { return null; } - inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return null; } - inout(ExpressionDsymbol) isExpressionDsymbol() inout { return null; } - inout(AliasAssign) isAliasAssign() inout { return null; } - inout(ThisDeclaration) isThisDeclaration() inout { return null; } - inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return null; } - inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return null; } - inout(TupleDeclaration) isTupleDeclaration() inout { return null; } - inout(AliasDeclaration) isAliasDeclaration() inout { return null; } - inout(AggregateDeclaration) isAggregateDeclaration() inout { return null; } - inout(FuncDeclaration) isFuncDeclaration() inout { return null; } - inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return null; } - inout(OverDeclaration) isOverDeclaration() inout { return null; } - inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return null; } - inout(CtorDeclaration) isCtorDeclaration() inout { return null; } - inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return null; } - inout(DtorDeclaration) isDtorDeclaration() inout { return null; } - inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return null; } - inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return null; } - inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return null; } - inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return null; } - inout(InvariantDeclaration) isInvariantDeclaration() inout { return null; } - inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return null; } - inout(NewDeclaration) isNewDeclaration() inout { return null; } - inout(VarDeclaration) isVarDeclaration() inout { return null; } - inout(VersionSymbol) isVersionSymbol() inout { return null; } - inout(DebugSymbol) isDebugSymbol() inout { return null; } - inout(ClassDeclaration) isClassDeclaration() inout { return null; } - inout(StructDeclaration) isStructDeclaration() inout { return null; } - inout(UnionDeclaration) isUnionDeclaration() inout { return null; } - inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return null; } - inout(ScopeDsymbol) isScopeDsymbol() inout { return null; } - inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return null; } - inout(WithScopeSymbol) isWithScopeSymbol() inout { return null; } - inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return null; } - inout(Import) isImport() inout { return null; } - inout(EnumDeclaration) isEnumDeclaration() inout { return null; } - inout(SymbolDeclaration) isSymbolDeclaration() inout { return null; } - inout(AttribDeclaration) isAttribDeclaration() inout { return null; } - inout(AnonDeclaration) isAnonDeclaration() inout { return null; } - inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; } - inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return null; } - inout(OverloadSet) isOverloadSet() inout { return null; } - inout(MixinDeclaration) isMixinDeclaration() inout { return null; } - inout(StaticAssert) isStaticAssert() inout { return null; } - inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return null; } - inout(CAsmDeclaration) isCAsmDeclaration() inout { return null; } + pure nothrow @trusted @nogc final: + + inout(Package) isPackage() inout { return (dsym == DSYM.package_ || dsym == DSYM.module_) ? cast(inout(Package)) cast(void*) this : null; } + inout(Module) isModule() inout { return dsym == DSYM.module_ ? cast(inout(Module)) cast(void*) this : null; } + inout(EnumMember) isEnumMember() inout { return dsym == DSYM.enumMember ? cast(inout(EnumMember)) cast(void*) this : null; } + inout(TemplateDeclaration) isTemplateDeclaration() inout { return dsym == DSYM.templateDeclaration ? cast(inout(TemplateDeclaration)) cast(void*) this : null; } + inout(TemplateInstance) isTemplateInstance() inout { return (dsym == DSYM.templateInstance || dsym == DSYM.templateMixin) ? cast(inout(TemplateInstance)) cast(void*) this : null; } + inout(TemplateMixin) isTemplateMixin() inout { return dsym == DSYM.templateMixin ? cast(inout(TemplateMixin)) cast(void*) this : null; } + inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout { return dsym == DSYM.forwardingAttribDeclaration ? cast(inout(ForwardingAttribDeclaration)) cast(void*) this : null; } + inout(Nspace) isNspace() inout { return dsym == DSYM.nspace ? cast(inout(Nspace)) cast(void*) this : null; } + inout(Declaration) isDeclaration() inout { + switch (dsym) + { + case DSYM.tupleDeclaration: + case DSYM.aliasDeclaration: + case DSYM.overDeclaration: + case DSYM.varDeclaration: + case DSYM.bitFieldDeclaration: + case DSYM.typeInfoDeclaration: + case DSYM.thisDeclaration: + case DSYM.enumMember: + case DSYM.symbolDeclaration: + case DSYM.funcDeclaration: + case DSYM.funcAliasDeclaration: + case DSYM.funcLiteralDeclaration: + case DSYM.ctorDeclaration: + case DSYM.postBlitDeclaration: + case DSYM.dtorDeclaration: + case DSYM.staticCtorDeclaration: + case DSYM.sharedStaticCtorDeclaration: + case DSYM.staticDtorDeclaration: + case DSYM.sharedStaticDtorDeclaration: + case DSYM.invariantDeclaration: + case DSYM.unitTestDeclaration: + case DSYM.newDeclaration: + return cast(inout(Declaration)) cast(void*) this; + default: + return null; + } + } + inout(StorageClassDeclaration) isStorageClassDeclaration() inout { return dsym == DSYM.storageClassDeclaration ? cast(inout(StorageClassDeclaration)) cast(void*) this : null; } + inout(ExpressionDsymbol) isExpressionDsymbol() inout { return dsym == DSYM.expressionDsymbol ? cast(inout(ExpressionDsymbol)) cast(void*) this : null; } + inout(AliasAssign) isAliasAssign() inout { return dsym == DSYM.aliasAssign ? cast(inout(AliasAssign)) cast(void*) this : null; } + inout(ThisDeclaration) isThisDeclaration() inout { return dsym == DSYM.thisDeclaration ? cast(inout(ThisDeclaration)) cast(void*) this : null; } + inout(BitFieldDeclaration) isBitFieldDeclaration() inout { return dsym == DSYM.bitFieldDeclaration ? cast(inout(BitFieldDeclaration)) cast(void*) this : null; } + inout(TypeInfoDeclaration) isTypeInfoDeclaration() inout { return dsym == DSYM.typeInfoDeclaration ? cast(inout(TypeInfoDeclaration)) cast(void*) this : null; } + inout(TupleDeclaration) isTupleDeclaration() inout { return dsym == DSYM.tupleDeclaration ? cast(inout(TupleDeclaration)) cast(void*) this : null; } + inout(AliasDeclaration) isAliasDeclaration() inout { return dsym == DSYM.aliasDeclaration ? cast(inout(AliasDeclaration)) cast(void*) this : null; } + inout(AggregateDeclaration) isAggregateDeclaration() inout { + switch (dsym) + { + case DSYM.aggregateDeclaration: + case DSYM.structDeclaration: + case DSYM.unionDeclaration: + case DSYM.classDeclaration: + case DSYM.interfaceDeclaration: + return cast(inout(AggregateDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(FuncDeclaration) isFuncDeclaration() inout { + switch (dsym) + { + case DSYM.funcDeclaration: + case DSYM.funcAliasDeclaration: + case DSYM.funcLiteralDeclaration: + case DSYM.ctorDeclaration: + case DSYM.postBlitDeclaration: + case DSYM.dtorDeclaration: + case DSYM.staticCtorDeclaration: + case DSYM.sharedStaticCtorDeclaration: + case DSYM.staticDtorDeclaration: + case DSYM.sharedStaticDtorDeclaration: + case DSYM.invariantDeclaration: + case DSYM.unitTestDeclaration: + case DSYM.newDeclaration: + return cast(inout(FuncDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout { return dsym == DSYM.funcAliasDeclaration ? cast(inout(FuncAliasDeclaration)) cast(void*) this : null; } + inout(OverDeclaration) isOverDeclaration() inout { return dsym == DSYM.overDeclaration ? cast(inout(OverDeclaration)) cast(void*) this : null; } + inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout { return dsym == DSYM.funcLiteralDeclaration ? cast(inout(FuncLiteralDeclaration)) cast(void*) this : null; } + inout(CtorDeclaration) isCtorDeclaration() inout { return dsym == DSYM.ctorDeclaration ? cast(inout(CtorDeclaration)) cast(void*) this : null; } + inout(PostBlitDeclaration) isPostBlitDeclaration() inout { return dsym == DSYM.postBlitDeclaration ? cast(inout(PostBlitDeclaration)) cast(void*) this : null; } + inout(DtorDeclaration) isDtorDeclaration() inout { return dsym == DSYM.dtorDeclaration ? cast(inout(DtorDeclaration)) cast(void*) this : null; } + inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout { return (dsym == DSYM.staticCtorDeclaration || dsym == DSYM.sharedStaticCtorDeclaration) ? cast(inout(StaticCtorDeclaration)) cast(void*) this : null; } + inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout { return (dsym == DSYM.staticDtorDeclaration || dsym == DSYM.sharedStaticDtorDeclaration) ? cast(inout(StaticDtorDeclaration)) cast(void*) this : null; } + inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout { return dsym == DSYM.sharedStaticCtorDeclaration ? cast(inout(SharedStaticCtorDeclaration)) cast(void*) this : null; } + inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout { return dsym == DSYM.sharedStaticDtorDeclaration ? cast(inout(SharedStaticDtorDeclaration)) cast(void*) this : null; } + inout(InvariantDeclaration) isInvariantDeclaration() inout { return dsym == DSYM.invariantDeclaration ? cast(inout(InvariantDeclaration)) cast(void*) this : null; } + inout(UnitTestDeclaration) isUnitTestDeclaration() inout { return dsym == DSYM.unitTestDeclaration ? cast(inout(UnitTestDeclaration)) cast(void*) this : null; } + inout(NewDeclaration) isNewDeclaration() inout { return dsym == DSYM.newDeclaration ? cast(inout(NewDeclaration)) cast(void*) this : null; } + inout(VarDeclaration) isVarDeclaration() inout { + switch (dsym) + { + case DSYM.varDeclaration: + case DSYM.bitFieldDeclaration: + case DSYM.typeInfoDeclaration: + case DSYM.thisDeclaration: + case DSYM.enumMember: + return cast(inout(VarDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(VersionSymbol) isVersionSymbol() inout { return dsym == DSYM.versionSymbol ? cast(inout(VersionSymbol)) cast(void*) this : null; } + inout(DebugSymbol) isDebugSymbol() inout { return dsym == DSYM.debugSymbol ? cast(inout(DebugSymbol)) cast(void*) this : null; } + inout(ClassDeclaration) isClassDeclaration() inout { return (dsym == DSYM.classDeclaration || dsym == DSYM.interfaceDeclaration) ? cast(inout(ClassDeclaration)) cast(void*) this : null; } + inout(StructDeclaration) isStructDeclaration() inout { return (dsym == DSYM.structDeclaration || dsym == DSYM.unionDeclaration) ? cast(inout(StructDeclaration)) cast(void*) this : null; } + inout(UnionDeclaration) isUnionDeclaration() inout { return dsym == DSYM.unionDeclaration ? cast(inout(UnionDeclaration)) cast(void*) this : null; } + inout(InterfaceDeclaration) isInterfaceDeclaration() inout { return dsym == DSYM.interfaceDeclaration ? cast(inout(InterfaceDeclaration)) cast(void*) this : null; } + inout(ScopeDsymbol) isScopeDsymbol() inout { + switch (dsym) + { + case DSYM.enumDeclaration: + case DSYM.scopeDsymbol: + case DSYM.package_: + case DSYM.module_: + case DSYM.nspace: + case DSYM.templateInstance: + case DSYM.templateMixin: + case DSYM.templateDeclaration: + case DSYM.aggregateDeclaration: + case DSYM.structDeclaration: + case DSYM.unionDeclaration: + case DSYM.classDeclaration: + case DSYM.interfaceDeclaration: + case DSYM.withScopeSymbol: + case DSYM.arrayScopeSymbol: + case DSYM.forwardingScopeDsymbol: + return cast(inout(ScopeDsymbol)) cast(void*) this; + default: + return null; + } + } + inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout { return dsym == DSYM.forwardingScopeDsymbol ? cast(inout(ForwardingScopeDsymbol)) cast(void*) this : null; } + inout(WithScopeSymbol) isWithScopeSymbol() inout { return dsym == DSYM.withScopeSymbol ? cast(inout(WithScopeSymbol)) cast(void*) this : null; } + inout(ArrayScopeSymbol) isArrayScopeSymbol() inout { return dsym == DSYM.arrayScopeSymbol ? cast(inout(ArrayScopeSymbol)) cast(void*) this : null; } + inout(Import) isImport() inout { return dsym == DSYM.import_ ? cast(inout(Import)) cast(void*) this : null; } + inout(EnumDeclaration) isEnumDeclaration() inout { return dsym == DSYM.enumDeclaration ? cast(inout(EnumDeclaration)) cast(void*) this : null; } + inout(SymbolDeclaration) isSymbolDeclaration() inout { return dsym == DSYM.symbolDeclaration ? cast(inout(SymbolDeclaration)) cast(void*) this : null; } + inout(AttribDeclaration) isAttribDeclaration() inout { + switch (dsym) + { + case DSYM.attribDeclaration: + case DSYM.storageClassDeclaration: + case DSYM.linkDeclaration: + case DSYM.cppMangleDeclaration: + case DSYM.cppNamespaceDeclaration: + case DSYM.visibilityDeclaration: + case DSYM.alignDeclaration: + case DSYM.anonDeclaration: + case DSYM.pragmaDeclaration: + case DSYM.conditionalDeclaration: + case DSYM.staticIfDeclaration: + case DSYM.staticForeachDeclaration: + case DSYM.forwardingAttribDeclaration: + case DSYM.mixinDeclaration: + case DSYM.userAttributeDeclaration: + return cast(inout(AttribDeclaration)) cast(void*) this; + default: + return null; + } + } + inout(AnonDeclaration) isAnonDeclaration() inout { return dsym == DSYM.anonDeclaration ? cast(inout(AnonDeclaration)) cast(void*) this : null; } + inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return dsym == DSYM.cppNamespaceDeclaration ? cast(inout(CPPNamespaceDeclaration)) cast(void*) this : null; } + inout(VisibilityDeclaration) isVisibilityDeclaration() inout { return dsym == DSYM.visibilityDeclaration ? cast(inout(VisibilityDeclaration)) cast(void*) this : null; } + inout(OverloadSet) isOverloadSet() inout { return dsym == DSYM.overloadSet ? cast(inout(OverloadSet)) cast(void*) this : null; } + inout(MixinDeclaration) isMixinDeclaration() inout { return dsym == DSYM.mixinDeclaration ? cast(inout(MixinDeclaration)) cast(void*) this : null; } + inout(StaticAssert) isStaticAssert() inout { return dsym == DSYM.staticAssert ? cast(inout(StaticAssert)) cast(void*) this : null; } + inout(StaticIfDeclaration) isStaticIfDeclaration() inout { return dsym == DSYM.staticIfDeclaration ? cast(inout(StaticIfDeclaration)) cast(void*) this : null; } + inout(CAsmDeclaration) isCAsmDeclaration() inout { return dsym == DSYM.cAsmDeclaration ? cast(inout(CAsmDeclaration)) cast(void*) this : null; } } /*********************************************************** @@ -1128,16 +1265,17 @@ private: public: final extern (D) this() nothrow @safe { + super(DSYM.scopeDsymbol); } final extern (D) this(Identifier ident) nothrow @safe { - super(ident); + super(DSYM.scopeDsymbol, ident); } - final extern (D) this(const ref Loc loc, Identifier ident) nothrow @safe + final extern (D) this(Loc loc, Identifier ident) nothrow @safe { - super(loc, ident); + super(DSYM.scopeDsymbol, loc, ident); } override ScopeDsymbol syntaxCopy(Dsymbol s) @@ -1277,7 +1415,7 @@ public: return (members is null); } - static void multiplyDefined(const ref Loc loc, Dsymbol s1, Dsymbol s2) + static void multiplyDefined(Loc loc, Dsymbol s1, Dsymbol s2) { version (none) { @@ -1313,7 +1451,7 @@ public: } else { - .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.locToChars()); + .error(s1.loc, "%s `%s` conflicts with %s `%s` at %s", s1.kind, s1.toPrettyChars, s2.kind(), s2.toPrettyChars(), s2.loc.toChars()); } } @@ -1347,29 +1485,6 @@ public: return symtab.lookup(id); } - /**************************************** - * Return true if any of the members are static ctors or static dtors, or if - * any members have members that are. - */ - override bool hasStaticCtorOrDtor() - { - if (members) - { - for (size_t i = 0; i < members.length; i++) - { - Dsymbol member = (*members)[i]; - if (member.hasStaticCtorOrDtor()) - return true; - } - } - return false; - } - - override final inout(ScopeDsymbol) isScopeDsymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1386,11 +1501,7 @@ extern (C++) final class WithScopeSymbol : ScopeDsymbol extern (D) this(WithStatement withstate) nothrow @safe { this.withstate = withstate; - } - - override inout(WithScopeSymbol) isWithScopeSymbol() inout - { - return this; + this.dsym = DSYM.withScopeSymbol; } override void accept(Visitor v) @@ -1414,6 +1525,7 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol assert(exp.op == EXP.index || exp.op == EXP.slice || exp.op == EXP.array); this._scope = sc; this.arrayContent = exp; + this.dsym = DSYM.arrayScopeSymbol; } extern (D) this(Scope* sc, TypeTuple type) nothrow @safe @@ -1428,11 +1540,6 @@ extern (C++) final class ArrayScopeSymbol : ScopeDsymbol this.arrayContent = td; } - override inout(ArrayScopeSymbol) isArrayScopeSymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -1448,7 +1555,7 @@ extern (C++) final class OverloadSet : Dsymbol extern (D) this(Identifier ident, OverloadSet os = null) nothrow { - super(ident); + super(DSYM.overloadSet, ident); if (os) { a.pushSlice(os.a[]); @@ -1460,11 +1567,6 @@ extern (C++) final class OverloadSet : Dsymbol a.push(s); } - override inout(OverloadSet) isOverloadSet() inout - { - return this; - } - override const(char)* kind() const { return "overloadset"; @@ -1487,6 +1589,7 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol extern (D) this() nothrow @safe { super(); + this.dsym = DSYM.forwardingScopeDsymbol; } override Dsymbol symtabInsert(Dsymbol s) nothrow @@ -1555,11 +1658,6 @@ extern (C++) final class ForwardingScopeDsymbol : ScopeDsymbol override const(char)* kind()const{ return "local scope"; } - override inout(ForwardingScopeDsymbol) isForwardingScopeDsymbol() inout nothrow - { - return this; - } - } /** @@ -1572,14 +1670,9 @@ extern (C++) final class ExpressionDsymbol : Dsymbol Expression exp; this(Expression exp) nothrow @safe { - super(); + super(DSYM.expressionDsymbol); this.exp = exp; } - - override inout(ExpressionDsymbol) isExpressionDsymbol() inout nothrow - { - return this; - } } /********************************************** @@ -1595,9 +1688,9 @@ extern (C++) final class AliasAssign : Dsymbol Dsymbol aliassym; /// replace previous RHS of AliasDeclaration with `aliassym` /// only one of type and aliassym can be != null - extern (D) this(const ref Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow @safe + extern (D) this(Loc loc, Identifier ident, Type type, Dsymbol aliassym) nothrow @safe { - super(loc, null); + super(DSYM.aliasAssign, loc, null); this.ident = ident; this.type = type; this.aliassym = aliassym; @@ -1612,11 +1705,6 @@ extern (C++) final class AliasAssign : Dsymbol return aa; } - override inout(AliasAssign) isAliasAssign() inout - { - return this; - } - override const(char)* kind() const { return "alias assignment"; @@ -1709,15 +1797,10 @@ extern (C++) final class CAsmDeclaration : Dsymbol Expression code; extern (D) this(Expression e) nothrow @safe { - super(); + super(DSYM.cAsmDeclaration); this.code = e; } - override inout(CAsmDeclaration) isCAsmDeclaration() inout nothrow - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/dsymbol.h b/gcc/d/dmd/dsymbol.h index f845435..558d156 100644 --- a/gcc/d/dmd/dsymbol.h +++ b/gcc/d/dmd/dsymbol.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -139,20 +139,6 @@ enum class PASS : uint8_t obj // toObjFile() run }; -enum -{ - PASSinit, // initial state - PASSsemantic, // semantic() started - PASSsemanticdone, // semantic() done - PASSsemantic2, // semantic2() started - PASSsemantic2done, // semantic2() done - PASSsemantic3, // semantic3() started - PASSsemantic3done, // semantic3() done - PASSinline, // inline started - PASSinlinedone, // inline done - PASSobj // toObjFile() run -}; - /* Flags for symbol search */ typedef unsigned SearchOptFlags; @@ -192,17 +178,22 @@ public: Identifier *ident; Dsymbol *parent; Symbol *csym; // symbol for code generator - Loc loc; // where defined Scope *_scope; // !=NULL means context to use for semantic() - const utf8_t *prettystring; private: DsymbolAttributes* atts; public: - d_bool errors; // this symbol failed to pass semantic() - PASS semanticRun; + Loc loc; // where defined unsigned short localNum; // perturb mangled name to avoid collisions with those in FuncDeclaration.localsymtab + + bool errors() const; + PASS semanticRun() const; + PASS semanticRun(PASS v); +private: + unsigned char bitfields; + unsigned char dsym; +public: static Dsymbol *create(Identifier *); - const char *toChars() const override; + const char *toChars() const final override; DeprecatedDeclaration* depdecl(); CPPNamespaceDeclaration* cppnamespace(); UserAttributeDeclaration* userAttribDecl(); @@ -210,8 +201,6 @@ public: CPPNamespaceDeclaration* cppnamespace(CPPNamespaceDeclaration* ns); UserAttributeDeclaration* userAttribDecl(UserAttributeDeclaration* uad); virtual const char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments - Loc getLoc(); - const char *locToChars(); bool equals(const RootObject * const o) const override; bool isAnonymous() const; Module *getModule(); @@ -222,9 +211,9 @@ public: Dsymbol *toParent2(); Dsymbol *toParentDecl(); Dsymbol *toParentLocal(); - Dsymbol *toParentP(Dsymbol *p1, Dsymbol *p2 = NULL); + Dsymbol *toParentP(Dsymbol *p1, Dsymbol *p2 = nullptr); TemplateInstance *isInstantiated(); - bool followInstantiationContext(Dsymbol *p1, Dsymbol *p2 = NULL); + bool followInstantiationContext(Dsymbol *p1, Dsymbol *p2 = nullptr); TemplateInstance *isSpeculative(); Ungag ungagSpeculative(); @@ -237,7 +226,7 @@ public: virtual Dsymbol *toAlias(); // resolve real symbol virtual Dsymbol *toAlias2(); virtual bool overloadInsert(Dsymbol *s); - virtual uinteger_t size(const Loc &loc); + virtual uinteger_t size(Loc loc); virtual bool isforwardRef(); virtual AggregateDeclaration *isThis(); // is a 'this' required to access the member virtual bool isExport() const; // is Dsymbol exported? @@ -254,11 +243,8 @@ public: virtual bool needThis(); // need a 'this' pointer? virtual Visibility visible(); virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees - virtual bool oneMember(Dsymbol *&ps, Identifier *ident); virtual bool hasPointers(); - virtual bool hasStaticCtorOrDtor(); virtual void addObjcSymbols(ClassDeclarations *, ClassDeclarations *) { } - virtual void checkCtorConstInit() { } virtual void addComment(const utf8_t *comment); const utf8_t *comment(); // current value of comment @@ -269,61 +255,61 @@ public: bool inNonRoot(); // Eliminate need for dynamic_cast - virtual Package *isPackage() { return NULL; } - virtual Module *isModule() { return NULL; } - virtual EnumMember *isEnumMember() { return NULL; } - virtual TemplateDeclaration *isTemplateDeclaration() { return NULL; } - virtual TemplateInstance *isTemplateInstance() { return NULL; } - virtual TemplateMixin *isTemplateMixin() { return NULL; } - virtual ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return NULL; } - virtual Nspace *isNspace() { return NULL; } - virtual Declaration *isDeclaration() { return NULL; } - virtual StorageClassDeclaration *isStorageClassDeclaration(){ return NULL; } - virtual ExpressionDsymbol *isExpressionDsymbol() { return NULL; } - virtual AliasAssign *isAliasAssign() { return NULL; } - virtual ThisDeclaration *isThisDeclaration() { return NULL; } - virtual BitFieldDeclaration *isBitFieldDeclaration() { return NULL; } - virtual TypeInfoDeclaration *isTypeInfoDeclaration() { return NULL; } - virtual TupleDeclaration *isTupleDeclaration() { return NULL; } - virtual AliasDeclaration *isAliasDeclaration() { return NULL; } - virtual AggregateDeclaration *isAggregateDeclaration() { return NULL; } - virtual FuncDeclaration *isFuncDeclaration() { return NULL; } - virtual FuncAliasDeclaration *isFuncAliasDeclaration() { return NULL; } - virtual OverDeclaration *isOverDeclaration() { return NULL; } - virtual FuncLiteralDeclaration *isFuncLiteralDeclaration() { return NULL; } - virtual CtorDeclaration *isCtorDeclaration() { return NULL; } - virtual PostBlitDeclaration *isPostBlitDeclaration() { return NULL; } - virtual DtorDeclaration *isDtorDeclaration() { return NULL; } - virtual StaticCtorDeclaration *isStaticCtorDeclaration() { return NULL; } - virtual StaticDtorDeclaration *isStaticDtorDeclaration() { return NULL; } - virtual SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return NULL; } - virtual SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return NULL; } - virtual InvariantDeclaration *isInvariantDeclaration() { return NULL; } - virtual UnitTestDeclaration *isUnitTestDeclaration() { return NULL; } - virtual NewDeclaration *isNewDeclaration() { return NULL; } - virtual VarDeclaration *isVarDeclaration() { return NULL; } - virtual VersionSymbol *isVersionSymbol() { return NULL; } - virtual DebugSymbol *isDebugSymbol() { return NULL; } - virtual ClassDeclaration *isClassDeclaration() { return NULL; } - virtual StructDeclaration *isStructDeclaration() { return NULL; } - virtual UnionDeclaration *isUnionDeclaration() { return NULL; } - virtual InterfaceDeclaration *isInterfaceDeclaration() { return NULL; } - virtual ScopeDsymbol *isScopeDsymbol() { return NULL; } - virtual ForwardingScopeDsymbol *isForwardingScopeDsymbol() { return NULL; } - virtual WithScopeSymbol *isWithScopeSymbol() { return NULL; } - virtual ArrayScopeSymbol *isArrayScopeSymbol() { return NULL; } - virtual Import *isImport() { return NULL; } - virtual EnumDeclaration *isEnumDeclaration() { return NULL; } - virtual SymbolDeclaration *isSymbolDeclaration() { return NULL; } - virtual AttribDeclaration *isAttribDeclaration() { return NULL; } - virtual AnonDeclaration *isAnonDeclaration() { return NULL; } - virtual CPPNamespaceDeclaration *isCPPNamespaceDeclaration() { return NULL; } - virtual VisibilityDeclaration *isVisibilityDeclaration() { return NULL; } - virtual OverloadSet *isOverloadSet() { return NULL; } - virtual MixinDeclaration *isMixinDeclaration() { return NULL; } - virtual StaticAssert *isStaticAssert() { return NULL; } - virtual StaticIfDeclaration *isStaticIfDeclaration() { return NULL; } - virtual CAsmDeclaration *isCAsmDeclaration() { return NULL; } + Package *isPackage(); + Module *isModule(); + EnumMember *isEnumMember(); + TemplateDeclaration *isTemplateDeclaration(); + TemplateInstance *isTemplateInstance(); + TemplateMixin *isTemplateMixin(); + ForwardingAttribDeclaration *isForwardingAttribDeclaration(); + Nspace *isNspace(); + Declaration *isDeclaration(); + StorageClassDeclaration *isStorageClassDeclaration(); + ExpressionDsymbol *isExpressionDsymbol(); + AliasAssign *isAliasAssign(); + ThisDeclaration *isThisDeclaration(); + BitFieldDeclaration *isBitFieldDeclaration(); + TypeInfoDeclaration *isTypeInfoDeclaration(); + TupleDeclaration *isTupleDeclaration(); + AliasDeclaration *isAliasDeclaration(); + AggregateDeclaration *isAggregateDeclaration(); + FuncDeclaration *isFuncDeclaration(); + FuncAliasDeclaration *isFuncAliasDeclaration(); + OverDeclaration *isOverDeclaration(); + FuncLiteralDeclaration *isFuncLiteralDeclaration(); + CtorDeclaration *isCtorDeclaration(); + PostBlitDeclaration *isPostBlitDeclaration(); + DtorDeclaration *isDtorDeclaration(); + StaticCtorDeclaration *isStaticCtorDeclaration(); + StaticDtorDeclaration *isStaticDtorDeclaration(); + SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration(); + SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration(); + InvariantDeclaration *isInvariantDeclaration(); + UnitTestDeclaration *isUnitTestDeclaration(); + NewDeclaration *isNewDeclaration(); + VarDeclaration *isVarDeclaration(); + VersionSymbol *isVersionSymbol(); + DebugSymbol *isDebugSymbol(); + ClassDeclaration *isClassDeclaration(); + StructDeclaration *isStructDeclaration(); + UnionDeclaration *isUnionDeclaration(); + InterfaceDeclaration *isInterfaceDeclaration(); + ScopeDsymbol *isScopeDsymbol(); + ForwardingScopeDsymbol *isForwardingScopeDsymbol(); + WithScopeSymbol *isWithScopeSymbol(); + ArrayScopeSymbol *isArrayScopeSymbol(); + Import *isImport(); + EnumDeclaration *isEnumDeclaration(); + SymbolDeclaration *isSymbolDeclaration(); + AttribDeclaration *isAttribDeclaration(); + AnonDeclaration *isAnonDeclaration(); + CPPNamespaceDeclaration *isCPPNamespaceDeclaration(); + VisibilityDeclaration *isVisibilityDeclaration(); + OverloadSet *isOverloadSet(); + MixinDeclaration *isMixinDeclaration(); + StaticAssert *isStaticAssert(); + StaticIfDeclaration *isStaticIfDeclaration(); + CAsmDeclaration *isCAsmDeclaration(); void accept(Visitor *v) override { v->visit(this); } }; @@ -346,13 +332,11 @@ public: virtual void importScope(Dsymbol *s, Visibility visibility); virtual bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); bool isforwardRef() override final; - static void multiplyDefined(const Loc &loc, Dsymbol *s1, Dsymbol *s2); + static void multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2); const char *kind() const override; virtual Dsymbol *symtabInsert(Dsymbol *s); virtual Dsymbol *symtabLookup(Dsymbol *s, Identifier *id); - bool hasStaticCtorOrDtor() override; - ScopeDsymbol *isScopeDsymbol() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -364,7 +348,6 @@ public: WithStatement *withstate; - WithScopeSymbol *isWithScopeSymbol() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -375,7 +358,6 @@ class ArrayScopeSymbol final : public ScopeDsymbol public: RootObject *arrayContent; - ArrayScopeSymbol *isArrayScopeSymbol() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -387,7 +369,6 @@ public: Dsymbols a; // array of Dsymbols void push(Dsymbol *s); - OverloadSet *isOverloadSet() override { return this; } const char *kind() const override; void accept(Visitor *v) override { v->visit(this); } }; @@ -402,7 +383,6 @@ public: void importScope(Dsymbol *s, Visibility visibility) override; const char *kind() const override; - ForwardingScopeDsymbol *isForwardingScopeDsymbol() override { return this; } }; class ExpressionDsymbol final : public Dsymbol @@ -410,7 +390,6 @@ class ExpressionDsymbol final : public Dsymbol public: Expression *exp; - ExpressionDsymbol *isExpressionDsymbol() override { return this; } }; class CAsmDeclaration final : public Dsymbol @@ -418,7 +397,6 @@ class CAsmDeclaration final : public Dsymbol public: Expression *code; // string expression - CAsmDeclaration *isCAsmDeclaration() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -446,7 +424,11 @@ public: namespace dmd { void addMember(Dsymbol *dsym, Scope *sc, ScopeDsymbol *sds); - Dsymbol *search(Dsymbol *d, const Loc &loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly); + Dsymbol *search(Dsymbol *d, Loc loc, Identifier *ident, SearchOptFlags flags = (SearchOptFlags)SearchOpt::localsOnly); + Dsymbols *include(Dsymbol *d, Scope *sc); void setScope(Dsymbol *d, Scope *sc); void importAll(Dsymbol *d, Scope *sc); + void addComment(Dsymbol *d, const char *comment); + bool oneMember(Dsymbol *d, Dsymbol *&ps, Identifier *ident); + bool hasStaticCtorOrDtor(Dsymbol *d); } diff --git a/gcc/d/dmd/dsymbolsem.d b/gcc/d/dmd/dsymbolsem.d index b13f98a..acbac7a 100644 --- a/gcc/d/dmd/dsymbolsem.d +++ b/gcc/d/dmd/dsymbolsem.d @@ -2,12 +2,12 @@ * Does the semantic 1 pass on the AST, which looks at symbol declarations but not initializers * or function bodies. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d, _dsymbolsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dsymbolsem.d, _dsymbolsem.d) * Documentation: https://dlang.org/phobos/dmd_dsymbolsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dsymbolsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dsymbolsem.d */ module dmd.dsymbolsem; @@ -21,12 +21,15 @@ import dmd.arraytypes; import dmd.astcodegen; import dmd.astenums; import dmd.attrib; +import dmd.attribsem; import dmd.clone; import dmd.cond; +import dmd.timetrace; import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.denum; +import dmd.deps; import dmd.dimport; import dmd.dinterpret; import dmd.dmodule; @@ -50,6 +53,7 @@ import dmd.init; import dmd.initsem; import dmd.intrange; import dmd.hdrgen; +import dmd.lexer; import dmd.location; import dmd.mtype; import dmd.mustuse; @@ -58,11 +62,14 @@ import dmd.objc; import dmd.opover; import dmd.optimize; import dmd.parse; +debug import dmd.printast; import dmd.root.array; import dmd.root.filename; +import dmd.root.string; import dmd.common.outbuffer; import dmd.root.rmem; import dmd.rootobject; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; import dmd.sideeffect; @@ -126,10 +133,10 @@ AlignDeclaration getAlignment(AlignDeclaration ad, Scope* sc) else { auto n = e.toInteger(); - if (sc.flags & SCOPE.Cfile && n == 0) // C11 6.7.5-6 allows 0 for alignment + if (sc.inCfile && n == 0) // C11 6.7.5-6 allows 0 for alignment continue; - if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isintegral()) + if (n < 1 || n & (n - 1) || ushort.max < n || !e.type.isIntegral()) { error(ad.loc, "alignment must be an integer positive power of 2, not 0x%llx", cast(ulong)n); errors = true; @@ -167,7 +174,7 @@ const(char)* getMessage(DeprecatedDeclaration dd) return dd.msgstr; } -bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) +bool checkDeprecated(Dsymbol d, Loc loc, Scope* sc) { if (global.params.useDeprecated == DiagnosticReporting.off) return false; @@ -178,7 +185,7 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) return false; // Don't complain if we're inside a template constraint // https://issues.dlang.org/show_bug.cgi?id=21831 - if (sc.flags & SCOPE.constraint) + if (sc.inTemplateConstraint) return false; const(char)* message = null; @@ -204,7 +211,7 @@ bool checkDeprecated(Dsymbol d, const ref Loc loc, Scope* sc) /********************************* * Check type to see if it is based on a deprecated symbol. */ -private void checkDeprecated(Type type, const ref Loc loc, Scope* sc) +private void checkDeprecated(Type type, Loc loc, Scope* sc) { if (Dsymbol s = type.toDsymbol(sc)) { @@ -238,38 +245,58 @@ package bool allowsContractWithoutBody(FuncDeclaration funcdecl) } /* -Tests whether the `ctor` that is part of `ti` is an rvalue constructor -(i.e. a constructor that receives a single parameter of the same type as -`Unqual!typeof(this)`). If that is the case and `sd` contains a copy -constructor, than an error is issued. +If sd has a copy constructor and ctor is an rvalue constructor, +issue an error. Params: - sd = struct declaration that may contin both an rvalue and copy constructor - ctor = constructor that will be checked if it is an evalue constructor + sd = struct declaration that may contain both an rvalue and copy constructor + ctor = constructor that will be checked if it is an rvalue constructor ti = template instance the ctor is part of Return: - `false` if ctor is not an rvalue constructor or if `sd` does not contain a - copy constructor. `true` otherwise + `true` if sd has a copy constructor and ctor is an rvalue constructor */ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti) { - auto loc = ctor.loc; - auto tf = cast(TypeFunction)ctor.type; - auto dim = tf.parameterList.length; - if (sd && sd.hasCopyCtor && (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))) + //printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars()); + /* cannot use ctor.isMoveCtor because semantic pass may not have been run yet, + * so use isRvalueConstructor() + */ + if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor)) + { + .error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars()); + .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`", + ti.toPrettyChars(), sd.toChars()); + + return true; + } + + return false; +} + +/************************************************ + * Check if ctor is an rvalue constructor. + * A constructor that receives a single parameter of the same type as + * `Unqual!typeof(this)` is an rvalue constructor. + * Params: + * sd = struct that ctor is a member of + * ctor = constructor to test + * Returns: + * true if it is an rvalue constructor + */ +bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor) +{ + // note commonality with setting isMoveCtor in the semantic code for CtorDeclaration + auto tf = ctor.type.isTypeFunction(); + const dim = tf.parameterList.length; + if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)) { auto param = tf.parameterList[0]; if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) { - .error(loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars()); - .errorSupplemental(ti.loc, "Template instance `%s` creates an rvalue constructor for `struct %s`", - ti.toPrettyChars(), sd.toChars()); - return true; } } - return false; } @@ -286,6 +313,7 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem */ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false) { + //printf("resolveAliasThis() %s\n", toChars(e)); import dmd.typesem : dotExp; for (AggregateDeclaration ad = isAggregate(e.type); ad;) { @@ -294,7 +322,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find Loc loc = e.loc; Type tthis = (e.op == EXP.type ? e.type : null); const flags = cast(DotExpFlag) (DotExpFlag.noAliasThis | (gag * DotExpFlag.gag)); - uint olderrors = gag ? global.startGagging() : 0; + const olderrors = gag ? global.startGagging() : 0; e = dotExp(ad.type, sc, e, ad.aliasthis.ident, flags); if (!e || findOnly) return gag && global.endGagging(olderrors) ? null : e; @@ -368,7 +396,7 @@ Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool find * Returns: * Whether the alias this was reported as deprecated. */ -private bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc) +private bool checkDeprecatedAliasThis(AliasThis at, Loc loc, Scope* sc) { if (global.params.useDeprecated != DiagnosticReporting.off && at.isDeprecated() && !sc.isDeprecated()) @@ -396,7 +424,7 @@ private bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc } // Save the scope and defer semantic analysis on the Dsymbol. -void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope *scx) +void deferDsymbolSemantic(Scope* sc, Dsymbol s, Scope* scx) { s._scope = scx ? scx : sc.copy(); s._scope.setNoFree(); @@ -595,12 +623,31 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } if (dsym.storage_class & STC.extern_ && dsym._init) - .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars); + { + if (sc.inCfile) + { + // https://issues.dlang.org/show_bug.cgi?id=24447 + // extern int x = 3; is allowed in C + dsym.storage_class &= ~STC.extern_; + } + else + .error(dsym.loc, "%s `%s` extern symbols cannot have initializers", dsym.kind, dsym.toPrettyChars); + + } AggregateDeclaration ad = dsym.isThis(); if (ad) dsym.storage_class |= ad.storage_class & STC.TYPECTOR; + if ((dsym.storage_class & STC.auto_) && (dsym.storage_class & STC.ref_)) + { + if (!(dsym.storage_class & STC.autoref)) + { + .error(dsym.loc, "%s `%s` - `auto ref` variable must have `auto` and `ref` adjacent", dsym.kind, dsym.toChars()); + dsym.storage_class |= STC.autoref; + } + } + /* If auto type inference, do the inference */ int inferred = 0; @@ -613,12 +660,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool needctfe = (dsym.storage_class & (STC.manifest | STC.static_)) != 0 || !sc.func; if (needctfe) { - sc.flags |= SCOPE.condition; + sc.condition = true; sc = sc.startCTFE(); } //printf("inferring type for %s with init %s\n", dsym.toChars(), dsym._init.toChars()); dsym._init = dsym._init.inferType(sc); - dsym.type = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0).type; + dsym.type = dsym._init.initializerToExpression(null, sc.inCfile).type; if (needctfe) sc = sc.endCTFE(); @@ -670,7 +717,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Calculate type size + safety checks if (dsym.storage_class & STC.gshared && !dsym.isMember()) { - sc.setUnsafe(false, dsym.loc, "__gshared not allowed in safe functions; use shared"); + sc.setUnsafe(false, dsym.loc, "using `__gshared` instead of `shared`"); } Dsymbol parent = dsym.toParent(); @@ -718,7 +765,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor dsym.type = Type.terror; } } - if ((dsym.storage_class & STC.auto_) && !inferred) + if ((dsym.storage_class & STC.auto_) && !inferred && !(dsym.storage_class & STC.autoref)) .error(dsym.loc, "%s `%s` - storage class `auto` has no effect if type is not inferred, did you mean `scope`?", dsym.kind, dsym.toPrettyChars); if (auto tt = tb.isTypeTuple()) @@ -727,7 +774,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor * and add those. */ size_t nelems = Parameter.dim(tt.arguments); - Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0) : null; + Expression ie = (dsym._init && !dsym._init.isVoidInitializer()) ? dsym._init.initializerToExpression(null, sc.inCfile) : null; if (ie) ie = ie.expressionSemantic(sc); if (nelems > 0 && ie) @@ -765,7 +812,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } else if (isAliasThisTuple(e)) { - auto v = copyToTemp(0, "__tup", e); + auto v = copyToTemp(STC.none, "__tup", e); v.dsymbolSemantic(sc); auto ve = new VarExp(dsym.loc, v); ve.type = e.type; @@ -849,7 +896,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor else ti = dsym._init ? dsym._init.syntaxCopy() : null; - StorageClass storage_class = STC.temp | dsym.storage_class; + STC storage_class = STC.temp | dsym.storage_class; if ((dsym.storage_class & STC.parameter) && (arg.storageClass & STC.parameter)) storage_class |= arg.storageClass; auto v = new VarDeclaration(dsym.loc, arg.type, id, ti, storage_class); @@ -888,7 +935,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor else if (dsym.type.isWild()) dsym.storage_class |= STC.wild; - if (StorageClass stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)) + if (STC stc = dsym.storage_class & (STC.synchronized_ | STC.override_ | STC.abstract_ | STC.final_)) { if (stc == STC.final_) .error(dsym.loc, "%s `%s` cannot be `final`, perhaps you meant `const`?", dsym.kind, dsym.toPrettyChars); @@ -908,7 +955,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dsym.storage_class & STC.scope_) { - StorageClass stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared); + STC stc = dsym.storage_class & (STC.static_ | STC.extern_ | STC.manifest | STC.gshared); if (stc) { OutBuffer buf; @@ -977,7 +1024,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } // If it's a member template AggregateDeclaration ad2 = ti.tempdecl.isMember(); - if (ad2 && dsym.storage_class != STC.undefined_) + if (ad2 && dsym.storage_class != STC.none) { .error(dsym.loc, "%s `%s` - cannot use template to add field to aggregate `%s`", dsym.kind, dsym.toPrettyChars, ad2.toChars()); } @@ -1000,9 +1047,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } - if ((dsym.storage_class & (STC.ref_ | STC.parameter | STC.foreach_ | STC.temp | STC.result)) == STC.ref_ && dsym.ident != Id.This) + if ((dsym.storage_class & (STC.ref_ | STC.field)) == (STC.ref_ | STC.field) && dsym.ident != Id.This) { - .error(dsym.loc, "%s `%s` - only parameters, functions and `foreach` declarations can be `ref`", dsym.kind, dsym.toPrettyChars); + .error(dsym.loc, "%s `%s` - field declarations cannot be `ref`", dsym.kind, dsym.toPrettyChars); } if (dsym.type.hasWild()) @@ -1051,8 +1098,27 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } } + bool dsymIsRef = (dsym.storage_class & (STC.ref_ | STC.field | STC.parameter | STC.temp | STC.foreach_)) == STC.ref_; + if (dsymIsRef) + { + if (!dsym._init && dsym.ident != Id.This) + { + if (dsym.storage_class & STC.autoref) + { + dsymIsRef = false; + dsym.storage_class &= ~STC.ref_; + } + else + .error(dsym.loc, "%s `%s` - initializer is required for `ref` variable", dsym.kind, dsym.toPrettyChars); + } + else if (dsym._init.isVoidInitializer()) + { + .error(dsym.loc, "%s `%s` - void initializer not allowed for `ref` variable", dsym.kind, dsym.toPrettyChars); + } + } + FuncDeclaration fd = parent.isFuncDeclaration(); - if (dsym.type.isscope() && !(dsym.storage_class & STC.nodtor)) + if (dsym.type.isScopeClass() && !(dsym.storage_class & STC.nodtor)) { if (dsym.storage_class & (STC.field | STC.out_ | STC.ref_ | STC.static_ | STC.manifest | STC.gshared) || !fd) { @@ -1072,24 +1138,30 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // Calculate type size + safety checks if (sc && sc.func) { - if (dsym._init && dsym._init.isVoidInitializer()) + if (dsym._init && dsym._init.isVoidInitializer() && !(dsym.storage_class & STC.temp)) { + // Don't do these checks for STC.temp vars because the generated `opAssign` + // for a struct with postblit and destructor void initializes a temporary + // __swap variable, which can be trusted if (dsym.type.hasPointers()) // also computes type size sc.setUnsafe(false, dsym.loc, - "`void` initializers for pointers not allowed in safe functions"); + "`void` initializing a pointer"); else if (dsym.type.hasInvariant()) sc.setUnsafe(false, dsym.loc, - "`void` initializers for structs with invariants are not allowed in safe functions"); - else if (dsym.type.hasSystemFields()) + "`void` initializing a struct with an invariant"); + else if (dsym.type.toBasetype().ty == Tbool) + sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc, + "void intializing a bool (which must always be 0 or 1)"); + else if (dsym.type.hasUnsafeBitpatterns()) sc.setUnsafePreview(global.params.systemVariables, false, dsym.loc, - "`void` initializers for `@system` variables not allowed in safe functions"); + "`void` initializing a type with unsafe bit patterns"); } else if (!dsym._init && !(dsym.storage_class & (STC.static_ | STC.extern_ | STC.gshared | STC.manifest | STC.field | STC.parameter)) && dsym.type.hasVoidInitPointers()) { - sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers not allowed in safe functions"); + sc.setUnsafe(false, dsym.loc, "`void` initializers for pointers"); } } @@ -1115,7 +1187,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor bool isBlit = false; uinteger_t sz; - if (sc.flags & SCOPE.Cfile && !dsym._init) + if (sc.inCfile && !dsym._init) { addDefaultCInitializer(dsym); } @@ -1180,7 +1252,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~(STC.TYPECTOR | STC.pure_ | STC.nothrow_ | STC.nogc | STC.ref_ | STC.disable); - if (sc.flags & SCOPE.Cfile && + if (sc.inCfile && dsym.type.isTypeSArray() && dsym.type.isTypeSArray().isIncomplete() && dsym._init.isVoidInitializer() && @@ -1210,12 +1282,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (ai && tb.ty == Taarray) e = ai.toAssocArrayLiteral(); else - e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + e = dsym._init.initializerToExpression(null, sc.inCfile); if (!e) { // Run semantic, but don't need to interpret dsym._init = dsym._init.initializerSemantic(sc, dsym.type, INITnointerpret); - e = dsym._init.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + e = dsym._init.initializerToExpression(null, sc.inCfile); if (!e) { .error(dsym.loc, "%s `%s` is not a static and cannot have static initializer", dsym.kind, dsym.toPrettyChars); @@ -1225,7 +1297,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ei = new ExpInitializer(dsym._init.loc, e); dsym._init = ei; } - else if (sc.flags & SCOPE.Cfile && dsym.type.isTypeSArray() && + else if (sc.inCfile && dsym.type.isTypeSArray() && dsym.type.isTypeSArray().isIncomplete()) { // C11 6.7.9-22 determine the size of the incomplete array, @@ -1240,9 +1312,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ex = (cast(AssignExp)ex).e2; if (auto ne = ex.isNewExp()) { + if (ne.placement) + { + } /* See if initializer is a NewExp that can be allocated on the stack. */ - if (dsym.type.toBasetype().ty == Tclass) + else if (dsym.type.toBasetype().ty == Tclass) { /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak. * https://issues.dlang.org/show_bug.cgi?id=23145 @@ -1251,7 +1326,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { import dmd.escape : setUnsafeDIP1000; const inSafeFunc = sc.func && sc.func.isSafeBypassingInference(); // isSafeBypassingInference may call setUnsafe(). - if (sc.setUnsafeDIP1000(false, dsym.loc, "`scope` allocation of `%s` requires that constructor be annotated with `scope`", dsym)) + if (setUnsafeDIP1000(*sc, false, dsym.loc, "`scope` allocation of `%s` with a non-`scope` constructor", dsym)) errorSupplemental(ne.member.loc, "is the location of the constructor"); } ne.onstack = 1; @@ -1276,21 +1351,67 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Expression exp = ei.exp; Expression e1 = new VarExp(dsym.loc, dsym); - if (isBlit) - exp = new BlitExp(dsym.loc, e1, exp); + + void constructInit(bool isBlit) + { + if (isBlit) + exp = new BlitExp(dsym.loc, e1, exp); + else + exp = new ConstructExp(dsym.loc, e1, exp); + dsym.canassign++; + exp = exp.expressionSemantic(sc); + dsym.canassign--; + } + + if (dsymIsRef) // follow logic similar to typesem.argumentMatchParameter() and statementsem.visitForeach() + { + dsym.storage_class |= STC.nodtor; + exp = exp.expressionSemantic(sc); + Type tp = dsym.type; + Type ta = exp.type; + if (!exp.isLvalue()) + { + if (dsym.storage_class & STC.autoref) + { + dsym.storage_class &= ~STC.ref_; + constructInit(isBlit); + } + else + { + .error(dsym.loc, "rvalue `%s` cannot be assigned to `ref %s`", exp.toChars(), dsym.toChars()); + exp = ErrorExp.get(); + } + } + else if (!ta.constConv(tp)) + { + if (dsym.storage_class & STC.autoref) + { + dsym.storage_class &= ~STC.ref_; + constructInit(false); + } + else + { + .error(dsym.loc, "type `%s` cannot be assigned to `ref %s %s`", ta.toChars(), tp.toChars(), dsym.toChars()); + exp = ErrorExp.get(); + } + } + else + { + constructInit(false); + } + } else - exp = new ConstructExp(dsym.loc, e1, exp); - dsym.canassign++; - exp = exp.expressionSemantic(sc); - dsym.canassign--; - exp = exp.optimize(WANTvalue); + { + constructInit(isBlit); + } + if (exp.op == EXP.error) { dsym._init = new ErrorInitializer(); ei = null; } else - ei.exp = exp; + ei.exp = exp.optimize(WANTvalue); } else { @@ -1314,7 +1435,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } else if (dsym.storage_class & (STC.const_ | STC.immutable_ | STC.manifest) || dsym.type.isConst() || dsym.type.isImmutable() || - sc.flags & SCOPE.Cfile) + sc.inCfile) { /* Because we may need the results of a const declaration in a * subsequent type, such as an array dimension, before semantic2() @@ -1325,7 +1446,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor */ if (!inferred) { - uint errors = global.errors; + const errors = global.errors; dsym.inuse++; // Bug 20549. Don't try this on modules or packages, syntaxCopy // could crash (inf. recursion) on a mod/pkg referencing itself @@ -1459,7 +1580,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (dsym.errors) return; - if (!(global.params.bitfields || sc.flags & SCOPE.Cfile)) + if (!(sc.previews.bitfields || sc.inCfile)) { version (IN_GCC) .error(dsym.loc, "%s `%s` use `-fpreview=bitfields` for bitfield support", dsym.kind, dsym.toPrettyChars); @@ -1476,7 +1597,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor auto width = dsym.width.expressionSemantic(sc); sc = sc.endCTFE(); width = width.ctfeInterpret(); - if (!dsym.type.isintegral()) + if (!dsym.type.isIntegral()) { // C11 6.7.2.1-5 error(width.loc, "bit-field type `%s` is not an integer type", dsym.type.toChars()); @@ -1507,6 +1628,8 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(Import imp) { + timeTraceBeginEvent(TimeTraceEventType.sema1Import); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Import, imp); static if (LOG) { printf("Import::semantic('%s') %s\n", imp.toPrettyChars(), imp.id.toChars()); @@ -1547,156 +1670,91 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor imp.mod.checkImportDeprecation(imp.loc, sc); } } - if (imp.mod) + if (!imp.mod) { - // Modules need a list of each imported module - - // if inside a template instantiation, the instantianting - // module gets the import. - // https://issues.dlang.org/show_bug.cgi?id=17181 - Module importer = sc._module; - if (sc.minst && sc.tinst) - { - importer = sc.minst; - if (!sc.tinst.importedModules.contains(imp.mod)) - sc.tinst.importedModules.push(imp.mod); - } - //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars()); - if (!importer.aimports.contains(imp.mod)) - importer.aimports.push(imp.mod); - - if (sc.explicitVisibility) - imp.visibility = sc.visibility; - - if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import - { - ScopeDsymbol scopesym = sc.getScopesym(); + imp.semanticRun = PASS.semanticdone; + addImportDep(global.params.moduleDeps, imp, sc._module); + } - if (!imp.isstatic) - { - scopesym.importScope(imp.mod, imp.visibility); - } + // Modules need a list of each imported module + // if inside a template instantiation, the instantianting + // module gets the import. + // https://issues.dlang.org/show_bug.cgi?id=17181 + Module importer = sc._module; + if (sc.minst && sc.tinst) + { + importer = sc.minst; + if (!sc.tinst.importedModules.contains(imp.mod)) + sc.tinst.importedModules.push(imp.mod); + } + //printf("%s imports %s\n", importer.toChars(), imp.mod.toChars()); + if (!importer.aimports.contains(imp.mod)) + importer.aimports.push(imp.mod); - imp.addPackageAccess(scopesym); - } + if (sc.explicitVisibility) + imp.visibility = sc.visibility; - // if a module has errors it means that parsing has failed. - if (!imp.mod.errors) - imp.mod.dsymbolSemantic(null); + if (!imp.aliasId && !imp.names.length) // neither a selective nor a renamed import + { + ScopeDsymbol scopesym = sc.getScopesym(); - if (imp.mod.needmoduleinfo) + if (!imp.isstatic) { - //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars()); - importer.needmoduleinfo = 1; + scopesym.importScope(imp.mod, imp.visibility); } - sc = sc.push(imp.mod); - sc.visibility = imp.visibility; - for (size_t i = 0; i < imp.aliasdecls.length; i++) - { - AliasDeclaration ad = imp.aliasdecls[i]; - //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope); - Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports); - if (sym) - { - import dmd.access : symbolIsVisible; - if (!symbolIsVisible(sc, sym) && !sym.errors) - { - .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars, - imp.names[i].toChars(), sc._module.toChars()); - sym.errors = true; - } - ad.dsymbolSemantic(sc); - // If the import declaration is in non-root module, - // analysis of the aliased symbol is deferred. - // Therefore, don't see the ad.aliassym or ad.type here. - } - else - { - Dsymbol s = imp.mod.search_correct(imp.names[i]); - // https://issues.dlang.org/show_bug.cgi?id=23908 - // Don't suggest symbols from the importer's module - if (s && s.parent != importer) - .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars()); - else - .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars()); - ad.type = Type.terror; - } - } - sc = sc.pop(); + + imp.addPackageAccess(scopesym); } - imp.semanticRun = PASS.semanticdone; + // if a module has errors it means that parsing has failed. + if (!imp.mod.errors) + imp.mod.dsymbolSemantic(null); - // object self-imports itself, so skip that - // https://issues.dlang.org/show_bug.cgi?id=7547 - // don't list pseudo modules __entrypoint.d, __main.d - // https://issues.dlang.org/show_bug.cgi?id=11117 - // https://issues.dlang.org/show_bug.cgi?id=11164 - if (global.params.moduleDeps.buffer is null || (imp.id == Id.object && sc._module.ident == Id.object) || - strcmp(sc._module.ident.toChars(), "__main") == 0) - return; + if (imp.mod.needmoduleinfo) + { + //printf("module4 %s because of %s\n", importer.toChars(), imp.mod.toChars()); + importer.needmoduleinfo = 1; + } - /* The grammar of the file is: - * ImportDeclaration - * ::= BasicImportDeclaration [ " : " ImportBindList ] [ " -> " - * ModuleAliasIdentifier ] "\n" - * - * BasicImportDeclaration - * ::= ModuleFullyQualifiedName " (" FilePath ") : " Protection|"string" - * " [ " static" ] : " ModuleFullyQualifiedName " (" FilePath ")" - * - * FilePath - * - any string with '(', ')' and '\' escaped with the '\' character - */ - OutBuffer* ob = global.params.moduleDeps.buffer; - Module imod = sc._module; - if (!global.params.moduleDeps.name) - ob.writestring("depsImport "); - ob.writestring(imod.toPrettyChars()); - ob.writestring(" ("); - escapePath(ob, imod.srcfile.toChars()); - ob.writestring(") : "); - // use visibility instead of sc.visibility because it couldn't be - // resolved yet, see the comment above - visibilityToBuffer(*ob, imp.visibility); - ob.writeByte(' '); - if (imp.isstatic) - { - stcToBuffer(*ob, STC.static_); - ob.writeByte(' '); - } - ob.writestring(": "); - foreach (pid; imp.packages) - { - ob.printf("%s.", pid.toChars()); - } - ob.writestring(imp.id.toString()); - ob.writestring(" ("); - if (imp.mod) - escapePath(ob, imp.mod.srcfile.toChars()); - else - ob.writestring("???"); - ob.writeByte(')'); - foreach (i, name; imp.names) + sc = sc.push(imp.mod); + sc.visibility = imp.visibility; + for (size_t i = 0; i < imp.aliasdecls.length; i++) { - if (i == 0) - ob.writeByte(':'); - else - ob.writeByte(','); - Identifier _alias = imp.aliases[i]; - if (!_alias) + AliasDeclaration ad = imp.aliasdecls[i]; + //printf("\tImport %s alias %s = %s, scope = %p\n", toPrettyChars(), aliases[i].toChars(), names[i].toChars(), ad._scope); + Dsymbol sym = imp.mod.search(imp.loc, imp.names[i], SearchOpt.ignorePrivateImports); + if (sym) { - ob.printf("%s", name.toChars()); - _alias = name; + import dmd.access : symbolIsVisible; + if (!symbolIsVisible(sc, sym) && !sym.errors) + { + .error(imp.loc, "%s `%s` member `%s` is not visible from module `%s`", imp.mod.kind, imp.mod.toPrettyChars, + imp.names[i].toChars(), sc._module.toChars()); + sym.errors = true; + } + ad.dsymbolSemantic(sc); + // If the import declaration is in non-root module, + // analysis of the aliased symbol is deferred. + // Therefore, don't see the ad.aliassym or ad.type here. } else - ob.printf("%s=%s", _alias.toChars(), name.toChars()); + { + Dsymbol s = imp.mod.search_correct(imp.names[i]); + // https://issues.dlang.org/show_bug.cgi?id=23908 + // Don't suggest symbols from the importer's module + if (s && s.parent != importer) + .error(imp.loc, "%s `%s` import `%s` not found, did you mean %s `%s`?", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars(), s.kind(), s.toPrettyChars()); + else + .error(imp.loc, "%s `%s` import `%s` not found", imp.mod.kind, imp.mod.toPrettyChars, imp.names[i].toChars()); + ad.type = Type.terror; + } } - if (imp.aliasId) - ob.printf(" -> %s", imp.aliasId.toChars()); - ob.writenl(); + sc = sc.pop(); + + imp.semanticRun = PASS.semanticdone; + addImportDep(global.params.moduleDeps, imp, sc._module); } void attribSemantic(AttribDeclaration ad) @@ -1706,20 +1764,25 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ad.semanticRun = PASS.semantic; Dsymbols* d = ad.include(sc); //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); - if (d) + if (!d) { - Scope* sc2 = ad.newScope(sc); - bool errors; - for (size_t i = 0; i < d.length; i++) - { - Dsymbol s = (*d)[i]; - s.dsymbolSemantic(sc2); - errors |= s.errors; - } - ad.errors |= errors; - if (sc2 != sc) - sc2.pop(); + ad.semanticRun = PASS.semanticdone; + return; + } + + Scope* sc2 = ad.newScope(sc); + bool errors; + for (size_t i = 0; i < d.length; i++) + { + Dsymbol s = (*d)[i]; + s.dsymbolSemantic(sc2); + errors |= s.errors; } + if (errors) + ad.errors = true; + if (sc2 != sc) + sc2.pop(); + ad.semanticRun = PASS.semanticdone; } @@ -1747,7 +1810,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.gshared); sc.inunion = scd.isunion ? scd : null; - sc.flags = 0; + sc.resetAllFlags(); for (size_t i = 0; i < scd.decl.length; i++) { Dsymbol s = (*scd.decl)[i]; @@ -1781,7 +1844,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { //printf("MixinDeclaration::compileIt(loc = %d) %s\n", cd.loc.linnum, cd.exp.toChars()); OutBuffer buf; - if (expressionsToString(buf, sc, cd.exps)) + if (expressionsToString(buf, sc, cd.exps, cd.loc, null, true)) return null; const errors = global.errors; @@ -1789,9 +1852,9 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto loc = adjustLocForMixin(str, cd.loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, cd.loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); auto d = p.parseDeclDefs(0); @@ -1837,8 +1900,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor const sident = se.toStringz(); if (!sident.length || !Identifier.isValidIdentifier(sident)) { - error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%.*s`", - cast(int)sident.length, sident.ptr); + error(ns.exp.loc, "expected valid identifier for C++ namespace but got `%s`", se.toErrMsg()); return null; } else @@ -1904,6 +1966,25 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { if (sa.semanticRun < PASS.semanticdone) sa.semanticRun = PASS.semanticdone; + else + return; + + // https://issues.dlang.org/show_bug.cgi?id=24645 + // This is a short-circuit. Usually, static assert conditions are evaluated + // in semantic2, but it's not uncommon to use this pattern: + // --- + // version(X) + // {} + // else + // static assert(false, "unsupported platform"); + // --- + // However, without this short-circuit, the static assert error may get drowned + // out by subsequent semantic1 (import) errors. Only short-circuit at module scope though, + // inside mixin templates you want an instantiation trace (which you don't get here). + if (sc.parent && sc.parent.isModule()) + if (auto i = sa.exp.isIntegerExp()) + if (i.toInteger() == 0) + staticAssertFail(sa, sc); } override void visit(DebugSymbol ds) @@ -1929,6 +2010,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { if (m.semanticRun != PASS.initial) return; + + timeTraceBeginEvent(TimeTraceEventType.sema1Module); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema1Module, m); + //printf("+Module::semantic(this = %p, '%s'): parent = %p\n", this, toChars(), parent); m.semanticRun = PASS.semantic; // Note that modules get their own scope, from scratch. @@ -2141,7 +2226,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor tm.argsym.parent = scy.parent; Scope* argscope = scy.push(tm.argsym); - uint errorsave = global.errors; + const errorsave = global.errors; // Declare each template parameter as an alias for the argument type tm.declareParameters(argscope); @@ -2286,7 +2371,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ns.semanticRun = PASS.semantic; ns.parent = sc.parent; // Link does not matter here, if the UDA is present it will error - UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); + checkGNUABITag(ns, LINK.cpp); if (!ns.members) { @@ -2326,7 +2411,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(CtorDeclaration ctd) { - //printf("CtorDeclaration::semantic() %s\n", toChars()); + //printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars()); if (ctd.semanticRun >= PASS.semanticdone) return; if (ctd._scope) @@ -2359,6 +2444,24 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc.stc &= ~STC.static_; // not a static constructor funcDeclarationSemantic(sc, ctd); + // Check short constructor: this() => expr; + if (ctd.fbody) + { + if (auto s = ctd.fbody.isExpStatement()) + { + if (s.exp) + { + auto ce = s.exp.isCallExp(); + // check this/super before semantic + if (!ce || (!ce.e1.isThisExp() && !ce.e1.isSuperExp())) + { + s.exp = s.exp.expressionSemantic(sc); + if (s.exp.type.ty != Tvoid) + error(s.loc, "can only return void expression, `this` call or `super` call from constructor"); + } + } + } + } sc.pop(); @@ -2409,12 +2512,17 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor } else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))) { - //printf("tf: %s\n", tf.toChars()); + //printf("tf: %s\n", toChars(tf)); auto param = tf.parameterList[0]; - if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) + if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf()) { - //printf("copy constructor\n"); - ctd.isCpCtor = true; + //printf("copy constructor %p\n", ctd); + assert(!ctd.isCpCtor && !ctd.isMoveCtor); + if (param.storageClass & STC.ref_) + ctd.isCpCtor = true; // copy constructor + else + ctd.isMoveCtor = true; // move constructor + assert(!(ctd.isCpCtor && ctd.isMoveCtor)); } } } @@ -2528,50 +2636,53 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc.pop(); } - override void visit(StaticCtorDeclaration scd) + void visitStaticCDtorDeclaration(FuncDeclaration sd, bool isDestructor) { - //printf("StaticCtorDeclaration::semantic()\n"); - if (scd.semanticRun >= PASS.semanticdone) + if (sd.semanticRun >= PASS.semanticdone) return; - if (scd._scope) + if (sd._scope) { - sc = scd._scope; - scd._scope = null; + sc = sd._scope; + sd._scope = null; } - - scd.parent = sc.parent; - Dsymbol p = scd.parent.pastMixin(); + sd.parent = sc.parent; + Dsymbol p = sd.parent.pastMixin(); + const bool isShared = !!(sd.isSharedStaticDtorDeclaration() || sd.isSharedStaticCtorDeclaration()); + const(char)* what = isDestructor ? "destructor" : "constructor"; if (!p.isScopeDsymbol()) { - const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : ""); - error(scd.loc, "`%sstatic` constructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars()); - scd.type = Type.terror; - scd.errors = true; + const(char)* s = isShared ? "shared " : ""; + error(sd.loc, "`%sstatic` %s can only be member of module/aggregate/template, not %s `%s`", s, what, p.kind(), p.toChars()); + sd.type = Type.terror; + sd.errors = true; return; } - if (!scd.type) - scd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, scd.storage_class); - /* If the static ctor appears within a template instantiation, + if (!sd.type) + sd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sd.storage_class); + + /* If the static [dc]tor appears within a template instantiation, * it could get called multiple times by the module constructors * for different modules. Thus, protect it with a gate. */ - if (scd.isInstantiated() && scd.semanticRun < PASS.semantic) + if (sd.isInstantiated() && sd.semanticRun < PASS.semantic) { /* Add this prefix to the constructor: * ``` * static int gate; - * if (++gate != 1) return; + * if ([--|++]gate != [0|1]) return; // dependant on ctor/dtor * ``` * or, for shared constructor: * ``` * shared int gate; - * if (core.atomic.atomicOp!"+="(gate, 1) != 1) return; + * enum op = isDestructor ? "-=" : "+="; + * enum cmp = isDestructor ? 0 : 1; + * if (core.atomic.atomicOp!op(gate, 1) != cmp) return; * ``` */ - const bool isShared = !!scd.isSharedStaticCtorDeclaration(); + auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); - v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0); + v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : STC.none); auto sa = new Statements(); Statement s = new ExpStatement(Loc.initial, v); @@ -2580,49 +2691,56 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor Expression e; if (isShared) { - e = doAtomicOp("+=", v.ident, IntegerExp.literal!(1)); + e = doAtomicOp(isDestructor ? "-=" : "+=", v.ident, IntegerExp.literal!(1)); if (e is null) { - .error(scd.loc, "%s `%s` shared static constructor within a template require `core.atomic : atomicOp` to be present", scd.kind, scd.toPrettyChars); + .error(sd.loc, "%s `%s` shared static %s within a template require `core.atomic : atomicOp` to be present", sd.kind, sd.toPrettyChars, what); return; } } else { + IntegerExp one = isDestructor ? IntegerExp.literal!(-1) : IntegerExp.literal!(1); e = new AddAssignExp( - Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!1); + Loc.initial, new IdentifierExp(Loc.initial, v.ident), one); } - - e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!1); + IntegerExp cmp = isDestructor ? IntegerExp.literal!0 : IntegerExp.literal!1; + e = new EqualExp(EXP.notEqual, Loc.initial, e, cmp); s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial); sa.push(s); - if (scd.fbody) - sa.push(scd.fbody); + if (sd.fbody) + sa.push(sd.fbody); - scd.fbody = new CompoundStatement(Loc.initial, sa); + sd.fbody = new CompoundStatement(Loc.initial, sa); + if (isDestructor) + (cast(StaticDtorDeclaration)sd).vgate = v; } - const LINK save = sc.linkage; if (save != LINK.d) { - const(char)* s = (scd.isSharedStaticCtorDeclaration() ? "shared " : ""); - deprecation(scd.loc, "`%sstatic` constructor can only be of D linkage", s); + const(char)* s = isShared ? "shared " : ""; + deprecation(sd.loc, "`%sstatic` %s can only be of D linkage", s, what); // Just correct it sc.linkage = LINK.d; } - funcDeclarationSemantic(sc, scd); + funcDeclarationSemantic(sc, sd); sc.linkage = save; // We're going to need ModuleInfo - Module m = scd.getModule(); + Module m = sd.getModule(); if (!m) m = sc._module; if (m) { m.needmoduleinfo = 1; - //printf("module1 %s needs moduleinfo\n", m.toChars()); + //printf("module2 %s needs moduleinfo\n", m.toChars()); } + } + override void visit(StaticCtorDeclaration scd) + { + //printf("StaticCtorDeclaration::semantic()\n"); + visitStaticCDtorDeclaration(scd, false); foreachUda(scd, sc, (Expression e) { import dmd.attrib : isEnumAttribute; @@ -2645,100 +2763,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor override void visit(StaticDtorDeclaration sdd) { - if (sdd.semanticRun >= PASS.semanticdone) - return; - if (sdd._scope) - { - sc = sdd._scope; - sdd._scope = null; - } - - sdd.parent = sc.parent; - Dsymbol p = sdd.parent.pastMixin(); - if (!p.isScopeDsymbol()) - { - const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : ""); - error(sdd.loc, "`%sstatic` destructor can only be member of module/aggregate/template, not %s `%s`", s, p.kind(), p.toChars()); - sdd.type = Type.terror; - sdd.errors = true; - return; - } - if (!sdd.type) - sdd.type = new TypeFunction(ParameterList(), Type.tvoid, LINK.d, sdd.storage_class); - - /* If the static ctor appears within a template instantiation, - * it could get called multiple times by the module constructors - * for different modules. Thus, protect it with a gate. - */ - if (sdd.isInstantiated() && sdd.semanticRun < PASS.semantic) - { - /* Add this prefix to the constructor: - * ``` - * static int gate; - * if (--gate != 0) return; - * ``` - * or, for shared constructor: - * ``` - * shared int gate; - * if (core.atomic.atomicOp!"-="(gate, 1) != 0) return; - * ``` - */ - const bool isShared = !!sdd.isSharedStaticDtorDeclaration(); - auto v = new VarDeclaration(Loc.initial, Type.tint32, Id.gate, null); - v.storage_class = STC.temp | STC.static_ | (isShared ? STC.shared_ : 0); - - auto sa = new Statements(); - Statement s = new ExpStatement(Loc.initial, v); - sa.push(s); - - Expression e; - if (isShared) - { - e = doAtomicOp("-=", v.ident, IntegerExp.literal!(1)); - if (e is null) - { - .error(sdd.loc, "%s `%s` shared static destructo within a template require `core.atomic : atomicOp` to be present", sdd.kind, sdd.toPrettyChars); - return; - } - } - else - { - e = new AddAssignExp( - Loc.initial, new IdentifierExp(Loc.initial, v.ident), IntegerExp.literal!(-1)); - } - - e = new EqualExp(EXP.notEqual, Loc.initial, e, IntegerExp.literal!0); - s = new IfStatement(Loc.initial, null, e, new ReturnStatement(Loc.initial, null), null, Loc.initial); - - sa.push(s); - if (sdd.fbody) - sa.push(sdd.fbody); - - sdd.fbody = new CompoundStatement(Loc.initial, sa); - - sdd.vgate = v; - } - - const LINK save = sc.linkage; - if (save != LINK.d) - { - const(char)* s = (sdd.isSharedStaticDtorDeclaration() ? "shared " : ""); - deprecation(sdd.loc, "`%sstatic` destructor can only be of D linkage", s); - // Just correct it - sc.linkage = LINK.d; - } - funcDeclarationSemantic(sc, sdd); - sc.linkage = save; - - // We're going to need ModuleInfo - Module m = sdd.getModule(); - if (!m) - m = sc._module; - if (m) - { - m.needmoduleinfo = 1; - //printf("module2 %s needs moduleinfo\n", m.toChars()); - } + visitStaticCDtorDeclaration(sdd, true); } override void visit(InvariantDeclaration invd) @@ -2775,7 +2800,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor sc = sc.push(); sc.stc &= ~STC.static_; // not a static invariant sc.stc |= STC.const_; // invariant() is always const - sc.flags = (sc.flags & ~SCOPE.contract) | SCOPE.invariant_; + sc.contract = Contract.invariant_; sc.linkage = LINK.d; funcDeclarationSemantic(sc, invd); @@ -2852,7 +2877,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (sd.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+StructDeclaration::semantic(this=%p, '%s', sizeok = %d)\n", sd, sd.toPrettyChars(), sd.sizeok); Scope* scx = null; @@ -2919,7 +2944,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return; sd.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(sd, sc.linkage); + checkGNUABITag(sd, sc.linkage); if (!sd.members) // if opaque declaration { @@ -2941,7 +2966,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor */ sd.members.foreachDsymbol( s => s.setScope(sc2) ); sd.members.foreachDsymbol( s => s.importAll(sc2) ); - sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); sd.errors |= s.errors; } ); + sd.members.foreachDsymbol( (s) { s.dsymbolSemantic(sc2); if (sd.errors) s.errors = true; } ); if (sd.errors) sd.type = Type.terror; @@ -2988,13 +3013,42 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor buildDtors(sd, sc2); - sd.hasCopyCtor = buildCopyCtor(sd, sc2); + bool hasCopyCtor; + bool hasMoveCtor; + bool needCopyCtor; + bool needMoveCtor; + needCopyOrMoveCtor(sd, hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor); + //printf("%s hasCopy %d hasMove %d needCopy %d needMove %d\n", sd.toChars(), hasCopyCtor, hasMoveCtor, needCopyCtor, needMoveCtor); + + /* When generating a move ctor, generate a copy ctor too, otherwise + * https://github.com/s-ludwig/taggedalgebraic/issues/75 + */ + if (0 && needMoveCtor && !hasCopyCtor) + { + needCopyCtor = true; + } + + if (needCopyCtor) + { + assert(hasCopyCtor == false); + buildCopyOrMoveCtor(sd, sc2, false); // build copy constructor + hasCopyCtor = true; + } + if (needMoveCtor) + { + assert(hasMoveCtor == false); + buildCopyOrMoveCtor(sd, sc2, true); // build move constructor + hasMoveCtor = true; + } + sd.hasCopyCtor = hasCopyCtor; + sd.hasMoveCtor = hasMoveCtor; + sd.postblit = buildPostBlit(sd, sc2); buildOpAssign(sd, sc2); buildOpEquals(sd, sc2); - if (!(sc2.flags & SCOPE.Cfile) && + if (!sc2.inCfile && global.params.useTypeInfo && Type.dtypeinfo) // these functions are used for TypeInfo { sd.xeq = buildXopEquals(sd, sc2); @@ -3011,10 +3065,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (sd.ctor) { - Dsymbol scall = sd.search(Loc.initial, Id.call); + Dsymbol scall = sd.search(Loc.initial, Id.opCall); if (scall) { - uint xerrors = global.startGagging(); + const xerrors = global.startGagging(); sc = sc.push(); sc.tinst = null; sc.minst = null; @@ -3099,7 +3153,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (cldec.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+ClassDeclaration.dsymbolSemantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); @@ -3159,7 +3213,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return; } cldec.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(cldec, sc.linkage); + checkGNUABITag(cldec, sc.linkage); checkMustUseReserved(cldec); if (cldec.baseok < Baseok.done) @@ -3379,8 +3433,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor { void badObjectDotD() { - .error(cldec.loc, "%s `%s` missing or corrupt object.d", cldec.kind, cldec.toPrettyChars); - fatal(); + ObjectNotFound(cldec.loc, cldec.ident); } if (!cldec.object || cldec.object.errors) @@ -3634,7 +3687,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor // This is required if other lowerings add code to the generated constructor which // is less strict (e.g. `preview=dtorfields` might introduce a call to a less qualified dtor) - auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, 0, tf); + auto ctor = new CtorDeclaration(cldec.loc, Loc.initial, STC.none, tf); ctor.storage_class |= STC.inference | (fd.storage_class & STC.scope_); ctor.isGenerated = true; ctor.fbody = new CompoundStatement(Loc.initial, new Statements()); @@ -3763,7 +3816,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor //printf("InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); if (idec.semanticRun >= PASS.semanticdone) return; - int errors = global.errors; + const errors = global.errors; //printf("+InterfaceDeclaration.dsymbolSemantic(%s), type = %p\n", toChars(), type); @@ -3872,7 +3925,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor if (!idec.baseclasses.length && sc.linkage == LINK.cpp) idec.classKind = ClassKind.cpp; idec.cppnamespace = sc.namespace; - UserAttributeDeclaration.checkGNUABITag(idec, sc.linkage); + checkGNUABITag(idec, sc.linkage); checkMustUseReserved(idec); if (sc.linkage == LINK.objc) @@ -4093,7 +4146,7 @@ private extern(C++) class AddMemberVisitor : Visitor Scope* sc; ScopeDsymbol sds; - this(Scope* sc, ScopeDsymbol sds) + this(Scope* sc, ScopeDsymbol sds) @safe { this.sc = sc; this.sds = sds; @@ -4131,7 +4184,8 @@ private extern(C++) class AddMemberVisitor : Visitor } // If using C tag/prototype/forward declaration rules - if (sc.flags & SCOPE.Cfile && !dsym.isImport()) + if (sc && sc.inCfile && !dsym.isImport()) + // When merging master, replace with: if (sc && sc.inCfile && !dsym.isImport()) { if (handleTagSymbols(*sc, dsym, s2, sds)) return; @@ -4152,7 +4206,7 @@ private extern(C++) class AddMemberVisitor : Visitor if (sds.isAggregateDeclaration() || sds.isEnumDeclaration()) { if (dsym.ident == Id.__sizeof || - !(sc && sc.flags & SCOPE.Cfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof)) + !(sc && sc.inCfile) && (dsym.ident == Id.__xalignof || dsym.ident == Id._mangleof)) { .error(dsym.loc, "%s `%s` `.%s` property cannot be redefined", dsym.kind, dsym.toPrettyChars, dsym.ident.toChars()); dsym.errors = true; @@ -4303,34 +4357,21 @@ private extern(C++) class AddMemberVisitor : Visitor Module m = sds.isModule(); // Do not add the member to the symbol table, // just make sure subsequent debug declarations work. - if (ds.ident) + if (!m) { - if (!m) - { - .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); - ds.errors = true; - } - else - { - if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident)) - { - .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars); - ds.errors = true; - } - if (!m.debugids) - m.debugids = new Identifiers(); - m.debugids.push(ds.ident); - } + .error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); + ds.errors = true; } else { - if (!m) + if (m.debugidsNot && findCondition(*m.debugidsNot, ds.ident)) { - .error(ds.loc, "%s `%s` level declaration must be at module level", ds.kind, ds.toPrettyChars); + .error(ds.loc, "%s `%s` defined after use", ds.kind, ds.toPrettyChars); ds.errors = true; } - else - m.debuglevel = ds.level; + if (!m.debugids) + m.debugids = new Identifiers(); + m.debugids.push(ds.ident); } } @@ -4340,36 +4381,24 @@ private extern(C++) class AddMemberVisitor : Visitor Module m = sds.isModule(); // Do not add the member to the symbol table, // just make sure subsequent debug declarations work. - if (vs.ident) + VersionCondition.checkReserved(vs.loc, vs.ident.toString()); + if (!m) { - VersionCondition.checkReserved(vs.loc, vs.ident.toString()); - if (!m) - { - .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); - vs.errors = true; - } - else - { - if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident)) - { - .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars); - vs.errors = true; - } - if (!m.versionids) - m.versionids = new Identifiers(); - m.versionids.push(vs.ident); - } + .error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); + vs.errors = true; } else { - if (!m) + if (m.versionidsNot && findCondition(*m.versionidsNot, vs.ident)) { - .error(vs.loc, "%s `%s` level declaration must be at module level", vs.kind, vs.toPrettyChars); + .error(vs.loc, "%s `%s` defined after use", vs.kind, vs.toPrettyChars); vs.errors = true; } - else - m.versionlevel = vs.level; + if (!m.versionids) + m.versionids = new Identifiers(); + m.versionids.push(vs.ident); } + } override void visit(Nspace ns) @@ -4428,7 +4457,7 @@ private extern(C++) class AddMemberVisitor : Visitor */ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) { - const bool isCEnum = (sc.flags & SCOPE.Cfile) != 0; // it's an ImportC enum + const bool isCEnum = sc.inCfile; // it's an ImportC enum //printf("addEnumMembersToSymtab(ed: %s added: %d Cfile: %d)\n", ed.toChars(), ed.added, isCEnum); if (ed.added) return; @@ -4459,6 +4488,8 @@ void addEnumMembersToSymtab(EnumDeclaration ed, Scope* sc, ScopeDsymbol sds) * the enum members to both symbol tables. */ em.addMember(sc, ed); // add em to ed's symbol table + if (em.errors) + return; em.addMember(sc, sds); // add em to symbol table that ed is in em.parent = ed; // restore it after previous addMember() changed it } @@ -4724,6 +4755,15 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList visit(cast(Dsymbol)sds); } + override void visit(StructDeclaration sd) + { + // need to visit auto-generated methods as well + if (sd.xeq) visit(sd.xeq); + if (sd.xcmp) visit(sd.xcmp); + if (sd.xhash) visit(sd.xhash); + visit(cast(ScopeDsymbol)sd); + } + override void visit(AttribDeclaration ad) { ad.include(null).foreachDsymbol( s => s.accept(this) ); @@ -4771,7 +4811,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList printf("\timplement template instance %s '%s'\n", tempdecl.parent.toChars(), tempinst.toChars()); printf("\ttempdecl %s\n", tempdecl.toChars()); } - uint errorsave = global.errors; + const errorsave = global.errors; tempinst.inst = tempinst; tempinst.parent = tempinst.enclosing ? tempinst.enclosing : tempdecl.parent; @@ -4799,7 +4839,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList continue; Type t = isType((*tempinst.tiargs)[i]); assert(t); - if (StorageClass stc = ModToStc(t.mod)) + if (STC stc = ModToStc(t.mod)) { //printf("t = %s, stc = x%llx\n", t.toChars(), stc); auto s = new Dsymbols(); @@ -4827,11 +4867,11 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList _scope = _scope.push(tempinst.argsym); _scope.tinst = tempinst; _scope.minst = tempinst.minst; - //scope.stc = 0; + //scope.stc = STC.none; // Declare each template parameter as an alias for the argument type Scope* paramscope = _scope.push(); - paramscope.stc = 0; + paramscope.stc = STC.none; paramscope.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=14169 // template parameters should be public tempinst.declareParameters(paramscope); @@ -4964,7 +5004,7 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList if (global.errors != errorsave) goto Laftersemantic; - if ((sc.func || (sc.flags & SCOPE.fullinst)) && !tempinst.tinst) + if ((sc.func || sc.fullinst) && !tempinst.tinst) { /* If a template is instantiated inside function, the whole instantiation * should be done at that position. But, immediate running semantic3 of @@ -5164,7 +5204,7 @@ void aliasSeqInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDecl { //printf("[%s] aliasSeqInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars()); Scope* paramscope = sc.push(); - paramscope.stc = 0; + paramscope.stc = STC.none; paramscope.visibility = Visibility(Visibility.Kind.public_); TemplateTupleParameter ttp = (*tempdecl.parameters)[0].isTemplateTupleParameter(); @@ -5189,7 +5229,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara { //printf("[%s] aliasInstance.dsymbolSemantic('%s')\n", tempinst.loc.toChars(), tempinst.toChars()); Scope* paramscope = sc.push(); - paramscope.stc = 0; + paramscope.stc = STC.none; paramscope.visibility = Visibility(Visibility.Kind.public_); TemplateTypeParameter ttp = (*tempdecl.parameters)[0].isTemplateTypeParameter(); @@ -5211,7 +5251,7 @@ void aliasInstanceSemantic(TemplateInstance tempinst, Scope* sc, TemplateDeclara // function used to perform semantic on AliasDeclaration void aliasSemantic(AliasDeclaration ds, Scope* sc) { - //printf("AliasDeclaration::semantic() %s\n", ds.toChars()); + //printf("AliasDeclaration::semantic() %s %p\n", ds.toChars(), ds.aliassym); // as DsymbolSemanticVisitor::visit(AliasDeclaration), in case we're called first. // see https://issues.dlang.org/show_bug.cgi?id=21001 @@ -5295,6 +5335,27 @@ void aliasSemantic(AliasDeclaration ds, Scope* sc) // Detect `alias sym = sym;` to prevent creating loops in overload overnext lists. if (auto tident = ds.type.isTypeIdentifier()) { + if (sc.hasEdition(Edition.v2024) && tident.idents.length) + { + alias mt = tident; + Dsymbol pscopesym; + Dsymbol s = sc.search(ds.loc, mt.ident, pscopesym); + // detect `alias a = var1.member_var;` which confusingly resolves to + // `typeof(var1).member_var`, which can be valid inside the aggregate type + if (s && s.isVarDeclaration() && + mt.ident != Id.This && mt.ident != Id._super) + { + s = tident.toDsymbol(sc); + // don't error for `var1.static_symbol` + if (s && s.needThis()) + { + error(ds.loc, "cannot alias %s member `%s` of variable `%s`", + s.kind(), s.toChars(), mt.ident.toChars()); + errorSupplemental(ds.loc, "Use `typeof(%s)` instead to preserve behaviour", + mt.ident.toChars()); + } + } + } // Selective imports are allowed to alias to the same name `import mod : sym=sym`. if (!ds._import) { @@ -5444,7 +5505,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) if (!aliassym) return errorRet(); - if (aliassym.adFlags & Declaration.wasRead) + if (aliassym.wasRead) { if (!aliassym.errors) error(ds.loc, "%s was read, so cannot reassign", aliassym.toChars()); @@ -5452,7 +5513,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) return errorRet(); } - aliassym.adFlags |= Declaration.ignoreRead; // temporarilly allow reads of aliassym + aliassym.ignoreRead = true; // temporarilly allow reads of aliassym const storage_class = sc.stc & (STC.deprecated_ | STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.shared_ | STC.disable); @@ -5576,8 +5637,7 @@ private void aliasAssignSemantic(AliasAssign ds, Scope* sc) aliassym.aliassym = null; } - - aliassym.adFlags &= ~Declaration.ignoreRead; + aliassym.ignoreRead = false; if (aliassym.type && aliassym.type.ty == Terror || global.gag && errors != global.errors) @@ -5840,34 +5900,40 @@ private CallExp doAtomicOp (string op, Identifier var, Expression arg) * Set up loc for a parse of a mixin. Append the input text to the mixin. * Params: * input = mixin text - * loc = location to adjust + * loc = location of expansion + * baseLoc = location to adjust * mixinOut = sink for mixin text data * Returns: * adjusted loc suitable for Parser */ -Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOut) +void adjustLocForMixin(const(char)[] input, Loc loc, ref BaseLoc baseLoc, ref Output mixinOut) { - Loc result; if (mixinOut.doOutput) { const lines = mixinOut.bufferLines; writeMixin(input, loc, mixinOut.bufferLines, *mixinOut.buffer); - result = Loc(mixinOut.name.ptr, lines + 2, loc.charnum); + baseLoc.startLine = lines + 2; + baseLoc.filename = mixinOut.name; + return; } - else if (loc.filename) + + SourceLoc sl = SourceLoc(loc); + if (sl.filename.length == 0) { - /* Create a pseudo-filename for the mixin string, as it may not even exist - * in the source file. - */ - auto len = strlen(loc.filename) + 7 + (loc.linnum).sizeof * 3 + 1; - char* filename = cast(char*)mem.xmalloc(len); - snprintf(filename, len, "%s-mixin-%d", loc.filename, cast(int)loc.linnum); - result = Loc(filename, loc.linnum, loc.charnum); + // Rare case of compiler-generated mixin exp, e.g. __xtoHash + baseLoc.filename = ""; + return; } - else - result = loc; - return result; + + /* Create a pseudo-filename for the mixin string, as it may not even exist + * in the source file. + */ + auto len = sl.filename.length + 7 + (sl.linnum).sizeof * 3 + 1; + char* filename = cast(char*) mem.xmalloc(len); + snprintf(filename, len, "%.*s-mixin-%d", cast(int) sl.filename.length, sl.filename.ptr, cast(int) sl.linnum); + baseLoc.startLine = sl.line; + baseLoc.filename = filename.toDString; } /************************************** @@ -5879,7 +5945,7 @@ Loc adjustLocForMixin(const(char)[] input, ref const Loc loc, ref Output mixinOu * lines = line count to update * output = sink for output */ -private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref OutBuffer buf) +private void writeMixin(const(char)[] s, Loc loc, ref int lines, ref OutBuffer buf) { buf.writestring("// expansion at "); buf.writestring(loc.toChars()); @@ -5916,67 +5982,6 @@ private void writeMixin(const(char)[] s, ref const Loc loc, ref int lines, ref O ++lines; } -/** - * Check signature of `pragma(printf)` function, print error if invalid. - * - * printf/scanf-like functions must be of the form: - * extern (C/C++) T printf([parameters...], const(char)* format, ...); - * or: - * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); - * - * Params: - * funcdecl = function to check - * f = function type - * sc = scope - */ -void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc) -{ - static bool isPointerToChar(Parameter p) - { - if (auto tptr = p.type.isTypePointer()) - { - return tptr.next.ty == Tchar; - } - return false; - } - - bool isVa_list(Parameter p) - { - return p.type.equals(target.va_listType(funcdecl.loc, sc)); - } - - const nparams = f.parameterList.length; - const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars(); - if (!(f.linkage == LINK.c || f.linkage == LINK.cpp)) - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage," - ~" not `extern(%s)`", - p, funcdecl.toChars(), f.linkage.linkageToChars()); - } - if (f.parameterList.varargs == VarArg.variadic) - { - if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1]))) - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have" - ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`", - p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars()); - } - } - else if (f.parameterList.varargs == VarArg.none) - { - if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) && - isVa_list(f.parameterList[nparams - 1]))) - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~ - " signature `%s %s([parameters...], const(char)*, va_list)`", - p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars()); - } - else - { - .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter", - p, funcdecl.toChars()); - } -} - /********************************************* * Search for ident as member of d. * Params: @@ -5987,7 +5992,7 @@ void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* * Returns: * null if not found */ -Dsymbol search(Dsymbol d, const ref Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) +Dsymbol search(Dsymbol d, Loc loc, Identifier ident, SearchOptFlags flags = SearchOpt.all) { scope v = new SearchVisitor(loc, ident, flags); d.accept(v); @@ -6034,7 +6039,7 @@ private extern(C++) class SearchVisitor : Visitor SearchOptFlags flags; Dsymbol result; - this(const ref Loc loc, Identifier ident, SearchOptFlags flags) + this(Loc loc, Identifier ident, SearchOptFlags flags) @safe { this.loc = loc; this.ident = ident; @@ -6250,7 +6255,7 @@ private extern(C++) class SearchVisitor : Visitor VarDeclaration* pvar; Expression ce; - static Dsymbol dollarFromTypeTuple(const ref Loc loc, TypeTuple tt, Scope* sc) + static Dsymbol dollarFromTypeTuple(Loc loc, TypeTuple tt, Scope* sc) { /* $ gives the number of type entries in the type tuple @@ -6511,7 +6516,7 @@ private extern(C++) class SearchVisitor : Visitor return setResult(m.searchCacheSymbol); } - uint errors = global.errors; + const errors = global.errors; m.insearch = true; visit(cast(ScopeDsymbol)m); @@ -6610,7 +6615,7 @@ private extern(C++) class SearchVisitor : Visitor s = b.sym.search(loc, ident, flags); if (!s) continue; - else if (s == cd) // happens if s is nested in this and derives from this + if (s == cd) // happens if s is nested in this and derives from this s = null; else if (!(flags & SearchOpt.ignoreVisibility) && !(s.visible().kind == Visibility.Kind.protected_) && !symbolIsVisible(cd, s)) s = null; @@ -6640,7 +6645,7 @@ private extern(C++) class SetScopeVisitor : Visitor alias visit = typeof(super).visit; Scope* sc; - this(Scope* sc) + this(Scope* sc) @safe { this.sc = sc; } @@ -6783,7 +6788,7 @@ extern(C++) class ImportAllVisitor : Visitor alias visit = typeof(super).visit; Scope* sc; - this(Scope* sc) + this(Scope* sc) @safe { this.sc = sc; } @@ -6862,7 +6867,7 @@ extern(C++) class ImportAllVisitor : Visitor (*m.members)[0].ident != Id.object || (*m.members)[0].isImport() is null)) { - auto im = new Import(Loc.initial, null, Id.object, null, 0); + auto im = new Import(m.loc, null, Id.object, null, 0); m.members.shift(im); } if (!m.symtab) @@ -6949,7 +6954,7 @@ extern (D) bool load(Import imp, Scope* sc) { if (p.isPkgMod == PKG.unknown) { - uint preverrors = global.errors; + const preverrors = global.errors; imp.mod = Module.load(imp.loc, imp.packages, imp.id); if (!imp.mod) p.isPkgMod = PKG.package_; @@ -7026,7 +7031,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor FieldState* fieldState; bool isunion; - this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) + this(AggregateDeclaration ad, FieldState* fieldState, bool isunion) @safe { this.ad = ad; this.fieldState = fieldState; @@ -7115,7 +7120,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor assert(sz != SIZE_INVALID && sz < uint.max); uint memsize = cast(uint)sz; // size of member uint memalignsize = target.fieldalign(t); // size of member for alignment purposes - vd.offset = placeField( + vd.offset = placeField(vd.loc, fieldState.offset, memsize, memalignsize, vd.alignment, ad.structsize, ad.alignsize, @@ -7162,12 +7167,19 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor error(bfd.loc, "bit field width %d is larger than type", bfd.fieldWidth); const style = target.c.bitFieldStyle; + if (style != TargetC.BitFieldStyle.MS && style != TargetC.BitFieldStyle.Gcc_Clang) + assert(0, "unsupported bit-field style"); + + const isMicrosoftStyle = style == TargetC.BitFieldStyle.MS; + const contributesToAggregateAlignment = target.c.contributesToAggregateAlignment(bfd); void startNewField() { if (log) printf("startNewField()\n"); uint alignsize; - if (style == TargetC.BitFieldStyle.Gcc_Clang) + if (isMicrosoftStyle) + alignsize = memsize; // not memalignsize + else { if (bfd.fieldWidth > 32) alignsize = memalignsize; @@ -7178,15 +7190,13 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor else alignsize = 1; } - else - alignsize = memsize; // not memalignsize uint dummy; - bfd.offset = placeField( + bfd.offset = placeField(bfd.loc, fieldState.offset, memsize, alignsize, bfd.alignment, ad.structsize, - (anon && style == TargetC.BitFieldStyle.Gcc_Clang) ? dummy : ad.alignsize, + contributesToAggregateAlignment ? ad.alignsize : dummy, isunion); fieldState.inFlight = true; @@ -7195,64 +7205,30 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor fieldState.fieldSize = memsize; } - if (style == TargetC.BitFieldStyle.Gcc_Clang) - { - if (bfd.fieldWidth == 0) - { - if (!isunion) - { - // Use type of zero width field to align to next field - fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; - } + if (ad.alignsize == 0) + ad.alignsize = 1; + if (!isMicrosoftStyle && contributesToAggregateAlignment && ad.alignsize < memalignsize) + ad.alignsize = memalignsize; - if (ad.alignsize == 0) - ad.alignsize = 1; - if (!anon && - ad.alignsize < memalignsize) - ad.alignsize = memalignsize; - } - else if (style == TargetC.BitFieldStyle.MS) + if (bfd.fieldWidth == 0) { - if (ad.alignsize == 0) - ad.alignsize = 1; - if (bfd.fieldWidth == 0) + if (!isMicrosoftStyle && !isunion) { - if (fieldState.inFlight && !isunion) - { - // documentation says align to next int - //const alsz = cast(uint)Type.tint32.size(); - const alsz = memsize; // but it really does this - fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; + // Use type of zero width field to align to next field + fieldState.offset = (fieldState.offset + memalignsize - 1) & ~(memalignsize - 1); + ad.structsize = fieldState.offset; } - } - else if (style == TargetC.BitFieldStyle.DM) - { - if (anon && bfd.fieldWidth && (!fieldState.inFlight || fieldState.bitOffset == 0)) - return; // this probably should be a bug in DMC - if (ad.alignsize == 0) - ad.alignsize = 1; - if (bfd.fieldWidth == 0) + else if (isMicrosoftStyle && fieldState.inFlight && !isunion) { - if (fieldState.inFlight && !isunion) - { - const alsz = memsize; - fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); - ad.structsize = fieldState.offset; - } - - fieldState.inFlight = false; - return; + // documentation says align to next int + //const alsz = cast(uint)Type.tint32.size(); + const alsz = memsize; // but it really does this + fieldState.offset = (fieldState.offset + alsz - 1) & ~(alsz - 1); + ad.structsize = fieldState.offset; } + + fieldState.inFlight = false; + return; } if (!fieldState.inFlight) @@ -7260,12 +7236,14 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor //printf("not in flight\n"); startNewField(); } - else if (style == TargetC.BitFieldStyle.Gcc_Clang) + else if (!isMicrosoftStyle) { - // If the bit-field spans more units of alignment than its type, - // start a new field at the next alignment boundary. - if (fieldState.bitOffset == fieldState.fieldSize * 8 && - fieldState.bitOffset + bfd.fieldWidth > memalignsize * 8) + // If the bit-field spans more units of alignment than its type + // and is at the alignment boundary, start a new field at the + // next alignment boundary. This affects when offsetof reports + // a higher number and bitoffsetof starts at zero again. + if (fieldState.bitOffset % (memalignsize * 8) == 0 && + fieldState.bitOffset + bfd.fieldWidth > memsize * 8) { if (log) printf("more units of alignment than its type\n"); startNewField(); // the bit field is full @@ -7273,18 +7251,17 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor else { // if alignment boundary is crossed - uint start = fieldState.fieldOffset * 8 + fieldState.bitOffset; + uint start = (fieldState.fieldOffset * 8 + fieldState.bitOffset) % (memalignsize * 8); uint end = start + bfd.fieldWidth; //printf("%s start: %d end: %d memalignsize: %d\n", ad.toChars(), start, end, memalignsize); - if (start / (memalignsize * 8) != (end - 1) / (memalignsize * 8)) + if (start / (memsize * 8) != (end - 1) / (memsize * 8)) { if (log) printf("alignment is crossed\n"); startNewField(); } } } - else if (style == TargetC.BitFieldStyle.DM || - style == TargetC.BitFieldStyle.MS) + else { if (memsize != fieldState.fieldSize || fieldState.bitOffset + bfd.fieldWidth > fieldState.fieldSize * 8) @@ -7293,16 +7270,16 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor startNewField(); } } - else - assert(0); bfd.offset = fieldState.fieldOffset; bfd.bitOffset = fieldState.bitOffset; const pastField = bfd.bitOffset + bfd.fieldWidth; - if (style == TargetC.BitFieldStyle.Gcc_Clang) + if (isMicrosoftStyle) + fieldState.fieldSize = memsize; + else { - auto size = (pastField + 7) / 8; + const size = (pastField + 7) / 8; fieldState.fieldSize = size; //printf(" offset: %d, size: %d\n", offset, size); if (isunion) @@ -7314,8 +7291,6 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor else ad.structsize = bfd.offset + size; } - else - fieldState.fieldSize = memsize; //printf("at end: ad.structsize = %d\n", cast(int)ad.structsize); //print(fieldState); @@ -7402,7 +7377,7 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor /* Given the anon 'member's size and alignment, * go ahead and place it. */ - anond.anonoffset = placeField( + anond.anonoffset = placeField(anond.loc, fieldState.offset, anond.anonstructsize, anond.anonalignsize, alignment, ad.structsize, ad.alignsize, @@ -7419,3 +7394,819 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor } } } + +extern(D) Scope* newScope(Dsymbol d, Scope* sc) +{ + scope nsv = new NewScopeVisitor(sc); + d.accept(nsv); + return nsv.sc; +} + +private extern(C++) class NewScopeVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + this(Scope* sc) + { + this.sc = sc; + } + + /**************************************** + * A hook point to supply scope for members. + * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. + */ + override void visit(AttribDeclaration dc){} + + override void visit(StorageClassDeclaration swt) + { + STC scstc = sc.stc; + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) + scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); + if (swt.stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) + scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); + if (swt.stc & (STC.const_ | STC.immutable_ | STC.manifest)) + scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); + if (swt.stc & (STC.gshared | STC.shared_)) + scstc &= ~(STC.gshared | STC.shared_); + if (swt.stc & (STC.safe | STC.trusted | STC.system)) + scstc &= ~(STC.safe | STC.trusted | STC.system); + scstc |= swt.stc; + //printf("scstc = x%llx\n", scstc); + sc = swt.createNewScope(sc, scstc, sc.linkage, sc.cppmangle, + sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); + } + + /** + * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set + * + * Calls `StorageClassDeclaration.newScope` (as it must be called or copied + * in any function overriding `newScope`), then set the `Scope`'s depdecl. + * + * Returns: + * Always a new scope, to use for this `DeprecatedDeclaration`'s members. + */ + override void visit(DeprecatedDeclaration dpd) + { + auto oldsc = sc; + visit((cast(StorageClassDeclaration)dpd)); + auto scx = sc; + sc = oldsc; + // The enclosing scope is deprecated as well + if (scx == sc) + scx = sc.push(); + scx.depdecl = dpd; + sc = scx; + } + + override void visit(LinkDeclaration lid) + { + sc= lid.createNewScope(sc, sc.stc, lid.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, + sc.aligndecl, sc.inlining); + } + + override void visit(CPPMangleDeclaration cpmd) + { + sc = cpmd.createNewScope(sc, sc.stc, LINK.cpp, cpmd.cppmangle, sc.visibility, sc.explicitVisibility, + sc.aligndecl, sc.inlining); + } + + /** + * Returns: + * A copy of the parent scope, with `this` as `namespace` and C++ linkage + *///override Scope* visit(Scope* sc) + override void visit(CPPNamespaceDeclaration scd) + { + auto scx = sc.copy(); + scx.linkage = LINK.cpp; + scx.namespace = scd; + sc = scx; + } + + override void visit(VisibilityDeclaration atbd) + { + if (atbd.pkg_identifiers) + dsymbolSemantic(atbd, sc); + + sc = atbd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, atbd.visibility, 1, sc.aligndecl, sc.inlining); + } + + override void visit(AlignDeclaration visd) + { + sc = visd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, + sc.explicitVisibility, visd, sc.inlining); + } + + override void visit(PragmaDeclaration prd) + { + if (prd.ident == Id.Pinline) + { + // We keep track of this pragma inside scopes, + // then it's evaluated on demand in function semantic + sc = prd.createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, prd); // @suppress(dscanner.style.long_line) + } + } + + /************************************** + * Use the ForwardingScopeDsymbol as the parent symbol for members. + */ + override void visit(ForwardingAttribDeclaration fad) + { + sc = sc.push(fad.sym); + } + + override void visit(UserAttributeDeclaration uac) + { + Scope* sc2 = sc; + if (uac.atts && uac.atts.length) + { + // create new one for changes + sc2 = sc.copy(); + sc2.userAttribDecl = uac; + } + sc = sc2; + } +} + + +extern(C++) Dsymbols* include(Dsymbol d, Scope* sc) +{ + scope icv = new IncludeVisitor(sc); + d.accept(icv); + return icv.symbols; +} + +extern(C++) class IncludeVisitor : Visitor +{ + alias visit = typeof(super).visit; + Scope* sc; + Dsymbols* symbols; + this(Scope* sc) + { + this.sc = sc; + } + + override void visit(AttribDeclaration ad) + { + if (ad.errors) + { + symbols = null; + return; + } + symbols = ad.decl; + return; + } + +// Decide if 'then' or 'else' code should be included + override void visit(ConditionalDeclaration cdc) + { + //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); + + if (cdc.errors) + { + symbols = null; + return; + } + assert(cdc.condition); + symbols = cdc.condition.include(cdc._scope ? cdc._scope : sc) ? cdc.decl : cdc.elsedecl; + } + + override void visit(StaticIfDeclaration sif) + { + /**************************************** + * Different from other AttribDeclaration subclasses, include() call requires + * the completion of addMember and setScope phases. + */ + //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); + if (sif.errors || sif.onStack) + { + symbols = null; + return; + } + sif.onStack = true; + scope(exit) sif.onStack = false; + + if (sc && sif.condition.inc == Include.notComputed) + { + assert(sif.scopesym); // addMember is already done + assert(sif._scope); // setScope is already done + + Scope* saved_scope = sc; + sc = sif._scope; + visit(cast(ConditionalDeclaration) sif); + Dsymbols* d = symbols; + sc = saved_scope; + + if (d && !sif.addisdone) + { + // Add members lazily. + d.foreachDsymbol( s => s.addMember(sif._scope, sif.scopesym) ); + + // Set the member scopes lazily. + d.foreachDsymbol( s => s.setScope(sif._scope) ); + + sif.addisdone = true; + } + symbols = d; + return; + } + else + { + visit(cast(ConditionalDeclaration)sif); + } + } + + override void visit(StaticForeachDeclaration sfd) + { + if (sfd.errors || sfd.onStack) + { + symbols = null; + return; + } + if (sfd.cached) + { + assert(!sfd.onStack); + symbols = sfd.cache; + return; + } + sfd.onStack = true; + scope(exit) sfd.onStack = false; + + if (sfd._scope) + { + sfd.sfe.prepare(sfd._scope); // lower static foreach aggregate + } + if (!sfd.sfe.ready()) + { + symbols = null;// TODO: ok? + return; + } + + // expand static foreach + import dmd.statementsem: makeTupleForeach; + Dsymbols* d = makeTupleForeach(sfd._scope, true, true, sfd.sfe.aggrfe, sfd.decl, sfd.sfe.needExpansion).decl; + if (d) // process generated declarations + { + // Add members lazily. + d.foreachDsymbol( s => s.addMember(sfd._scope, sfd.scopesym) ); + + // Set the member scopes lazily. + d.foreachDsymbol( s => s.setScope(sfd._scope) ); + } + sfd.cached = true; + sfd.cache = d; + symbols = d; + } +} + +/** + * Called from a symbol's semantic to check if `gnuAbiTag` UDA + * can be applied to them + * + * Directly emits an error if the UDA doesn't work with this symbol + * + * Params: + * sym = symbol to check for `gnuAbiTag` + * linkage = Linkage of the symbol (Declaration.link or sc.link) + */ +void checkGNUABITag(Dsymbol sym, LINK linkage) +{ + if (global.params.cplusplus < CppStdRevision.cpp11) + return; + + foreachUdaNoSemantic(sym, (exp) { + if (!isGNUABITag(exp)) + return 0; // continue + if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) + { + .error(exp.loc, "`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); + sym.errors = true; + } + else if (linkage != LINK.cpp) + { + .error(exp.loc, "`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); + sym.errors = true; + } + // Only one `@gnuAbiTag` is allowed by semantic2 + return 1; // break + }); +} + +/** + * Check if the provided expression references `core.attribute.gnuAbiTag` + * + * This should be called after semantic has been run on the expression. + * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). + * + * Params: + * e = Expression to check (usually from `UserAttributeDeclaration.atts`) + * + * Returns: + * `true` if the expression references the compiler-recognized `gnuAbiTag` + */ +bool isGNUABITag(Expression e) +{ + if (global.params.cplusplus < CppStdRevision.cpp11) + return false; + + auto ts = e.type ? e.type.isTypeStruct() : null; + if (!ts) + return false; + if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) + return false; + // Can only be defined in druntime + Module m = ts.sym.parent.isModule(); + if (!m || !m.isCoreModule(Id.attribute)) + return false; + return true; +} + +/****************************************** + * If a variable has a scope destructor call, return call for it. + * Otherwise, return NULL. + */ +private Expression callScopeDtor(VarDeclaration vd, Scope* sc) +{ + //printf("VarDeclaration::callScopeDtor() %s\n", toChars()); + + // Destruction of STC.field's is handled by buildDtor() + if (vd.storage_class & (STC.nodtor | STC.ref_ | STC.out_ | STC.field)) + { + return null; + } + + if (vd.iscatchvar) + return null; // destructor is built by `void semantic(Catch c, Scope* sc)`, not here + + Expression e = null; + // Destructors for structs and arrays of structs + Type tv = vd.type.baseElemOf(); + if (tv.ty == Tstruct) + { + StructDeclaration sd = (cast(TypeStruct)tv).sym; + if (!sd.dtor || sd.errors) + return null; + + const sz = vd.type.size(); + assert(sz != SIZE_INVALID); + if (!sz) + return null; + + if (vd.type.toBasetype().ty == Tstruct) + { + // v.__xdtor() + e = new VarExp(vd.loc, vd); + + /* This is a hack so we can call destructors on const/immutable objects. + * Need to add things like "const ~this()" and "immutable ~this()" to + * fix properly. + */ + e.type = e.type.mutableOf(); + + // Enable calling destructors on shared objects. + // The destructor is always a single, non-overloaded function, + // and must serve both shared and non-shared objects. + e.type = e.type.unSharedOf; + + e = new DotVarExp(vd.loc, e, sd.dtor, false); + e = new CallExp(vd.loc, e); + } + else + { + // __ArrayDtor(v[0 .. n]) + e = new VarExp(vd.loc, vd); + + const sdsz = sd.type.size(); + assert(sdsz != SIZE_INVALID && sdsz != 0); + const n = sz / sdsz; + SliceExp se = new SliceExp(vd.loc, e, new IntegerExp(vd.loc, 0, Type.tsize_t), + new IntegerExp(vd.loc, n, Type.tsize_t)); + + // Prevent redundant bounds check + se.upperIsInBounds = true; + se.lowerIsLessThanUpper = true; + + // This is a hack so we can call destructors on const/immutable objects. + se.type = sd.type.arrayOf(); + + e = new CallExp(vd.loc, new IdentifierExp(vd.loc, Id.__ArrayDtor), se); + } + return e; + } + // Destructors for classes + if (!(vd.storage_class & (STC.auto_ | STC.scope_) && !(vd.storage_class & STC.parameter))) + return null; + + for (ClassDeclaration cd = vd.type.isClassHandle(); cd; cd = cd.baseClass) + { + /* We can do better if there's a way with onstack + * classes to determine if there's no way the monitor + * could be set. + */ + //if (cd.isInterfaceDeclaration()) + // error("interface `%s` cannot be scope", cd.toChars()); + + if (!vd.onstack) // if any destructors + continue; + // delete'ing C++ classes crashes (and delete is deprecated anyway) + if (cd.classKind == ClassKind.cpp) + { + // Don't call non-existant dtor + if (!cd.dtor) + break; + + e = new VarExp(vd.loc, vd); + e.type = e.type.mutableOf().unSharedOf(); // Hack for mutable ctor on immutable instances + e = new DotVarExp(vd.loc, e, cd.dtor, false); + e = new CallExp(vd.loc, e); + break; + } + + // delete this; + Expression ec; + ec = new VarExp(vd.loc, vd); + e = new DeleteExp(vd.loc, ec, true); + break; + } + return e; +} + +/*************************************** + * Collect all instance fields, then determine instance size. + * Returns: + * false if failed to determine the size. + */ +bool determineSize(AggregateDeclaration ad, Loc loc) +{ + //printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok); + + // The previous instance size finalizing had: + if (ad.type.ty == Terror || ad.errors) + return false; // failed already + if (ad.sizeok == Sizeok.done) + return true; // succeeded + + if (!ad.members) + { + .error(loc, "%s `%s` unknown size", ad.kind, ad.toPrettyChars); + return false; + } + + if (ad._scope) + dsymbolSemantic(ad, null); + + // Determine the instance size of base class first. + if (auto cd = ad.isClassDeclaration()) + { + cd = cd.baseClass; + if (cd && !cd.determineSize(loc)) + goto Lfail; + } + + // Determine instance fields when sizeok == Sizeok.none + if (!ad.determineFields()) + goto Lfail; + if (ad.sizeok != Sizeok.done) + ad.finalizeSize(); + + // this aggregate type has: + if (ad.type.ty == Terror) + return false; // marked as invalid during the finalizing. + if (ad.sizeok == Sizeok.done) + return true; // succeeded to calculate instance size. + +Lfail: + // There's unresolvable forward reference. + if (ad.type != Type.terror) + error(loc, "%s `%s` no size because of forward reference", ad.kind, ad.toPrettyChars); + // Don't cache errors from speculative semantic, might be resolvable later. + // https://issues.dlang.org/show_bug.cgi?id=16574 + if (!global.gag) + { + ad.type = Type.terror; + ad.errors = true; + } + return false; +} + +extern (C++) void addComment(Dsymbol d, const(char)* comment) +{ + scope v = new AddCommentVisitor(comment); + d.accept(v); +} + +extern (C++) class AddCommentVisitor: Visitor +{ + alias visit = Visitor.visit; + + const(char)* comment; + + this(const(char)* comment) + { + this.comment = comment; + } + + override void visit(Dsymbol d) + { + if (!comment || !*comment) + return; + + //printf("addComment '%s' to Dsymbol %p '%s'\n", comment, this, toChars()); + void* h = cast(void*)d; // just the pointer is the key + auto p = h in d.commentHashTable; + if (!p) + { + d.commentHashTable[h] = comment; + return; + } + if (strcmp(*p, comment) != 0) + { + // Concatenate the two + *p = Lexer.combineComments((*p).toDString(), comment.toDString(), true); + } + } + override void visit(AttribDeclaration atd) + { + if (comment) + { + atd.include(null).foreachDsymbol( s => s.addComment(comment) ); + } + } + override void visit(ConditionalDeclaration cd) + { + if (comment) + { + cd.decl .foreachDsymbol( s => s.addComment(comment) ); + cd.elsedecl.foreachDsymbol( s => s.addComment(comment) ); + } + } + override void visit(StaticForeachDeclaration sfd) {} +} + +void checkCtorConstInit(Dsymbol d) +{ + scope v = new CheckCtorConstInitVisitor(); + d.accept(v); +} + +private extern(C++) class CheckCtorConstInitVisitor : Visitor +{ + alias visit = Visitor.visit; + + override void visit(AttribDeclaration ad) + { + ad.include(null).foreachDsymbol( s => s.checkCtorConstInit() ); + } + + override void visit(VarDeclaration vd) + { + version (none) + { + /* doesn't work if more than one static ctor */ + if (vd.ctorinit == 0 && vd.isCtorinit() && !vd.isField()) + error("missing initializer in static constructor for const variable"); + } + } + + override void visit(Dsymbol d){} +} + +/************************************** +* Determine if this symbol is only one. +* Returns: +* false, ps = null: There are 2 or more symbols +* true, ps = null: There are zero symbols +* true, ps = symbol: The one and only one symbol +*/ +bool oneMember(Dsymbol d, out Dsymbol ps, Identifier ident) +{ + scope v = new OneMemberVisitor(ps, ident); + d.accept(v); + return v.result; +} + +private extern(C++) class OneMemberVisitor : Visitor +{ + alias visit = Visitor.visit; + + Dsymbol* ps; + Identifier ident; + bool result; + + this(out Dsymbol ps, Identifier ident) + { + this.ps = &ps; + this.ident = ident; + } + + override void visit(AttribDeclaration atb) + { + Dsymbols* d = atb.include(null); + result = Dsymbol.oneMembers(d, *ps, ident); + } + + override void visit(StaticForeachDeclaration sfd) + { + // Required to support IFTI on a template that contains a + // `static foreach` declaration. `super.oneMember` calls + // include with a `null` scope. As `static foreach` requires + // the scope for expansion, `oneMember` can only return a + // precise result once `static foreach` has been expanded. + if (sfd.cached) + { + this.visit(cast(AttribDeclaration) sfd); + } + else + { + *ps = null; // a `static foreach` declaration may in general expand to multiple symbols + result = false; + } + } + + override void visit(StorageClassDeclaration scd) + { + bool t = Dsymbol.oneMembers(scd.decl, *ps, ident); + if (t && *ps) + { + /* This is to deal with the following case: + * struct Tick { + * template to(T) { const T to() { ... } } + * } + * For eponymous function templates, the 'const' needs to get attached to 'to' + * before the semantic analysis of 'to', so that template overloading based on the + * 'this' pointer can be successful. + */ + if (FuncDeclaration fd = (*ps).isFuncDeclaration()) + { + /* Use storage_class2 instead of storage_class otherwise when we do .di generation + * we'll wind up with 'const const' rather than 'const'. + */ + /* Don't think we need to worry about mutually exclusive storage classes here + */ + fd.storage_class2 |= scd.stc; + } + } + result = t; + } + + override void visit(ConditionalDeclaration cd) + { + //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc); + if (cd.condition.inc != Include.notComputed) + { + Dsymbols* d = cd.condition.include(null) ? cd.decl : cd.elsedecl; + result = Dsymbol.oneMembers(d, *ps, ident); + } + else + { + bool res = (Dsymbol.oneMembers(cd.decl, *ps, ident) && *ps is null && Dsymbol.oneMembers(cd.elsedecl, *ps, ident) && *ps is null); + *ps = null; + result = res; + } + } + + override void visit(ScopeDsymbol sd) + { + if (sd.isAnonymous()) + result = Dsymbol.oneMembers(sd.members, *ps, ident); + else { + // visit(Dsymbol dsym) + *ps = sd; + result = true; + } + } + + override void visit(StaticAssert sa) + { + //printf("StaticAssert::oneMember())\n"); + *ps = null; + result = true; + } + + override void visit(TemplateInstance ti) + { + *ps = null; + result = true; + } + + override void visit(TemplateMixin tm) + { + *ps = tm; + result = true; + } + + override void visit(Dsymbol dsym) + { + *ps = dsym; + result = true; + } +} + +/**************************************** +* Return true if any of the members are static ctors or static dtors, or if +* any members have members that are. +*/ +extern(C++) bool hasStaticCtorOrDtor(Dsymbol d) +{ + scope v = new HasStaticCtorOrDtor(); + d.accept(v); + return v.result; +} + +private extern(C++) class HasStaticCtorOrDtor : Visitor +{ + import dmd.mtype : Type; + + alias visit = Visitor.visit; + bool result; + + // attrib.d + override void visit(AttribDeclaration ad){ + result = ad.include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; + } + + // dsymbol.d + override void visit(Dsymbol d){ + //printf("Dsymbol::hasStaticCtorOrDtor() %s\n", toChars()); + result = false; + } + + override void visit(ScopeDsymbol sd) { + if (sd.members) + { + for (size_t i = 0; i < sd.members.length; i++) + { + Dsymbol member = (*(sd.members))[i]; + if (member.hasStaticCtorOrDtor()) + result = true; + return; + } + } + result = false; + } + + // dtemplate.d + override void visit(TemplateDeclaration td) { + result = false; // don't scan uninstantiated templates + } + + // func.d + override void visit(StaticCtorDeclaration scd) { + result = true; + } + + override void visit(StaticDtorDeclaration sdd) @nogc nothrow pure @safe { + result = true; + } +} + +extern(C++) bool isFuncHidden(ClassDeclaration cd, FuncDeclaration fd) +{ + import dmd.funcsem : overloadApply; + //printf("ClassDeclaration.isFuncHidden(class = %s, fd = %s)\n", toChars(), fd.toPrettyChars()); + Dsymbol s = cd.search(Loc.initial, fd.ident, SearchOpt.ignoreAmbiguous | SearchOpt.ignoreErrors); + if (!s) + { + //printf("not found\n"); + /* Because, due to a hack, if there are multiple definitions + * of fd.ident, NULL is returned. + */ + return false; + } + s = s.toAlias(); + if (auto os = s.isOverloadSet()) + { + foreach (sm; os.a) + { + auto fm = sm.isFuncDeclaration(); + if (overloadApply(fm, s => fd == s.isFuncDeclaration())) + return false; + } + return true; + } + else + { + auto f = s.isFuncDeclaration(); + //printf("%s fdstart = %p\n", s.kind(), fdstart); + if (overloadApply(f, s => fd == s.isFuncDeclaration())) + return false; + return !fd.parent.isTemplateMixin(); + } +} + +Dsymbol vtblSymbol(ClassDeclaration cd) +{ + if (!cd.vtblsym) + { + auto vtype = Type.tvoidptr.immutableOf().sarrayOf(cd.vtbl.length); + auto var = new VarDeclaration(cd.loc, vtype, Identifier.idPool("__vtbl"), null, STC.immutable_ | STC.static_); + var.addMember(null, cd); + var.isdataseg = 1; + var._linkage = LINK.d; + var.semanticRun = PASS.semanticdone; // no more semantic wanted + cd.vtblsym = var; + } + return cd.vtblsym; +} diff --git a/gcc/d/dmd/dtemplate.d b/gcc/d/dmd/dtemplate.d index d181fac..2158895 100644 --- a/gcc/d/dmd/dtemplate.d +++ b/gcc/d/dmd/dtemplate.d @@ -28,12 +28,12 @@ * arguments, and uses it if found. * - Otherwise, the rest of semantic is run on the `TemplateInstance`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtemplate.d, _dtemplate.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtemplate.d, _dtemplate.d) * Documentation: https://dlang.org/phobos/dmd_dtemplate.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtemplate.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dtemplate.d */ module dmd.dtemplate; @@ -50,36 +50,36 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; -import dmd.dsymbolsem; +import dmd.dsymbolsem : dsymbolSemantic, checkDeprecated, aliasSemantic, search, search_correct, setScope, importAll, include, hasStaticCtorOrDtor; import dmd.errors; import dmd.errorsink; import dmd.expression; -import dmd.expressionsem; +import dmd.expressionsem : resolveLoc, expressionSemantic, resolveProperties, checkValue; import dmd.func; -import dmd.funcsem; +import dmd.funcsem : functionSemantic, leastAsSpecialized, overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.impcnvtab; import dmd.init; -import dmd.initsem; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.opover; import dmd.optimize; import dmd.root.array; import dmd.common.outbuffer; import dmd.rootobject; -import dmd.semantic2; -import dmd.semantic3; -import dmd.templatesem; +import dmd.semantic3 : semantic3; +import dmd.templatesem : matchWithInstance, formatParamsWithTiargs, leastAsSpecialized, declareParameter; import dmd.tokens; -import dmd.typesem; +import dmd.typesem : hasPointers, typeSemantic, merge, merge2, resolve, toDsymbol, + addStorageClass, isBaseOf, equivalent, sarrayOf, constOf, mutableOf, unSharedOf, + unqualify, aliasthisOf, castMod, substWildTo, addMod; import dmd.visitor; import dmd.templateparamsem; @@ -89,7 +89,7 @@ private enum LOG = false; enum IDX_NOTFOUND = 0x12345678; -pure nothrow @nogc @safe +pure nothrow @nogc @trusted { /******************************************** @@ -136,6 +136,13 @@ inout(Parameter) isParameter(inout RootObject o) return cast(inout(Parameter))o; } +inout(Identifier) isIdentifier(inout RootObject o) +{ + if (!o || o.dyncast() != DYNCAST.identifier) + return null; + return cast(inout(Identifier))o; +} + inout(TemplateParameter) isTemplateParameter(inout RootObject o) { if (!o || o.dyncast() != DYNCAST.templateparameter) @@ -143,6 +150,11 @@ inout(TemplateParameter) isTemplateParameter(inout RootObject o) return cast(inout(TemplateParameter))o; } +} // end @trusted casts + +pure nothrow @nogc @safe +{ + /************************************** * Is this Object an error? */ @@ -206,23 +218,18 @@ Dsymbol getDsymbol(RootObject oarg) // Try to convert Expression to symbol if (auto ve = ea.isVarExp()) return ve.var; - else if (auto fe = ea.isFuncExp()) + if (auto fe = ea.isFuncExp()) return fe.td ? fe.td : fe.fd; - else if (auto te = ea.isTemplateExp()) + if (auto te = ea.isTemplateExp()) return te.td; - else if (auto te = ea.isScopeExp()) + if (auto te = ea.isScopeExp()) return te.sds; - else - return null; - } - else - { - // Try to convert Type to symbol - if (auto ta = isType(oarg)) - return ta.toDsymbol(null); - else - return isDsymbol(oarg); // if already a symbol + return null; } + // Try to convert Type to symbol + if (auto ta = isType(oarg)) + return ta.toDsymbol(null); + return isDsymbol(oarg); // if already a symbol } @@ -282,6 +289,18 @@ private bool match(RootObject o1, RootObject o2) o1, o1.toChars(), o1.dyncast(), o2, o2.toChars(), o2.dyncast()); } + bool yes() + { + static if (log) + printf("\t. match\n"); + return true; + } + bool no() + { + static if (log) + printf("\t. nomatch\n"); + return false; + } /* A proper implementation of the various equals() overrides * should make it possible to just do o1.equals(o2), but * we'll do that another day. @@ -294,7 +313,7 @@ private bool match(RootObject o1, RootObject o2) { auto t2 = isType(o2); if (!t2) - goto Lnomatch; + return no(); static if (log) { @@ -302,15 +321,15 @@ private bool match(RootObject o1, RootObject o2) printf("\tt2 = %s\n", t2.toChars()); } if (!t1.equals(t2)) - goto Lnomatch; + return no(); - goto Lmatch; + return yes(); } if (auto e1 = getExpression(o1)) { auto e2 = getExpression(o2); if (!e2) - goto Lnomatch; + return no(); static if (log) { @@ -323,15 +342,15 @@ private bool match(RootObject o1, RootObject o2) // as well as expression equality to ensure templates are properly // matched. if (!(e1.type && e2.type && e1.type.equals(e2.type)) || !e1.equals(e2)) - goto Lnomatch; + return no(); - goto Lmatch; + return yes(); } if (auto s1 = isDsymbol(o1)) { auto s2 = isDsymbol(o2); if (!s2) - goto Lnomatch; + return no(); static if (log) { @@ -339,17 +358,17 @@ private bool match(RootObject o1, RootObject o2) printf("\ts2 = %s \n", s2.kind(), s2.toChars()); } if (!s1.equals(s2)) - goto Lnomatch; + return no(); if (s1.parent != s2.parent && !s1.isFuncDeclaration() && !s2.isFuncDeclaration()) - goto Lnomatch; + return no(); - goto Lmatch; + return yes(); } if (auto u1 = isTuple(o1)) { auto u2 = isTuple(o2); if (!u2) - goto Lnomatch; + return no(); static if (log) { @@ -357,19 +376,11 @@ private bool match(RootObject o1, RootObject o2) printf("\tu2 = %s\n", u2.toChars()); } if (!arrayObjectMatch(u1.objects, u2.objects)) - goto Lnomatch; + return no(); - goto Lmatch; + return yes(); } -Lmatch: - static if (log) - printf("\t. match\n"); - return true; - -Lnomatch: - static if (log) - printf("\t. nomatch\n"); - return false; + return yes(); } /************************************ @@ -414,8 +425,7 @@ private size_t arrayObjectHash(ref Objects oa1) hash = mixHash(hash, expressionHash(e1)); else if (auto s1 = isDsymbol(o1)) { - auto fa1 = s1.isFuncAliasDeclaration(); - if (fa1) + if (auto fa1 = s1.isFuncAliasDeclaration()) s1 = fa1.toAliasFunc(); hash = mixHash(hash, mixHash(cast(size_t)cast(void*)s1.getIdent(), cast(size_t)cast(void*)s1.parent)); } @@ -592,9 +602,10 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol Array!Expression lastConstraintNegs; /// its negative parts Objects* lastConstraintTiargs; /// template instance arguments for `lastConstraint` - extern (D) this(const ref Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false) + extern (D) this(Loc loc, Identifier ident, TemplateParameters* parameters, Expression constraint, Dsymbols* decldefs, bool ismixin = false, bool literal = false) { super(loc, ident); + this.dsym = DSYM.templateDeclaration; static if (LOG) { printf("TemplateDeclaration(this = %p, id = '%s')\n", this, ident.toChars()); @@ -733,19 +744,17 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol return true; } - override bool hasStaticCtorOrDtor() - { - return false; // don't scan uninstantiated templates - } - override const(char)* kind() const { return (onemember && onemember.isAggregateDeclaration()) ? onemember.kind() : "template"; } - override const(char)* toChars() const + /**************************** + * Similar to `toChars`, but does not print the template constraints + */ + const(char)* toCharsNoConstraints() const { - HdrGenState hgs; + HdrGenState hgs = { skipConstraints: true }; OutBuffer buf; toCharsMaybeConstraints(this, buf, hgs); return buf.extractChars(); @@ -877,11 +886,6 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol instances.remove(tibox); } - override inout(TemplateDeclaration) isTemplateDeclaration() inout - { - return this; - } - /** * Check if the last template parameter is a tuple one, * and returns it if so, else returns `null`. @@ -891,7 +895,7 @@ extern (C++) final class TemplateDeclaration : ScopeDsymbol */ extern (D) TemplateTupleParameter isVariadic() { - size_t dim = parameters.length; + const dim = parameters.length; if (dim == 0) return null; return (*parameters)[dim - 1].isTemplateTupleParameter(); @@ -992,6 +996,11 @@ size_t templateParameterLookup(Type tparam, TemplateParameters* parameters) return IDX_NOTFOUND; } +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + ubyte deduceWildHelper(Type t, Type* at, Type tparam) { if ((tparam.mod & MODFlags.wild) == 0) @@ -999,11 +1008,6 @@ ubyte deduceWildHelper(Type t, Type* at, Type tparam) *at = null; - auto X(T, U)(T U, U T) - { - return (U << 4) | T; - } - switch (X(tparam.mod, t.mod)) { case X(MODFlags.wild, 0): @@ -1078,12 +1082,6 @@ private Type rawTypeMerge(Type t1, Type t2) MATCH deduceTypeHelper(Type t, out Type at, Type tparam) { // 9*9 == 81 cases - - auto X(T, U)(T U, U T) - { - return (U << 4) | T; - } - switch (X(tparam.mod, t.mod)) { case X(0, 0): @@ -1630,60 +1628,66 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa override void visit(TypeSArray t) { // Extra check that array dimensions must match - if (tparam) + if (!tparam) { - if (tparam.ty == Tarray) - { - MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; - return; - } + visit(cast(Type)t); + return; + } - TemplateParameter tp = null; - Expression edim = null; - size_t i; - if (auto tsa = tparam.isTypeSArray()) + if (tparam.ty == Tarray) + { + MATCH m = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + result = (m >= MATCH.constant) ? MATCH.convert : MATCH.nomatch; + return; + } + + TemplateParameter tp = null; + Expression edim = null; + size_t i; + if (auto tsa = tparam.isTypeSArray()) + { + if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) { - if (tsa.dim.isVarExp() && tsa.dim.isVarExp().var.storage_class & STC.templateparameter) - { - Identifier id = tsa.dim.isVarExp().var.ident; - i = templateIdentifierLookup(id, ¶meters); - assert(i != IDX_NOTFOUND); - tp = parameters[i]; - } - else - edim = tsa.dim; + Identifier id = tsa.dim.isVarExp().var.ident; + i = templateIdentifierLookup(id, ¶meters); + assert(i != IDX_NOTFOUND); + tp = parameters[i]; } - else if (auto taa = tparam.isTypeAArray()) + else + edim = tsa.dim; + } + else if (auto taa = tparam.isTypeAArray()) + { + i = templateParameterLookup(taa.index, ¶meters); + if (i != IDX_NOTFOUND) + tp = parameters[i]; + else { - i = templateParameterLookup(taa.index, ¶meters); - if (i != IDX_NOTFOUND) - tp = parameters[i]; - else + Loc loc; + // The "type" (it hasn't been resolved yet) of the function parameter + // does not have a location but the parameter it is related to does, + // so we use that for the resolution (better error message). + if (inferStart < parameters.length) { - Loc loc; - // The "type" (it hasn't been resolved yet) of the function parameter - // does not have a location but the parameter it is related to does, - // so we use that for the resolution (better error message). - if (inferStart < parameters.length) - { - TemplateParameter loctp = parameters[inferStart]; - loc = loctp.loc; - } - - Expression e; - Type tx; - Dsymbol s; - taa.index.resolve(loc, sc, e, tx, s); - edim = s ? getValue(s) : getValue(e); + TemplateParameter loctp = parameters[inferStart]; + loc = loctp.loc; } + + Expression e; + Type tx; + Dsymbol s; + taa.index.resolve(loc, sc, e, tx, s); + edim = s ? getValue(s) : getValue(e); } - if (tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null) || edim && edim.toInteger() == t.dim.toInteger()) - { - result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); - return; - } } + if ((tp && tp.matchArg(sc, t.dim, i, ¶meters, dedtypes, null)) || + (edim && edim.isIntegerExp() && edim.toInteger() == t.dim.toInteger()) + ) + { + result = deduceType(t.next, sc, tparam.nextOf(), parameters, dedtypes, wm); + return; + } + visit(cast(Type)t); } @@ -1708,132 +1712,137 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa if (!tparam) return visit(cast(Type)t); - if (auto tp = tparam.isTypeFunction()) + auto tp = tparam.isTypeFunction(); + if (!tp) { - if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) - { - result = MATCH.nomatch; - return; - } + visit(cast(Type)t); + return; + } + + if (t.parameterList.varargs != tp.parameterList.varargs || t.linkage != tp.linkage) + { + result = MATCH.nomatch; + return; + } + + foreach (fparam; *tp.parameterList.parameters) + { + // https://issues.dlang.org/show_bug.cgi?id=2579 + // Apply function parameter storage classes to parameter types + fparam.type = fparam.type.addStorageClass(fparam.storageClass); + fparam.storageClass &= ~STC.TYPECTOR; - foreach (fparam; *tp.parameterList.parameters) + // https://issues.dlang.org/show_bug.cgi?id=15243 + // Resolve parameter type if it's not related with template parameters + if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) { - // https://issues.dlang.org/show_bug.cgi?id=2579 - // Apply function parameter storage classes to parameter types - fparam.type = fparam.type.addStorageClass(fparam.storageClass); - fparam.storageClass &= ~STC.TYPECTOR; - - // https://issues.dlang.org/show_bug.cgi?id=15243 - // Resolve parameter type if it's not related with template parameters - if (!reliesOnTemplateParameters(fparam.type, parameters[inferStart .. parameters.length])) + auto tx = fparam.type.typeSemantic(Loc.initial, sc); + if (tx.ty == Terror) { - auto tx = fparam.type.typeSemantic(Loc.initial, sc); - if (tx.ty == Terror) - { - result = MATCH.nomatch; - return; - } - fparam.type = tx; + result = MATCH.nomatch; + return; } + fparam.type = tx; } + } - const size_t nfargs = t.parameterList.length; - size_t nfparams = tp.parameterList.length; + const size_t nfargs = t.parameterList.length; + size_t nfparams = tp.parameterList.length; - /* See if tuple match + /* See if tuple match + */ + if (nfparams > 0 && nfargs >= nfparams - 1) + { + /* See if 'A' of the template parameter matches 'A' + * of the type of the last function parameter. */ - if (nfparams > 0 && nfargs >= nfparams - 1) + Parameter fparam = tp.parameterList[nfparams - 1]; + assert(fparam); + assert(fparam.type); + if (fparam.type.ty != Tident) + goto L1; + TypeIdentifier tid = fparam.type.isTypeIdentifier(); + if (tid.idents.length) + goto L1; + + /* Look through parameters to find tuple matching tid.ident + */ + size_t tupi = 0; + for (; 1; tupi++) { - /* See if 'A' of the template parameter matches 'A' - * of the type of the last function parameter. - */ - Parameter fparam = tp.parameterList[nfparams - 1]; - assert(fparam); - assert(fparam.type); - if (fparam.type.ty != Tident) - goto L1; - TypeIdentifier tid = fparam.type.isTypeIdentifier(); - if (tid.idents.length) + if (tupi == parameters.length) goto L1; + TemplateParameter tx = parameters[tupi]; + TemplateTupleParameter tup = tx.isTemplateTupleParameter(); + if (tup && tup.ident.equals(tid.ident)) + break; + } - /* Look through parameters to find tuple matching tid.ident - */ - size_t tupi = 0; - for (; 1; tupi++) + /* The types of the function arguments [nfparams - 1 .. nfargs] + * now form the tuple argument. + */ + size_t tuple_dim = nfargs - (nfparams - 1); + + /* See if existing tuple, and whether it matches or not + */ + RootObject o = dedtypes[tupi]; + if (o) + { + // Existing deduced argument must be a tuple, and must match + Tuple tup = isTuple(o); + if (!tup || tup.objects.length != tuple_dim) { - if (tupi == parameters.length) - goto L1; - TemplateParameter tx = parameters[tupi]; - TemplateTupleParameter tup = tx.isTemplateTupleParameter(); - if (tup && tup.ident.equals(tid.ident)) - break; + result = MATCH.nomatch; + return; } - - /* The types of the function arguments [nfparams - 1 .. nfargs] - * now form the tuple argument. - */ - size_t tuple_dim = nfargs - (nfparams - 1); - - /* See if existing tuple, and whether it matches or not - */ - RootObject o = dedtypes[tupi]; - if (o) + for (size_t i = 0; i < tuple_dim; i++) { - // Existing deduced argument must be a tuple, and must match - Tuple tup = isTuple(o); - if (!tup || tup.objects.length != tuple_dim) + Parameter arg = t.parameterList[nfparams - 1 + i]; + if (!arg.type.equals(tup.objects[i])) { result = MATCH.nomatch; return; } - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - if (!arg.type.equals(tup.objects[i])) - { - result = MATCH.nomatch; - return; - } - } } - else + } + else + { + // Create new tuple + auto tup = new Tuple(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) { - // Create new tuple - auto tup = new Tuple(tuple_dim); - for (size_t i = 0; i < tuple_dim; i++) - { - Parameter arg = t.parameterList[nfparams - 1 + i]; - tup.objects[i] = arg.type; - } - dedtypes[tupi] = tup; + Parameter arg = t.parameterList[nfparams - 1 + i]; + tup.objects[i] = arg.type; } - nfparams--; // don't consider the last parameter for type deduction - goto L2; + dedtypes[tupi] = tup; } + nfparams--; // don't consider the last parameter for type deduction + goto L2; + } - L1: - if (nfargs != nfparams) + L1: + if (nfargs != nfparams) + { + result = MATCH.nomatch; + return; + } + L2: + assert(nfparams <= tp.parameterList.length); + foreach (i, ap; tp.parameterList) + { + if (i == nfparams) + break; + + Parameter a = t.parameterList[i]; + + if (!a.isCovariant(t.isRef, ap) || + !deduceType(a.type, sc, ap.type, parameters, dedtypes)) { result = MATCH.nomatch; return; } - L2: - assert(nfparams <= tp.parameterList.length); - foreach (i, ap; tp.parameterList) - { - if (i == nfparams) - break; - - Parameter a = t.parameterList[i]; - - if (!a.isCovariant(t.isref, ap) || - !deduceType(a.type, sc, ap.type, parameters, dedtypes)) - { - result = MATCH.nomatch; - return; - } - } } + visit(cast(Type)t); } @@ -1860,78 +1869,82 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa override void visit(TypeInstance t) { // Extra check - if (tparam && tparam.ty == Tinstance && t.tempinst.tempdecl) + if (!tparam || tparam.ty != Tinstance || !t.tempinst.tempdecl) { - TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); - assert(tempdecl); + visit(cast(Type)t); + return; + } - TypeInstance tp = tparam.isTypeInstance(); + TemplateDeclaration tempdecl = t.tempinst.tempdecl.isTemplateDeclaration(); + assert(tempdecl); - //printf("tempinst.tempdecl = %p\n", tempdecl); - //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); - if (!tp.tempinst.tempdecl) - { - //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); + TypeInstance tp = tparam.isTypeInstance(); - /* Handle case of: - * template Foo(T : sa!(T), alias sa) + //printf("tempinst.tempdecl = %p\n", tempdecl); + //printf("tp.tempinst.tempdecl = %p\n", tp.tempinst.tempdecl); + if (!tp.tempinst.tempdecl) + { + //printf("tp.tempinst.name = '%s'\n", tp.tempinst.name.toChars()); + + /* Handle case of: + * template Foo(T : sa!(T), alias sa) + */ + size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); + if (i == IDX_NOTFOUND) + { + /* Didn't find it as a parameter identifier. Try looking + * it up and seeing if is an alias. + * https://issues.dlang.org/show_bug.cgi?id=1454 */ - size_t i = templateIdentifierLookup(tp.tempinst.name, ¶meters); - if (i == IDX_NOTFOUND) + auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); + Type tx; + Expression e; + Dsymbol s; + tid.resolve(tp.loc, sc, e, tx, s); + if (tx) { - /* Didn't find it as a parameter identifier. Try looking - * it up and seeing if is an alias. - * https://issues.dlang.org/show_bug.cgi?id=1454 - */ - auto tid = new TypeIdentifier(tp.loc, tp.tempinst.name); - Type tx; - Expression e; - Dsymbol s; - tid.resolve(tp.loc, sc, e, tx, s); - if (tx) + s = tx.toDsymbol(sc); + if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) { - s = tx.toDsymbol(sc); - if (TemplateInstance ti = s ? s.parent.isTemplateInstance() : null) - { - // https://issues.dlang.org/show_bug.cgi?id=14290 - // Try to match with ti.tempecl, - // only when ti is an enclosing instance. - Dsymbol p = sc.parent; - while (p && p != ti) - p = p.parent; - if (p) - s = ti.tempdecl; - } + // https://issues.dlang.org/show_bug.cgi?id=14290 + // Try to match with ti.tempecl, + // only when ti is an enclosing instance. + Dsymbol p = sc.parent; + while (p && p != ti) + p = p.parent; + if (p) + s = ti.tempdecl; } - if (s) + } + if (s) + { + s = s.toAlias(); + TemplateDeclaration td = s.isTemplateDeclaration(); + if (td) { - s = s.toAlias(); - TemplateDeclaration td = s.isTemplateDeclaration(); - if (td) + if (td.overroot) + td = td.overroot; + for (; td; td = td.overnext) { - if (td.overroot) - td = td.overroot; - for (; td; td = td.overnext) - { - if (td == tempdecl) - goto L2; - } + if (td == tempdecl) + goto L2; } } - goto Lnomatch; } - - TemplateParameter tpx = parameters[i]; - if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) - goto Lnomatch; - } - else if (tempdecl != tp.tempinst.tempdecl) goto Lnomatch; + } - L2: - if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + TemplateParameter tpx = parameters[i]; + if (!tpx.matchArg(sc, tempdecl, i, ¶meters, dedtypes, null)) goto Lnomatch; } + else if (tempdecl != tp.tempinst.tempdecl) + goto Lnomatch; + + L2: + if (!resolveTemplateInstantiation(sc, ¶meters, t.tempinst.tiargs, &t.tempinst.tdtypes, tempdecl, tp, &dedtypes)) + goto Lnomatch; + visit(cast(Type)t); return; @@ -2537,8 +2550,7 @@ MATCH deduceType(RootObject o, Scope* sc, Type tparam, ref TemplateParameters pa */ private void deduceBaseClassParameters(ref BaseClass b, Scope* sc, Type tparam, ref TemplateParameters parameters, ref Objects dedtypes, ref Objects best, ref int numBaseClassMatches) { - TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null; - if (parti) + if (TemplateInstance parti = b.sym ? b.sym.parent.isTemplateInstance() : null) { // Make a temporary copy of dedtypes so we don't destroy it auto tmpdedtypes = new Objects(dedtypes.length); @@ -2981,6 +2993,8 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(NewExp e) { //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); result = e.newtype.reliesOnTemplateParameters(tparams); @@ -3157,7 +3171,7 @@ extern (C++) class TemplateParameter : ASTNode bool dependent; /* ======================== TemplateParameter =============================== */ - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { this.loc = loc; this.ident = ident; @@ -3196,7 +3210,7 @@ extern (C++) class TemplateParameter : ASTNode abstract RootObject specialization(); - abstract RootObject defaultArg(const ref Loc instLoc, Scope* sc); + abstract RootObject defaultArg(Loc instLoc, Scope* sc); abstract bool hasDefaultArg(); @@ -3210,10 +3224,6 @@ extern (C++) class TemplateParameter : ASTNode return DYNCAST.templateparameter; } - /* Create dummy argument based on parameter. - */ - abstract RootObject dummyArg(); - override void accept(Visitor v) { v.visit(this); @@ -3232,7 +3242,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter extern (D) __gshared Type tdummy = null; - extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType) @safe + extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe { super(loc, ident); this.specType = specType; @@ -3278,7 +3288,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter return specType; } - override final RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override final RootObject defaultArg(Loc instLoc, Scope* sc) { Type t = defaultType; if (t) @@ -3294,19 +3304,6 @@ extern (C++) class TemplateTypeParameter : TemplateParameter return defaultType !is null; } - override final RootObject dummyArg() - { - Type t = specType; - if (!t) - { - // Use this for alias-parameter's too (?) - if (!tdummy) - tdummy = new TypeIdentifier(loc, ident); - t = tdummy; - } - return t; - } - override void accept(Visitor v) { v.visit(this); @@ -3320,7 +3317,7 @@ extern (C++) class TemplateTypeParameter : TemplateParameter */ extern (C++) final class TemplateThisParameter : TemplateTypeParameter { - extern (D) this(const ref Loc loc, Identifier ident, Type specType, Type defaultType) @safe + extern (D) this(Loc loc, Identifier ident, Type specType, Type defaultType) @safe { super(loc, ident, specType, defaultType); } @@ -3354,7 +3351,7 @@ extern (C++) final class TemplateValueParameter : TemplateParameter extern (D) __gshared Expression[void*] edummies; - extern (D) this(const ref Loc loc, Identifier ident, Type valType, + extern (D) this(Loc loc, Identifier ident, Type valType, Expression specValue, Expression defaultValue) @safe { super(loc, ident); @@ -3411,37 +3408,38 @@ extern (C++) final class TemplateValueParameter : TemplateParameter return specValue; } - override RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override RootObject defaultArg(Loc instLoc, Scope* sc) { Expression e = defaultValue; - if (e) - { - e = e.syntaxCopy(); - Scope* sc2 = sc.push(); - sc2.inDefaultArg = true; - e = e.expressionSemantic(sc2); - sc2.pop(); - if (e is null) - return null; - if (auto te = e.isTemplateExp()) - { - assert(sc && sc.tinst); - if (te.td == sc.tinst.tempdecl) - { - // defaultValue is a reference to its template declaration - // i.e: `template T(int arg = T)` - // Raise error now before calling resolveProperties otherwise we'll - // start looping on the expansion of the template instance. - auto td = sc.tinst.tempdecl; - .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars); - return ErrorExp.get(); - } + if (!e) + return null; + + e = e.syntaxCopy(); + Scope* sc2 = sc.push(); + sc2.inDefaultArg = true; + e = e.expressionSemantic(sc2); + sc2.pop(); + if (e is null) + return null; + if (auto te = e.isTemplateExp()) + { + assert(sc && sc.tinst); + if (te.td == sc.tinst.tempdecl) + { + // defaultValue is a reference to its template declaration + // i.e: `template T(int arg = T)` + // Raise error now before calling resolveProperties otherwise we'll + // start looping on the expansion of the template instance. + auto td = sc.tinst.tempdecl; + .error(td.loc, "%s `%s` recursive template expansion", td.kind, td.toPrettyChars); + return ErrorExp.get(); } - if ((e = resolveProperties(sc, e)) is null) - return null; - e = e.resolveLoc(instLoc, sc); // use the instantiated loc - e = e.optimize(WANTvalue); } + if ((e = resolveProperties(sc, e)) is null) + return null; + e = e.resolveLoc(instLoc, sc); // use the instantiated loc + e = e.optimize(WANTvalue); + return e; } @@ -3450,24 +3448,6 @@ extern (C++) final class TemplateValueParameter : TemplateParameter return defaultValue !is null; } - override RootObject dummyArg() - { - Expression e = specValue; - if (!e) - { - // Create a dummy value - auto pe = cast(void*)valType in edummies; - if (!pe) - { - e = valType.defaultInit(Loc.initial); - edummies[cast(void*)valType] = e; - } - else - e = *pe; - } - return e; - } - override void accept(Visitor v) { v.visit(this); @@ -3487,7 +3467,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter extern (D) __gshared Dsymbol sdummy = null; - extern (D) this(const ref Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe + extern (D) this(Loc loc, Identifier ident, Type specType, RootObject specAlias, RootObject defaultAlias) @safe { super(loc, ident); this.specType = specType; @@ -3525,7 +3505,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter return specAlias; } - override RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override RootObject defaultArg(Loc instLoc, Scope* sc) { RootObject da = defaultAlias; if (auto ta = isType(defaultAlias)) @@ -3553,18 +3533,6 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter return defaultAlias !is null; } - override RootObject dummyArg() - { - RootObject s = specAlias; - if (!s) - { - if (!sdummy) - sdummy = new Dsymbol(); - s = sdummy; - } - return s; - } - override void accept(Visitor v) { v.visit(this); @@ -3578,7 +3546,7 @@ extern (C++) final class TemplateAliasParameter : TemplateParameter */ extern (C++) final class TemplateTupleParameter : TemplateParameter { - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, ident); } @@ -3632,7 +3600,7 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter return null; } - override RootObject defaultArg(const ref Loc instLoc, Scope* sc) + override RootObject defaultArg(Loc instLoc, Scope* sc) { return null; } @@ -3642,11 +3610,6 @@ extern (C++) final class TemplateTupleParameter : TemplateParameter return false; } - override RootObject dummyArg() - { - return null; - } - override void accept(Visitor v) { v.visit(this); @@ -3679,7 +3642,22 @@ extern (C++) class TemplateInstance : ScopeDsymbol Dsymbol tempdecl; // referenced by foo.bar.abc Dsymbol enclosing; // if referencing local symbols, this is the context Dsymbol aliasdecl; // !=null if instance is an alias for its sole member - TemplateInstance inst; // refer to existing instance + + /** + If this is not null and it has a value that is not the current object, + then this field points to an existing template instance + and that object has been duplicated into us. + + If this object is a duplicate, + the ``memberOf`` field will be set to a root module (passed on CLI). + + This information is useful to deduplicate analysis that may occur + after semantic 3 has completed. + + See_Also: memberOf + */ + TemplateInstance inst; + ScopeDsymbol argsym; // argument symbol table size_t hash; // cached result of toHash() @@ -3691,7 +3669,15 @@ extern (C++) class TemplateInstance : ScopeDsymbol TemplateInstances* deferred; - Module memberOf; // if !null, then this TemplateInstance appears in memberOf.members[] + /** + If this is not null then this template instance appears in a root module's members. + + Note: This is not useful for determining duplication status of this template instance. + Use the field ``inst`` for determining if a template instance has been duplicated into this object. + + See_Also: inst + */ + Module memberOf; // Used to determine the instance needs code generation. // Note that these are inaccurate until semantic analysis phase completed. @@ -3738,13 +3724,14 @@ extern (C++) class TemplateInstance : ScopeDsymbol } } - extern (D) this(const ref Loc loc, Identifier ident, Objects* tiargs) scope + extern (D) this(Loc loc, Identifier ident, Objects* tiargs) scope { super(loc, null); static if (LOG) { printf("TemplateInstance(this = %p, ident = '%s')\n", this, ident ? ident.toChars() : "null"); } + this.dsym = DSYM.templateInstance; this.name = ident; this.tiargs = tiargs; } @@ -3753,13 +3740,14 @@ extern (C++) class TemplateInstance : ScopeDsymbol * This constructor is only called when we figured out which function * template to instantiate. */ - extern (D) this(const ref Loc loc, TemplateDeclaration td, Objects* tiargs) scope + extern (D) this(Loc loc, TemplateDeclaration td, Objects* tiargs) scope { super(loc, null); static if (LOG) { printf("TemplateInstance(this = %p, tempdecl = '%s')\n", this, td.toChars()); } + this.dsym = DSYM.templateInstance; this.name = td.ident; this.tiargs = tiargs; this.tempdecl = td; @@ -3830,19 +3818,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol return "template instance"; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - ps = null; - return true; - } - - override const(char)* toChars() const - { - OutBuffer buf; - toCBufferInstance(this, buf); - return buf.extractChars(); - } - override final const(char)* toPrettyCharsHelper() { OutBuffer buf; @@ -3889,7 +3864,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol // Set error here as we don't want it to depend on the number of // entries that are being printed. if (cl == Classification.error || - (cl == Classification.warning && global.params.warnings == DiagnosticReporting.error) || + (cl == Classification.warning && global.params.useWarnings == DiagnosticReporting.error) || (cl == Classification.deprecation && global.params.useDeprecated == DiagnosticReporting.error)) cur.errors = true; @@ -3905,7 +3880,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (n_instantiations <= max_shown) { for (TemplateInstance cur = this; cur; cur = cur.tinst) - printFn(cur.loc, format, cur.toChars()); + printFn(cur.loc, format, cur.toErrMsg()); } else if (n_instantiations - n_totalrecursions <= max_shown) { @@ -3973,66 +3948,63 @@ extern (C++) class TemplateInstance : ScopeDsymbol if (enclosing != ti.enclosing) { //printf("test2 enclosing %s ti.enclosing %s\n", enclosing ? enclosing.toChars() : "", ti.enclosing ? ti.enclosing.toChars() : ""); - goto Lnotequals; + return false; } //printf("parent = %s, ti.parent = %s\n", parent.toPrettyChars(), ti.parent.toPrettyChars()); if (!arrayObjectMatch(tdtypes, ti.tdtypes)) - goto Lnotequals; + return false; /* Template functions may have different instantiations based on * "auto ref" parameters. */ - if (auto fd = ti.toAlias().isFuncDeclaration()) - { - if (!fd.errors) - { - auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( - ArgumentList(this.fargs, this.fnames), null); + auto fd = ti.toAlias().isFuncDeclaration(); + if (!fd) + return true; + if (fd.errors) + return true; - // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d - // In that case, equalsx returns true to prevent endless template instantiations - // However, it can also mean the function was explicitly instantiated - // without function arguments: fail_compilation/fail14669 - // Hence the following check: - if (this.fargs && !resolvedArgs) - return true; + auto resolvedArgs = fd.type.isTypeFunction().resolveNamedArgs( + ArgumentList(this.fargs, this.fnames), null); - Expression[] args = resolvedArgs ? (*resolvedArgs)[] : []; + // resolvedArgs can be null when there's an error: fail_compilation/fail14669.d + // In that case, equalsx returns true to prevent endless template instantiations + // However, it can also mean the function was explicitly instantiated + // without function arguments: fail_compilation/fail14669 + // Hence the following check: + if (this.fargs && !resolvedArgs) + return true; - auto fparameters = fd.getParameterList(); - size_t nfparams = fparameters.length; // Num function parameters - for (size_t j = 0; j < nfparams; j++) - { - Parameter fparam = fparameters[j]; - if (fparam.storageClass & STC.autoref) // if "auto ref" - { - Expression farg = (j < args.length) ? args[j] : fparam.defaultArg; - // resolveNamedArgs strips trailing nulls / default params - // when it doesn't anymore, the ternary can be replaced with: - // assert(j < resolvedArgs.length); - if (!farg) - farg = fparam.defaultArg; - if (!farg) - goto Lnotequals; - if (farg.isLvalue()) - { - if (!(fparam.storageClass & STC.ref_)) - goto Lnotequals; // auto ref's don't match - } - else - { - if (fparam.storageClass & STC.ref_) - goto Lnotequals; // auto ref's don't match - } - } - } + Expression[] args = resolvedArgs ? (*resolvedArgs)[] : []; + + auto fparameters = fd.getParameterList(); + size_t nfparams = fparameters.length; // Num function parameters + for (size_t j = 0; j < nfparams; j++) + { + Parameter fparam = fparameters[j]; + if (!(fparam.storageClass & STC.autoref) ) // if "auto ref" + continue; + + Expression farg = (j < args.length) ? args[j] : fparam.defaultArg; + // resolveNamedArgs strips trailing nulls / default params + // when it doesn't anymore, the ternary can be replaced with: + // assert(j < resolvedArgs.length); + if (!farg) + farg = fparam.defaultArg; + if (!farg) + return false; + if (farg.isLvalue()) + { + if (!(fparam.storageClass & STC.ref_)) + return false; // auto ref's don't match + } + else + { + if (fparam.storageClass & STC.ref_) + return false; // auto ref's don't match } } return true; - - Lnotequals: - return false; } extern (D) final size_t toHash() @@ -4184,77 +4156,75 @@ extern (C++) class TemplateInstance : ScopeDsymbol // Elide codegen because there's no instantiation from any root modules. return false; } - else - { - // Prefer instantiations from non-root modules, to minimize object code size. - /* If a TemplateInstance is ever instantiated from a non-root module, - * we do not have to generate code for it, - * because it will be generated when the non-root module is compiled. - * - * But, if the non-root 'minst' imports any root modules, it might still need codegen. - * - * The problem is if A imports B, and B imports A, and both A - * and B instantiate the same template, does the compilation of A - * or the compilation of B do the actual instantiation? - * - * See https://issues.dlang.org/show_bug.cgi?id=2500. - * - * => Elide codegen if there is at least one instantiation from a non-root module - * which doesn't import any root modules. - */ - static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst) + // Prefer instantiations from non-root modules, to minimize object code size. + + /* If a TemplateInstance is ever instantiated from a non-root module, + * we do not have to generate code for it, + * because it will be generated when the non-root module is compiled. + * + * But, if the non-root 'minst' imports any root modules, it might still need codegen. + * + * The problem is if A imports B, and B imports A, and both A + * and B instantiate the same template, does the compilation of A + * or the compilation of B do the actual instantiation? + * + * See https://issues.dlang.org/show_bug.cgi?id=2500. + * + * => Elide codegen if there is at least one instantiation from a non-root module + * which doesn't import any root modules. + */ + static ThreeState needsCodegenRootOnly(TemplateInstance tithis, TemplateInstance tinst) + { + // If the ancestor isn't speculative, + // 1. do codegen if the ancestor needs it + // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) + if (tinst && tinst.inst) { - // If the ancestor isn't speculative, - // 1. do codegen if the ancestor needs it - // 2. elide codegen if the ancestor doesn't need it (non-root instantiation of ancestor incl. subtree) - if (tinst && tinst.inst) + tinst = tinst.inst; + const needsCodegen = tinst.needsCodegen(); // sets tinst.minst + if (tinst.minst) // not speculative { - tinst = tinst.inst; - const needsCodegen = tinst.needsCodegen(); // sets tinst.minst - if (tinst.minst) // not speculative - { - tithis.minst = tinst.minst; // cache result - return needsCodegen ? ThreeState.yes : ThreeState.no; - } + tithis.minst = tinst.minst; // cache result + return needsCodegen ? ThreeState.yes : ThreeState.no; } + } - // Elide codegen if `this` doesn't need it. - if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) - return ThreeState.no; + // Elide codegen if `this` doesn't need it. + if (tithis.minst && !tithis.minst.isRoot() && !tithis.minst.rootImports()) + return ThreeState.no; - return ThreeState.none; - } + return ThreeState.none; + } - if (const needsCodegen = needsCodegenRootOnly(this, tinst)) - return needsCodegen == ThreeState.yes ? true : false; + if (const needsCodegen = needsCodegenRootOnly(this, tinst)) + return needsCodegen == ThreeState.yes ? true : false; - // Elide codegen if a (non-speculative) sibling doesn't need it. - for (; tnext; tnext = tnext.tnext) + // Elide codegen if a (non-speculative) sibling doesn't need it. + for (; tnext; tnext = tnext.tnext) + { + const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst + if (tnext.minst) // not speculative { - const needsCodegen = needsCodegenRootOnly(tnext, tnext.tinst); // sets tnext.minst - if (tnext.minst) // not speculative + if (needsCodegen == ThreeState.no) { - if (needsCodegen == ThreeState.no) - { - minst = tnext.minst; // cache result - assert(!minst.isRoot() && !minst.rootImports()); - return false; - } - else if (!minst) - { - minst = tnext.minst; // cache result from non-speculative sibling - // continue searching - } - else if (needsCodegen != ThreeState.none) - break; + minst = tnext.minst; // cache result + assert(!minst.isRoot() && !minst.rootImports()); + return false; + } + else if (!minst) + { + minst = tnext.minst; // cache result from non-speculative sibling + // continue searching } + else if (needsCodegen != ThreeState.none) + break; } - - // Unless `this` is still speculative (=> all further siblings speculative too), - // do codegen because we found no guaranteed-codegen'd non-root instantiation. - return minst !is null; } + + // Unless `this` is still speculative (=> all further siblings speculative too), + // do codegen because we found no guaranteed-codegen'd non-root instantiation. + return minst !is null; } /********************************************** @@ -4503,7 +4473,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol * Returns: * false if one or more arguments have errors. */ - extern (D) static bool semanticTiargs(const ref Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) + extern (D) static bool semanticTiargs(Loc loc, Scope* sc, Objects* tiargs, int flags, TupleDeclaration atd = null) { // Run semantic on each argument, place results in tiargs[] //printf("+TemplateInstance.semanticTiargs()\n"); @@ -4513,9 +4483,13 @@ extern (C++) class TemplateInstance : ScopeDsymbol // The arguments are not treated as part of a default argument, // because they are evaluated at compile time. + const inCondition = sc.condition; sc = sc.push(); sc.inDefaultArg = false; + // https://issues.dlang.org/show_bug.cgi?id=24699 + sc.condition = inCondition; + for (size_t j = 0; j < tiargs.length; j++) { RootObject o = (*tiargs)[j]; @@ -4609,7 +4583,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol { if (ea.checkValue()) // check void expression ea = ErrorExp.get(); - uint olderrs = global.errors; + const olderrs = global.errors; ea = ea.ctfeInterpret(); if (global.errors != olderrs) ea = ErrorExp.get(); @@ -4821,7 +4795,7 @@ extern (C++) class TemplateInstance : ScopeDsymbol printf("TemplateInstance.findBestMatch()\n"); } - uint errs = global.errors; + const errs = global.errors; TemplateDeclaration td_last = null; Objects dedtypes; @@ -5036,135 +5010,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol return (errs == global.errors); } - /***************************************************** - * Determine if template instance is really a template function, - * and that template function needs to infer types from the function - * arguments. - * - * Like findBestMatch, iterate possible template candidates, - * but just looks only the necessity of type inference. - */ - extern (D) final bool needsTypeInference(Scope* sc, int flag = 0) - { - //printf("TemplateInstance.needsTypeInference() %s\n", toChars()); - if (semanticRun != PASS.initial) - return false; - - uint olderrs = global.errors; - Objects dedtypes; - size_t count = 0; - - auto tovers = tempdecl.isOverloadSet(); - foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) - { - Dsymbol dstart = tovers ? tovers.a[oi] : tempdecl; - int r = overloadApply(dstart, (Dsymbol s) - { - auto td = s.isTemplateDeclaration(); - if (!td) - return 0; - - /* If any of the overloaded template declarations need inference, - * then return true - */ - if (!td.onemember) - return 0; - if (auto td2 = td.onemember.isTemplateDeclaration()) - { - if (!td2.onemember || !td2.onemember.isFuncDeclaration()) - return 0; - if (tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0)) - return 0; - return 1; - } - auto fd = td.onemember.isFuncDeclaration(); - if (!fd || fd.type.ty != Tfunction) - return 0; - - foreach (tp; *td.parameters) - { - if (tp.isTemplateThisParameter()) - return 1; - } - - /* Determine if the instance arguments, tiargs, are all that is necessary - * to instantiate the template. - */ - //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length); - auto tf = fd.type.isTypeFunction(); - if (tf.parameterList.length) - { - auto tp = td.isVariadic(); - if (tp && td.parameters.length > 1) - return 1; - - if (!tp && tiargs.length < td.parameters.length) - { - // Can remain tiargs be filled by default arguments? - foreach (size_t i; tiargs.length .. td.parameters.length) - { - if (!(*td.parameters)[i].hasDefaultArg()) - return 1; - } - } - - foreach (i, fparam; tf.parameterList) - { - // 'auto ref' needs inference. - if (fparam.storageClass & STC.auto_) - return 1; - } - } - - if (!flag) - { - /* Calculate the need for overload resolution. - * When only one template can match with tiargs, inference is not necessary. - */ - dedtypes.setDim(td.parameters.length); - dedtypes.zero(); - if (td.semanticRun == PASS.initial) - { - if (td._scope) - { - // Try to fix forward reference. Ungag errors while doing so. - Ungag ungag = td.ungagSpeculative(); - td.dsymbolSemantic(td._scope); - } - if (td.semanticRun == PASS.initial) - { - .error(loc, "%s `%s` `%s` forward references template declaration `%s`", kind, toPrettyChars, toChars(), td.toChars()); - return 1; - } - } - MATCH m = matchWithInstance(sc, td, this, dedtypes, ArgumentList(), 0); - if (m == MATCH.nomatch) - return 0; - } - - /* If there is more than one function template which matches, we may - * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430) - */ - return ++count > 1 ? 1 : 0; - }); - if (r) - return true; - } - - if (olderrs != global.errors) - { - if (!global.gag) - { - errorSupplemental(loc, "while looking for match for `%s`", toChars()); - semanticRun = PASS.semanticdone; - inst = this; - } - errors = true; - } - //printf("false\n"); - return false; - } - /***************************************** * Determines if a TemplateInstance will need a nested * generation of the TemplateDeclaration. @@ -5486,11 +5331,6 @@ extern (C++) class TemplateInstance : ScopeDsymbol --nest; } - override final inout(TemplateInstance) isTemplateInstance() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -5628,12 +5468,13 @@ extern (C++) final class TemplateMixin : TemplateInstance { TypeQualified tqual; - extern (D) this(const ref Loc loc, Identifier ident, TypeQualified tqual, Objects* tiargs) + extern (D) this(Loc loc, Identifier ident, TypeQualified tqual, Objects* tiargs) { super(loc, tqual.idents.length ? cast(Identifier)tqual.idents[tqual.idents.length - 1] : (cast(TypeIdentifier)tqual).ident, tiargs ? tiargs : new Objects()); //printf("TemplateMixin(ident = '%s')\n", ident ? ident.toChars() : ""); + this.dsym = DSYM.templateMixin; this.ident = ident; this.tqual = tqual; } @@ -5649,24 +5490,12 @@ extern (C++) final class TemplateMixin : TemplateInstance return "mixin"; } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - return Dsymbol.oneMember(ps, ident); - } - override bool hasPointers() { //printf("TemplateMixin.hasPointers() %s\n", toChars()); return members.foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; } - override const(char)* toChars() const - { - OutBuffer buf; - toCBufferInstance(this, buf); - return buf.extractChars(); - } - extern (D) bool findTempDecl(Scope* sc) { // Follow qualifications to find the TemplateDeclaration @@ -5741,11 +5570,6 @@ extern (C++) final class TemplateMixin : TemplateInstance return true; } - override inout(TemplateMixin) isTemplateMixin() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -5767,7 +5591,7 @@ struct TemplateInstanceBox assert(this.ti.hash); } - size_t toHash() const @trusted pure nothrow + size_t toHash() const @safe pure nothrow { assert(ti.hash); return ti.hash; @@ -5883,8 +5707,8 @@ MATCH matchArg(TemplateParameter tp, Loc instLoc, Scope* sc, Objects* tiargs, si if (auto ttp = tp.isTemplateTupleParameter()) return matchArgTuple(ttp); - else - return matchArgParameter(); + + return matchArgParameter(); } MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, TemplateParameters* parameters, ref Objects dedtypes, Declaration* psparam) @@ -5987,7 +5811,7 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ /* If a function is really property-like, and then * it's CTFEable, ei will be a literal expression. */ - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); ei = resolveProperties(sc, ei); ei = ei.ctfeInterpret(); if (global.endGagging(olderrors) || ei.op == EXP.error) @@ -6260,14 +6084,13 @@ MATCH matchArg(TemplateParameter tp, Scope* sc, RootObject oarg, size_t i, Templ if (auto ttp = tp.isTemplateTypeParameter()) return matchArgType(ttp); - else if (auto tvp = tp.isTemplateValueParameter()) + if (auto tvp = tp.isTemplateValueParameter()) return matchArgValue(tvp); - else if (auto tap = tp.isTemplateAliasParameter()) + if (auto tap = tp.isTemplateAliasParameter()) return matchArgAlias(tap); - else if (auto ttp = tp.isTemplateTupleParameter()) + if (auto ttp = tp.isTemplateTupleParameter()) return matchArgTuple(ttp); - else - assert(0); + assert(0); } @@ -6353,8 +6176,8 @@ void printTemplateStats(bool listInstances, ErrorSink eSink) auto diff = b.ts.uniqueInstantiations - a.ts.uniqueInstantiations; if (diff) return diff; - else - return b.ts.numInstantiations - a.ts.numInstantiations; + + return b.ts.numInstantiations - a.ts.numInstantiations; } } @@ -6424,6 +6247,9 @@ void write(ref OutBuffer buf, RootObject obj) { if (obj) { - buf.writestring(obj.toChars()); + if (auto e = isExpression(obj)) + buf.writestring(e.toErrMsg()); + else + buf.writestring(obj.toChars()); } } diff --git a/gcc/d/dmd/dtoh.d b/gcc/d/dmd/dtoh.d index 2e2ced4..3946c25 100644 --- a/gcc/d/dmd/dtoh.d +++ b/gcc/d/dmd/dtoh.d @@ -2,12 +2,12 @@ * This module contains the implementation of the C++ header generation available through * the command line switch -Hc. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dtohd, _dtoh.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dtoh.d, _dtoh.d) * Documentation: https://dlang.org/phobos/dmd_dtoh.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dtoh.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dtoh.d */ module dmd.dtoh; @@ -104,7 +104,8 @@ void genCppHdrFiles(ref Modules ms) // Emit array compatibility because extern(C++) types may have slices // as members (as opposed to function parameters) - buf.writestring(` + if (v.hasDArray) + buf.writestring(` #ifdef CUSTOM_D_ARRAY_TYPE #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE #else @@ -133,6 +134,17 @@ struct _d_dynamicArray final #endif `); + if (v.hasExternSystem) + buf.writestring(` +#ifndef _WIN32 +#define EXTERN_SYSTEM_AFTER __stdcall +#define EXTERN_SYSTEM_BEFORE +#else +#define EXTERN_SYSTEM_AFTER +#define EXTERN_SYSTEM_BEFORE extern "C" +#endif +`); + if (v.hasReal) { hashIf(buf, "!defined(_d_real)"); @@ -248,7 +260,14 @@ public: OutBuffer* buf; /// The generated header uses `real` emitted as `_d_real`? - bool hasReal; + bool hasReal = false; + + /// The generated header has extern(System) functions, + /// which needs support macros in the header + bool hasExternSystem = false; + + /// There are functions taking slices, which need a compatibility struct for C++ + bool hasDArray = false; /// The generated header should contain comments for skipped declarations? const bool printIgnored; @@ -493,7 +512,7 @@ public: } } - if (global.params.warnings != DiagnosticReporting.off || canFix) + if (global.params.useWarnings != DiagnosticReporting.off || canFix) { // Warn about identifiers that are keywords in C++. if (auto kc = keywordClass(ident)) @@ -745,7 +764,7 @@ public: // Note that tf might be null for templated (member) functions auto tf = cast(AST.TypeFunction)fd.type; - if ((tf && (tf.linkage != LINK.c || adparent) && tf.linkage != LINK.cpp) || (!tf && fd.isPostBlitDeclaration())) + if ((tf && (tf.linkage != LINK.c || adparent) && tf.linkage != LINK.cpp && tf.linkage != LINK.windows) || (!tf && fd.isPostBlitDeclaration())) { ignored("function %s because of linkage", fd.toPrettyChars()); return checkFunctionNeedsPlaceholder(fd); @@ -793,8 +812,17 @@ public: writeProtection(fd.visibility.kind); - if (tf && tf.linkage == LINK.c) + if (fd._linkage == LINK.system) + { + hasExternSystem = true; + buf.writestring("EXTERN_SYSTEM_BEFORE "); + } + else if (tf && tf.linkage == LINK.c) buf.writestring("extern \"C\" "); + else if (tf && tf.linkage == LINK.windows) + { + // __stdcall is printed after return type + } else if (!adparent) buf.writestring("extern "); if (adparent && fd.isStatic()) @@ -948,7 +976,8 @@ public: { EnumKind kind = getEnumKind(type); - if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) { + if (vd.visibility.kind == AST.Visibility.Kind.none || vd.visibility.kind == AST.Visibility.Kind.private_) + { ignored("enum `%s` because it is `%s`.", vd.toPrettyChars(), AST.visibilityToChars(vd.visibility.kind)); return; } @@ -2058,6 +2087,8 @@ public: { debug (Debug_DtoH) mixin(traceVisit!t); + hasDArray = true; + if (t.isConst() || t.isImmutable()) buf.writestring("const "); buf.writestring("_d_dynamicArray< "); @@ -2261,8 +2292,8 @@ public: * Writes the function signature to `buf`. * * Params: - * fd = the function to print * tf = fd's type + * fd = the function to print */ private void funcToBuffer(AST.TypeFunction tf, AST.FuncDeclaration fd) { @@ -2294,9 +2325,18 @@ public: assert(tf.next, fd.loc.toChars().toDString()); tf.next == AST.Type.tsize_t ? originalType.next.accept(this) : tf.next.accept(this); - if (tf.isref) + if (tf.isRef) buf.writeByte('&'); buf.writeByte(' '); + + if (fd._linkage == LINK.system) + { + buf.writestring("EXTERN_SYSTEM_AFTER "); + } + else if (tf.linkage == LINK.windows) + { + buf.writestring("__stdcall "); + } writeIdentifier(fd); } @@ -2757,7 +2797,7 @@ public: { if (vd._init && !vd._init.isVoidInitializer()) return AST.initializerToExpression(vd._init); - else if (auto ts = vd.type.isTypeStruct()) + if (auto ts = vd.type.isTypeStruct()) { if (!ts.sym.noDefaultCtor && !ts.sym.isUnionDeclaration()) { diff --git a/gcc/d/dmd/dversion.d b/gcc/d/dmd/dversion.d index 2e3b352..26528e9 100644 --- a/gcc/d/dmd/dversion.d +++ b/gcc/d/dmd/dversion.d @@ -4,12 +4,12 @@ * Specification: $(LINK2 https://dlang.org/spec/version.html#version-specification, Version Specification), * $(LINK2 https://dlang.org/spec/version.html#debug_specification, Debug Specification). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dversion.d, _dversion.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/dversion.d, _dversion.d) * Documentation: https://dlang.org/phobos/dmd_dversion.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dversion.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/dversion.d */ module dmd.dversion; @@ -20,7 +20,6 @@ import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; import dmd.dsymbolsem; -import dmd.errors; import dmd.globals; import dmd.identifier; import dmd.location; @@ -30,21 +29,17 @@ import dmd.visitor; /*********************************************************** * DebugSymbol's happen for statements like: * debug = identifier; - * debug = integer; */ extern (C++) final class DebugSymbol : Dsymbol { - uint level; - - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { - super(loc, ident); + super(DSYM.debugSymbol, loc, ident); } - extern (D) this(const ref Loc loc, uint level) @safe + extern (D) this(Loc loc) @safe { - super(loc, null); - this.level = level; + super(DSYM.aliasDeclaration, loc, null); } override DebugSymbol syntaxCopy(Dsymbol s) @@ -52,32 +47,14 @@ extern (C++) final class DebugSymbol : Dsymbol assert(!s); auto ds = new DebugSymbol(loc, ident); ds.comment = comment; - ds.level = level; return ds; } - override const(char)* toChars() const nothrow - { - if (ident) - return ident.toChars(); - else - { - OutBuffer buf; - buf.print(level); - return buf.extractChars(); - } - } - override const(char)* kind() const nothrow { return "debug"; } - override inout(DebugSymbol) isDebugSymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -87,54 +64,33 @@ extern (C++) final class DebugSymbol : Dsymbol /*********************************************************** * VersionSymbol's happen for statements like: * version = identifier; - * version = integer; */ extern (C++) final class VersionSymbol : Dsymbol { - uint level; - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { - super(loc, ident); + super(DSYM.versionSymbol, loc, ident); } - extern (D) this(const ref Loc loc, uint level) @safe + extern (D) this(Loc loc) @safe { - super(loc, null); - this.level = level; + super(DSYM.versionSymbol, loc, null); } override VersionSymbol syntaxCopy(Dsymbol s) { assert(!s); - auto ds = ident ? new VersionSymbol(loc, ident) - : new VersionSymbol(loc, level); + auto ds = new VersionSymbol(loc, ident); ds.comment = comment; return ds; } - override const(char)* toChars() const nothrow - { - if (ident) - return ident.toChars(); - else - { - OutBuffer buf; - buf.print(level); - return buf.extractChars(); - } - } - override const(char)* kind() const nothrow { return "version"; } - override inout(VersionSymbol) isVersionSymbol() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/entity.d b/gcc/d/dmd/entity.d index af74c3b..a70029b 100644 --- a/gcc/d/dmd/entity.d +++ b/gcc/d/dmd/entity.d @@ -3,12 +3,12 @@ * * Specification $(LINK2 https://dlang.org/spec/entity.html, Named Character Entities) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/entity.d, _entity.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/entity.d, _entity.d) * Documentation: https://dlang.org/phobos/dmd_entity.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/entity.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/entity.d */ module dmd.entity; diff --git a/gcc/d/dmd/enum.h b/gcc/d/dmd/enum.h index 4e6fbe2..6d5d302 100644 --- a/gcc/d/dmd/enum.h +++ b/gcc/d/dmd/enum.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -20,7 +20,7 @@ class Expression; namespace dmd { // in enumsem.d - Expression *getDefaultValue(EnumDeclaration *ed, const Loc &loc); + Expression *getDefaultValue(EnumDeclaration *ed, Loc loc); } class EnumDeclaration final : public ScopeDsymbol @@ -52,14 +52,12 @@ public: bool inuse(bool v); EnumDeclaration *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; Type *getType() override; const char *kind() const override; bool isDeprecated() const override; // is Dsymbol deprecated? Visibility visible() override; bool isSpecial() const; - EnumDeclaration *isEnumDeclaration() override { return this; } Symbol *sinit; void accept(Visitor *v) override { v->visit(this); } @@ -87,6 +85,5 @@ public: EnumMember *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - EnumMember *isEnumMember() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/enumsem.d b/gcc/d/dmd/enumsem.d index 3886ca2..4f0d4e5 100644 --- a/gcc/d/dmd/enumsem.d +++ b/gcc/d/dmd/enumsem.d @@ -1,12 +1,12 @@ /** * Does the semantic passes on enums. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/enumsem.d, _enumsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/enumsem.d, _enumsem.d) * Documentation: https://dlang.org/phobos/dmd_enumsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/enumsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/enumsem.d */ module dmd.enumsem; @@ -30,7 +30,6 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -119,7 +118,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) ed.cppnamespace = sc.namespace; ed.semanticRun = PASS.semantic; - UserAttributeDeclaration.checkGNUABITag(ed, sc.linkage); + checkGNUABITag(ed, sc.linkage); checkMustUseReserved(ed); if (!ed.members && !ed.memtype) // enum ident; @@ -186,13 +185,13 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) if (ed.members.length == 0) { - .error(ed.loc, "%s `%s enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars()); + .error(ed.loc, "%s `%s` enum `%s` must have at least one member", ed.kind, ed.toPrettyChars, ed.toChars()); ed.errors = true; ed.semanticRun = PASS.semanticdone; return; } - if (!(sc.flags & SCOPE.Cfile)) // C enum remains incomplete until members are done + if (!sc.inCfile) // C enum remains incomplete until members are done ed.semanticRun = PASS.semanticdone; version (none) @@ -219,8 +218,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) */ ed.members.foreachDsymbol( (s) { - EnumMember em = s.isEnumMember(); - if (em) + if (EnumMember em = s.isEnumMember()) em._scope = sce; }); @@ -230,7 +228,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) */ addEnumMembersToSymtab(ed, sc, sc.getScopesym()); - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* C11 6.7.2.2 */ @@ -325,7 +323,10 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) if (EnumMember em = s.isEnumMember()) { em.type = commonType; - em.value = em.value.castTo(sc, commonType); + // optimize out the cast so that other parts of the compiler can + // assume that an integral enum's members are `IntegerExp`s. + // https://issues.dlang.org/show_bug.cgi?id=24504 + em.value = em.value.castTo(sc, commonType).optimize(WANTvalue); } }); } @@ -346,7 +347,7 @@ void enumSemantic(Scope* sc, EnumDeclaration ed) //printf("members = %s\n", members.toChars()); } -Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) +Expression getDefaultValue(EnumDeclaration ed, Loc loc) { Expression handleErrors(){ ed.defaultval = ErrorExp.get(); @@ -383,8 +384,7 @@ Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) foreach (const i; 0 .. ed.members.length) { - EnumMember em = (*ed.members)[i].isEnumMember(); - if (em) + if (EnumMember em = (*ed.members)[i].isEnumMember()) { if (em.semanticRun < PASS.semanticdone) { @@ -399,7 +399,7 @@ Expression getDefaultValue(EnumDeclaration ed, const ref Loc loc) return handleErrors(); } -Type getMemtype(EnumDeclaration ed, const ref Loc loc) +Type getMemtype(EnumDeclaration ed, Loc loc) { if (ed._scope) { @@ -698,7 +698,7 @@ void enumMemberSemantic(Scope* sc, EnumMember em) if (e.op == EXP.error) return errorReturn(); - if (e.type.isfloating()) + if (e.type.isFloating()) { // Check that e != eprev (not always true for floats) Expression etest = new EqualExp(EXP.equal, em.loc, e, eprev); diff --git a/gcc/d/dmd/errors.d b/gcc/d/dmd/errors.d index 79efc6e..90c18c3 100644 --- a/gcc/d/dmd/errors.d +++ b/gcc/d/dmd/errors.d @@ -1,20 +1,22 @@ /** * Functions for raising errors. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/errors.d, _errors.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errors.d, _errors.d) * Documentation: https://dlang.org/phobos/dmd_errors.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errors.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/errors.d */ module dmd.errors; -import core.stdc.stdarg; +public import core.stdc.stdarg; +public import dmd.root.string: fTuple; import dmd.errorsink; import dmd.globals; import dmd.location; +import dmd.root.string; nothrow: @@ -37,60 +39,39 @@ class ErrorSinkCompiler : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) + void verror(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); - va_end(ap); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) + void verrorSupplemental(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.error); - va_end(ap); } - void warning(const ref Loc loc, const(char)* format, ...) + void vwarning(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.warning); - va_end(ap); } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.warning); - va_end(ap); } - void deprecation(const ref Loc loc, const(char)* format, ...) + void vdeprecation(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.deprecation); - va_end(ap); } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation); - va_end(ap); } - void message(const ref Loc loc, const(char)* format, ...) + void vmessage(Loc loc, const(char)* format, va_list ap) { - va_list ap; - va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.message); - va_end(ap); } } @@ -130,9 +111,9 @@ enum Color : int static if (__VERSION__ < 2092) - private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {} + private extern (C++) void noop(Loc loc, const(char)* format, ...) {} else - pragma(printf) private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {} + pragma(printf) private extern (C++) void noop(Loc loc, const(char)* format, ...) {} package auto previewErrorFunc(bool isDeprecated, FeatureState featureState) @safe @nogc pure nothrow @@ -174,7 +155,7 @@ package auto previewSupplementalFunc(bool isDeprecated, FeatureState featureStat * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void error(const ref Loc loc, const(char)* format, ...) + extern (C++) void error(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -182,7 +163,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void error(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void error(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -202,7 +183,7 @@ else static if (__VERSION__ < 2092) extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) { - const loc = Loc(filename, linnum, charnum); + const loc = SourceLoc(filename.toDString, linnum, charnum); va_list ap; va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); @@ -211,13 +192,23 @@ static if (__VERSION__ < 2092) else pragma(printf) extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) { - const loc = Loc(filename, linnum, charnum); + const loc = SourceLoc(filename.toDString, linnum, charnum); va_list ap; va_start(ap, format); verrorReport(loc, format, ap, ErrorKind.error); va_end(ap); } +/// Callback for when the backend wants to report an error +extern(C++) void errorBackend(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) +{ + const loc = SourceLoc(filename.toDString, linnum, charnum); + va_list ap; + va_start(ap, format); + verrorReport(loc, format, ap, ErrorKind.error); + va_end(ap); +} + /** * Print additional details about an error message. * Doesn't increase the error count or print an additional error prefix. @@ -227,7 +218,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...) + extern (C++) void errorSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -235,7 +226,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void errorSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -251,7 +242,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void warning(const ref Loc loc, const(char)* format, ...) + extern (C++) void warning(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -259,7 +250,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void warning(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void warning(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -276,7 +267,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...) + extern (C++) void warningSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -284,7 +275,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void warningSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -301,7 +292,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...) + extern (C++) void deprecation(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -309,7 +300,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void deprecation(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -326,7 +317,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + extern (C++) void deprecationSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -334,7 +325,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void deprecationSupplemental(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -351,7 +342,7 @@ else * ... = printf-style variadic arguments */ static if (__VERSION__ < 2092) - extern (C++) void message(const ref Loc loc, const(char)* format, ...) + extern (C++) void message(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -359,7 +350,7 @@ static if (__VERSION__ < 2092) va_end(ap); } else - pragma(printf) extern (C++) void message(const ref Loc loc, const(char)* format, ...) + pragma(printf) extern (C++) void message(Loc loc, const(char)* format, ...) { va_list ap; va_start(ap, format); @@ -395,7 +386,7 @@ else * see verrorReport for arguments * Returns: true if error handling is done, false to continue printing to stderr */ -alias DiagnosticHandler = bool delegate(const ref Loc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2); +alias DiagnosticHandler = bool delegate(const ref SourceLoc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2); /** * The diagnostic handler. @@ -441,7 +432,13 @@ else * p1 = additional message prefix * p2 = additional message prefix */ -extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null); +private extern(C++) void verrorReport(Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null) +{ + return verrorReport(loc.SourceLoc, format, ap, kind, p1, p2); +} + +/// ditto +private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null); /** * Implements $(D errorSupplemental), $(D warningSupplemental), and @@ -454,7 +451,13 @@ extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list a * ap = printf-style variadic arguments * kind = kind of error being printed */ -extern (C++) void verrorReportSupplemental(const ref Loc loc, const(char)* format, va_list ap, ErrorKind kind); +private extern(C++) void verrorReportSupplemental(Loc loc, const(char)* format, va_list ap, ErrorKind kind) +{ + return verrorReportSupplemental(loc.SourceLoc, format, ap, kind); +} + +/// ditto +private extern(C++) void verrorReportSupplemental(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind); /** * The type of the fatal error handler diff --git a/gcc/d/dmd/errors.h b/gcc/d/dmd/errors.h index a47b5aa..a2d0f36 100644 --- a/gcc/d/dmd/errors.h +++ b/gcc/d/dmd/errors.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -31,20 +31,17 @@ enum class ErrorKind #endif // Print a warning, deprecation, or error, accepts printf-like format specifiers. -D_ATTRIBUTE_FORMAT(2, 3) void warning(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void warningSupplemental(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void deprecation(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void deprecationSupplemental(const Loc& loc, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void error(const Loc& loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void warning(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void warningSupplemental(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void deprecation(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void deprecationSupplemental(Loc loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void error(Loc loc, const char *format, ...); D_ATTRIBUTE_FORMAT(4, 5) void error(const char *filename, unsigned linnum, unsigned charnum, const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void errorSupplemental(const Loc& loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void errorSupplemental(Loc loc, const char *format, ...); D_ATTRIBUTE_FORMAT(1, 2) void message(const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 3) void message(const Loc& loc, const char *format, ...); +D_ATTRIBUTE_FORMAT(2, 3) void message(Loc loc, const char *format, ...); D_ATTRIBUTE_FORMAT(1, 2) void tip(const char *format, ...); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReport(const Loc& loc, const char *format, va_list ap, ErrorKind kind, const char *p1 = NULL, const char *p2 = NULL); -D_ATTRIBUTE_FORMAT(2, 0) void verrorReportSupplemental(const Loc& loc, const char* format, va_list ap, ErrorKind kind); - #if defined(__GNUC__) || defined(__clang__) #define D_ATTRIBUTE_NORETURN __attribute__((noreturn)) #elif _MSC_VER diff --git a/gcc/d/dmd/errorsink.d b/gcc/d/dmd/errorsink.d index afea689..5793ef1 100644 --- a/gcc/d/dmd/errorsink.d +++ b/gcc/d/dmd/errorsink.d @@ -1,16 +1,18 @@ /** * Provides an abstraction for what to do with error messages. * - * Copyright: Copyright (C) 2023-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2023-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/errorsink.d, _errorsink.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/errorsink.d, _errorsink.d) * Documentation: https://dlang.org/phobos/dmd_errorsink.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errorsink.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/errorsink.d */ module dmd.errorsink; +import core.stdc.stdarg; + import dmd.location; /*************************************** @@ -21,19 +23,78 @@ abstract class ErrorSink nothrow: extern (C++): - void error(const ref Loc loc, const(char)* format, ...); + void verror(Loc loc, const(char)* format, va_list ap); + void verrorSupplemental(Loc loc, const(char)* format, va_list ap); + void vwarning(Loc loc, const(char)* format, va_list ap); + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap); + void vmessage(Loc loc, const(char)* format, va_list ap); + void vdeprecation(Loc loc, const(char)* format, va_list ap); + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap); - void errorSupplemental(const ref Loc loc, const(char)* format, ...); + void error(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); + } - void warning(const ref Loc loc, const(char)* format, ...); + void errorSupplemental(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + verrorSupplemental(loc, format, ap); + va_end(ap); + } - void warningSupplemental(const ref Loc loc, const(char)* format, ...); + void warning(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vwarning(loc, format, ap); + va_end(ap); + } + + void warningSupplemental(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vwarningSupplemental(loc, format, ap); + va_end(ap); + } + + void message(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vmessage(loc, format, ap); + va_end(ap); + } - void message(const ref Loc loc, const(char)* format, ...); + void deprecation(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vdeprecation(loc, format, ap); + va_end(ap); + } - void deprecation(const ref Loc loc, const(char)* format, ...); + void deprecationSupplemental(Loc loc, const(char)* format, ...) + { + va_list ap; + va_start(ap, format); + vdeprecationSupplemental(loc, format, ap); + va_end(ap); + } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...); + /** + * This will be called to indicate compilation has either + * finished or terminated, no more errors are possible - it's + * now the time to print any stored errors. + * + * The default implementation does nothing since most error sinks have no state + */ + void plugSink() {} } /***************************************** @@ -45,19 +106,19 @@ class ErrorSinkNull : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) { } + void verror(Loc loc, const(char)* format, va_list ap) { } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) { } + void verrorSupplemental(Loc loc, const(char)* format, va_list ap) { } - void warning(const ref Loc loc, const(char)* format, ...) { } + void vwarning(Loc loc, const(char)* format, va_list ap) { } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap) { } - void message(const ref Loc loc, const(char)* format, ...) { } + void vmessage(Loc loc, const(char)* format, va_list ap) { } - void deprecation(const ref Loc loc, const(char)* format, ...) { } + void vdeprecation(Loc loc, const(char)* format, va_list ap) { } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap) { } } /***************************************** @@ -71,7 +132,7 @@ class ErrorSinkLatch : ErrorSinkNull bool sawErrors; - void error(const ref Loc loc, const(char)* format, ...) { sawErrors = true; } + void verror(Loc loc, const(char)* format, va_list ap) { sawErrors = true; } } /***************************************** @@ -87,7 +148,7 @@ class ErrorSinkStderr : ErrorSink extern (C++): override: - void error(const ref Loc loc, const(char)* format, ...) + void verror(Loc loc, const(char)* format, va_list ap) { fputs("Error: ", stderr); const p = loc.toChars(); @@ -97,16 +158,13 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void errorSupplemental(const ref Loc loc, const(char)* format, ...) { } + void verrorSupplemental(Loc loc, const(char)* format, va_list ap) { } - void warning(const ref Loc loc, const(char)* format, ...) + void vwarning(Loc loc, const(char)* format, va_list ap) { fputs("Warning: ", stderr); const p = loc.toChars(); @@ -116,16 +174,13 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void warningSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vwarningSupplemental(Loc loc, const(char)* format, va_list ap) { } - void deprecation(const ref Loc loc, const(char)* format, ...) + void vdeprecation(Loc loc, const(char)* format, va_list ap) { fputs("Deprecation: ", stderr); const p = loc.toChars(); @@ -135,14 +190,11 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void message(const ref Loc loc, const(char)* format, ...) + void vmessage(Loc loc, const(char)* format, va_list ap) { const p = loc.toChars(); if (*p) @@ -151,12 +203,9 @@ class ErrorSinkStderr : ErrorSink //mem.xfree(cast(void*)p); // loc should provide the free() } - va_list ap; - va_start(ap, format); vfprintf(stderr, format, ap); fputc('\n', stderr); - va_end(ap); } - void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) { } + void vdeprecationSupplemental(Loc loc, const(char)* format, va_list ap) { } } diff --git a/gcc/d/dmd/escape.d b/gcc/d/dmd/escape.d index 3e17ff4..a0c5472 100644 --- a/gcc/d/dmd/escape.d +++ b/gcc/d/dmd/escape.d @@ -1,12 +1,12 @@ /** * Most of the logic to implement scoped pointers and scoped references is here. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/escape.d, _escape.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/escape.d, _escape.d) * Documentation: https://dlang.org/phobos/dmd_escape.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/escape.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/escape.d */ module dmd.escape; @@ -25,6 +25,7 @@ import dmd.dsymbol; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals : FeatureState; import dmd.id; import dmd.identifier; @@ -33,6 +34,7 @@ import dmd.location; import dmd.mtype; import dmd.printast; import dmd.rootobject; +import dmd.safe; import dmd.tokens; import dmd.typesem : hasPointers, parameterStorageClass; import dmd.visitor; @@ -73,7 +75,7 @@ package(dmd) struct EscapeState * `true` if error */ public -bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, +bool checkMutableArguments(ref Scope sc, FuncDeclaration fd, TypeFunction tf, Expression ethis, Expressions* arguments, bool gag) { enum log = false; @@ -92,7 +94,8 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, struct EscapeBy { - EscapeByResults er; + VarDeclarations byref; + VarDeclarations byvalue; Parameter param; // null if no Parameter for this argument bool isMutable; // true if reference to mutable } @@ -150,14 +153,21 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, refs = true; auto var = outerVars[i - (len - outerVars.length)]; eb.isMutable = var.type.isMutable(); - eb.er.pushRef(var, false); + eb.byref.push(var); continue; } + void onRef(VarDeclaration v, bool transition) { eb.byref.push(v); } + void onValue(VarDeclaration v) { eb.byvalue.push(v); } + void onFunc(FuncDeclaration fd, bool called) {} + void onExp(Expression e, bool transition) {} + + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + if (refs) - escapeByRef(arg, &eb.er); + escapeByRef(arg, er); else - escapeByValue(arg, &eb.er); + escapeByValue(arg, er); } void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2, @@ -170,7 +180,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, if (!(eb.isMutable || eb2.isMutable)) return; - if (!tf.islive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe())) + if (!tf.isLive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && setFunctionToUnsafe(sc.func))) return; if (!gag) @@ -193,7 +203,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, void escape(size_t i, ref EscapeBy eb, bool byval) { - foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref) + foreach (VarDeclaration v; byval ? eb.byvalue : eb.byref) { if (log) { @@ -204,7 +214,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, continue; foreach (ref eb2; escapeBy[i + 1 .. $]) { - foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref) + foreach (VarDeclaration v2; byval ? eb2.byvalue : eb2.byref) { checkOnePair(i, eb, eb2, v, v2, byval); } @@ -231,7 +241,7 @@ bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, * `true` if any elements escaped */ public -bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag) +bool checkArrayLiteralEscape(ref Scope sc, ArrayLiteralExp ae, bool gag) { bool errors; if (ae.basis) @@ -255,7 +265,7 @@ bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag) * `true` if any elements escaped */ public -bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) +bool checkAssocArrayLiteralEscape(ref Scope sc, AssocArrayLiteralExp ae, bool gag) { bool errors; foreach (ex; *ae.keys) @@ -324,7 +334,7 @@ void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit) * `true` if pointers to the stack can escape via assignment */ public -bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag) +bool checkParamArgumentEscape(ref Scope sc, FuncDeclaration fdc, Identifier parId, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag) { enum log = false; if (log) printf("checkParamArgumentEscape(arg: %s par: %s parSTC: %llx)\n", @@ -335,22 +345,6 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, if (!arg.type.hasPointers()) return false; - EscapeByResults er; - - escapeByValue(arg, &er); - - if (parStc & STC.scope_) - { - // These errors only apply to non-scope parameters - // When the parameter is `scope`, only `checkScopeVarAddr` on `er.byref` is needed - er.byfunc.setDim(0); - er.byvalue.setDim(0); - er.byexp.setDim(0); - } - - if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length) - return false; - bool result = false; /* 'v' is assigned unsafely to 'par' @@ -360,7 +354,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, if (assertmsg) { result |= sc.setUnsafeDIP1000(gag, arg.loc, - desc ~ " `%s` assigned to non-scope parameter calling `assert()`", v); + "assigning" ~ desc ~ " `%s` to non-scope parameter calling `assert()`", v); return; } @@ -368,9 +362,9 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, const(char)* msg = (isThis) ? (desc ~ " `%s` calling non-scope member function `%s.%s()`") : - (fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s` calling `%s`") : - (fdc && !parId) ? (desc ~ " `%s` assigned to non-scope anonymous parameter calling `%s`") : - (!fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s`") : + (fdc && parId) ? ("assigning " ~ desc ~ " `%s` to non-scope parameter `%s` calling `%s`") : + (fdc && !parId) ? ("assigning " ~ desc ~ " `%s` to non-scope anonymous parameter calling `%s`") : + (!fdc && parId) ? ("assigning " ~ desc ~ " `%s` to non-scope parameter `%s`") : (desc ~ " `%s` assigned to non-scope anonymous parameter"); if (isThis ? @@ -382,51 +376,45 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, } } - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue %s\n", v.toChars()); - if (v.isDataseg()) - continue; - - Dsymbol p = v.toParent2(); + if (parStc & STC.scope_) + return; - notMaybeScope(v, vPar); + doNotInferScope(v, vPar); if (v.isScope()) { unsafeAssign!"scope variable"(v); } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - unsafeAssign!"variadic variable"(v); - } } - foreach (VarDeclaration v; er.byref) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref %s\n", v.toChars()); - if (v.isDataseg()) - continue; Dsymbol p = v.toParent2(); - notMaybeScope(v, arg); + doNotInferScope(v, arg); if (checkScopeVarAddr(v, arg, sc, gag)) { result = true; - continue; + return; } if (p == sc.func && !(parStc & STC.scope_)) { unsafeAssign!"reference to local variable"(v); - continue; + return; } } - foreach (FuncDeclaration fd; er.byfunc) + void onFunc(FuncDeclaration fd, bool called) { //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf); + if (parStc & STC.scope_) + return; VarDeclarations vars; findAllOuterAccessedVariables(fd, &vars); @@ -437,28 +425,29 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, Dsymbol p = v.toParent2(); - notMaybeScope(v, arg); + doNotInferScope(v, arg); if ((v.isReference() || v.isScope()) && p == sc.func) { unsafeAssign!"reference to local"(v); - continue; + return; } } } - if (!sc.func) - return result; - - foreach (Expression ee; er.byexp) + void onExp(Expression ee, bool retRefTransition) { + if (parStc & STC.scope_) + return; const(char)* msg = parId ? - "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`" : - "reference to stack allocated value returned by `%s` assigned to non-scope anonymous parameter"; + "assigning reference to stack allocated value returned by `%s` to non-scope parameter `%s`" : + "assigning reference to stack allocated value returned by `%s` to non-scope anonymous parameter"; result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, parId); } + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + escapeByValue(arg, er); return result; } @@ -476,7 +465,7 @@ bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, * `true` if assignment to `firstArg` would cause an error */ public -bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Parameter param, bool gag) +bool checkParamArgumentReturn(ref Scope sc, Expression firstArg, Expression arg, Parameter param, bool gag) { enum log = false; if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n", @@ -512,7 +501,7 @@ bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Pa * `true` if construction would cause an escaping reference error */ public -bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag) +bool checkConstructorEscape(ref Scope sc, CallExp ce, bool gag) { enum log = false; if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars()); @@ -579,7 +568,7 @@ public ReturnParamDest returnParamDest(TypeFunction tf, Type tthis) { assert(tf); - if (tf.isctor) + if (tf.isCtor) return ReturnParamDest.this_; if (!tf.nextOf() || (tf.nextOf().ty != Tvoid)) @@ -609,7 +598,7 @@ ReturnParamDest returnParamDest(TypeFunction tf, Type tthis) * `true` if pointers to the stack can escape via assignment */ public -bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) +bool checkAssignEscape(ref Scope sc, Expression e, bool gag, bool byRef) { enum log = false; if (log) printf("checkAssignEscape(e: %s, byRef: %d)\n", e.toChars(), byRef); @@ -624,17 +613,6 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (!e1.type.hasPointers()) return false; - if (e1.isSliceExp()) - { - if (VarDeclaration va = expToVariable(e1)) - { - if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable - !va.type.hasPointers()) - return false; - } - else - return false; - } /* The struct literal case can arise from the S(e2) constructor call: * return S(e2); @@ -645,17 +623,24 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (e1.isStructLiteralExp()) return false; - VarDeclaration va = expToVariable(e1); - EscapeByResults er; - - if (byRef) - escapeByRef(e2, &er); - else - escapeByValue(e2, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length) - return false; + int deref; + VarDeclaration va = expToVariable(e1, deref); + // transitive scope not implemented, so can't assign scope pointers to a dereferenced variable + if (deref > 0) + va = null; + if (e1.isSliceExp()) + { + // slice-copy is not assigning a pointer, but copying array content + if (va) + { + if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable + !va.type.hasPointers()) + return false; + } + else + return false; + } if (va && e.op == EXP.concatenateElemAssign) { @@ -669,23 +654,17 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) va = null; } - if (va && e1.isDotVarExp() && va.type.toBasetype().isTypeClass()) + if (e.op == EXP.construct && va && (va.storage_class & STC.temp) && va._init) { - /* https://issues.dlang.org/show_bug.cgi?id=17949 - * Draw an equivalence between: - * *q = p; - * and: - * va.field = e2; - * since we are not assigning to va, but are assigning indirectly through class reference va. - */ - va = null; + // Initializing a temporary is safe, `escapeExp` will forward such vars + // to their `va._init` if needed. + return false; } if (log && va) printf("va: %s\n", va.toChars()); FuncDeclaration fd = sc.func; - // Determine if va is a `ref` parameter, so it has a lifetime exceding the function scope const bool vaIsRef = va && va.isParameter() && va.isReference(); if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars()); @@ -701,7 +680,10 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) vaIsFirstRef = va == fd.vthis; break; case ReturnParamDest.firstArg: - vaIsFirstRef = (*fd.parameters)[0] == va; + // While you'd expect fd.parameters[0] to exist in this case, the compiler-generated + // expression that initializes an `out int* p = null` is analyzed before fd.parameters + // is created, so we still do a null and length check + vaIsFirstRef = fd.parameters && 0 < fd.parameters.length && (*fd.parameters)[0] == va; break; case ReturnParamDest.returnVal: break; @@ -710,35 +692,22 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars()); bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue: %s\n", v.toChars()); - if (v.isDataseg()) - continue; if (v == va) - continue; + return; Dsymbol p = v.toParent2(); - if (va && !vaIsRef && !va.isScope() && !v.isScope() && - !v.isTypesafeVariadicArray && !va.isTypesafeVariadicArray && - (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) && - p == fd) - { - /* Add v to va's list of dependencies - */ - va.addMaybe(v); - continue; - } - if (vaIsFirstRef && p == fd) { inferReturn(fd, v, /*returnScope:*/ true); } if (!(va && va.isScope()) || vaIsRef) - notMaybeScope(v, e); + doNotInferScope(v, e); if (v.isScope()) { @@ -746,7 +715,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) { // va=v, where v is `return scope` if (inferScope(va)) - continue; + return; } // If va's lifetime encloses v's, then error @@ -757,25 +726,23 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) { case EnclosedBy.none: assert(0); case EnclosedBy.returnScope: - msg = "scope variable `%s` assigned to return scope `%s`"; + msg = "assigning scope variable `%s` to return scope `%s`"; break; case EnclosedBy.longerScope: - if (v.storage_class & STC.temp) - continue; - msg = "scope variable `%s` assigned to `%s` with longer lifetime"; + msg = "assigning scope variable `%s` to `%s` with longer lifetime"; break; case EnclosedBy.refVar: - msg = "scope variable `%s` assigned to `ref` variable `%s` with longer lifetime"; + msg = "assigning scope variable `%s` to `ref` variable `%s` with longer lifetime"; break; case EnclosedBy.global: - msg = "scope variable `%s` assigned to global variable `%s`"; + msg = "assigning scope variable `%s` to global variable `%s`"; break; } if (sc.setUnsafeDIP1000(gag, ae.loc, msg, v, va)) { result = true; - continue; + return; } } @@ -793,15 +760,9 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) if (isRefReturnScope(va.storage_class)) va.storage_class |= STC.returnScope; } - continue; + return; } - result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1); - } - else if (v.isTypesafeVariadicArray && p == fd) - { - if (inferScope(va)) - continue; - result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1); + result |= sc.setUnsafeDIP1000(gag, ae.loc, "assigning scope variable `%s` to non-scope `%s`", v, e1); } else { @@ -809,20 +770,19 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) * It may escape via that assignment, therefore, v can never be 'scope'. */ //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__); - doNotInferScope(v, e); + if (!v.isParameter) + doNotInferScope(v, e); } } - foreach (VarDeclaration v; er.byref) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref: %s\n", v.toChars()); - if (v.isDataseg()) - continue; if (checkScopeVarAddr(v, ae, sc, gag)) { result = true; - continue; + return; } if (va && va.isScope() && !v.isReference()) @@ -834,7 +794,7 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) else { result |= sc.setUnsafeDIP1000(gag, ae.loc, - "address of local variable `%s` assigned to return scope `%s`", v, va); + "assigning address of local variable `%s` to return scope `%s`", v, va); } } @@ -849,32 +809,30 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) // If va's lifetime encloses v's, then error if (va && !(vaIsFirstRef && v.isReturn()) && va.enclosesLifetimeOf(v)) { - if (sc.setUnsafeDIP1000(gag, ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v, va)) + if (sc.setUnsafeDIP1000(gag, ae.loc, "assigning address of variable `%s` to `%s` with longer lifetime", v, va)) { result = true; - continue; + return; } } if (!(va && va.isScope())) - notMaybeScope(v, e); + doNotInferScope(v, e); if (p != sc.func) - continue; + return; if (inferScope(va)) { if (v.isReturn() && !va.isReturn()) va.storage_class |= STC.return_ | STC.returninferred; - continue; + return; } - if (e1.op == EXP.structLiteral) - continue; - result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1); + result |= sc.setUnsafeDIP1000(gag, ae.loc, "assigning reference to local variable `%s` to non-scope `%s`", v, e1); } - foreach (FuncDeclaration func; er.byfunc) + void onFunc(FuncDeclaration func, bool called) { if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf); VarDeclarations vars; @@ -896,10 +854,10 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) Dsymbol p = v.toParent2(); if (!(va && va.isScope())) - notMaybeScope(v, e); + doNotInferScope(v, e); if (!(v.isReference() || v.isScope()) || p != fd) - continue; + return; if (va && !va.isDataseg() && (va.isScope() || va.maybeScope)) { @@ -908,55 +866,41 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) */ //if (!va.isScope()) //va.storage_class |= STC.scope_ | STC.scopeinferred; - continue; + return; } result |= sc.setUnsafeDIP1000(gag, ae.loc, - "reference to local `%s` assigned to non-scope `%s` in @safe code", v, e1); + "assigning reference to local `%s` to non-scope `%s`", v, e1); } } - foreach (Expression ee; er.byexp) + void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp: %s\n", ee.toChars()); /* Do not allow slicing of a static array returned by a function */ - if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() && - !(va && va.storage_class & STC.temp)) + if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray()) { if (!gag) sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`", ee.toChars(), e1.toChars()); //result = true; - continue; + return; } - if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() && - (!va || !(va.storage_class & STC.temp) && !va.isScope())) - { - if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1)) - { - result = true; - continue; - } - } + const(char)* msg = (ee.op == EXP.structLiteral) ? + "assigning address of struct literal `%s` to `%s` with longer lifetime" : + "assigning address of expression temporary returned by `%s` to `%s` with longer lifetime"; - if (ee.op == EXP.structLiteral && - (!va || !(va.storage_class & STC.temp))) - { - if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1)) - { - result = true; - continue; - } - } + result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, e1); + } - if (inferScope(va)) - continue; + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); - result |= sc.setUnsafeDIP1000(gag, ee.loc, - "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1); - } + if (byRef) + escapeByRef(e2, er); + else + escapeByValue(e2, er); return result; } @@ -973,35 +917,32 @@ bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef) * `true` if pointers to the stack can escape */ public -bool checkThrowEscape(Scope* sc, Expression e, bool gag) +bool checkThrowEscape(ref Scope sc, Expression e, bool gag) { //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars()); - EscapeByResults er; - - escapeByValue(e, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byexp.length) - return false; bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onRef(VarDeclaration v, bool retRefTransition) {} + void onValue(VarDeclaration v) { //printf("byvalue %s\n", v.toChars()); - if (v.isDataseg()) - continue; - if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown // despite being `scope` { // https://issues.dlang.org/show_bug.cgi?id=17029 - result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be thrown", v); - continue; + result |= sc.setUnsafeDIP1000(gag, e.loc, "throwing scope variable `%s`", v); + return; } else { - notMaybeScope(v, new ThrowExp(e.loc, e)); + doNotInferScope(v, new ThrowExp(e.loc, e)); } } + void onFunc(FuncDeclaration fd, bool called) {} + void onExp(Expression exp, bool retRefTransition) {} + + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + escapeByValue(e, er); return result; } @@ -1017,7 +958,7 @@ bool checkThrowEscape(Scope* sc, Expression e, bool gag) * `true` if pointers to the stack can escape */ public -bool checkNewEscape(Scope* sc, Expression e, bool gag) +bool checkNewEscape(ref Scope sc, Expression e, bool gag) { import dmd.globals: FeatureState; import dmd.errors: previewErrorFunc; @@ -1025,19 +966,11 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars()); enum log = false; if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars()); - EscapeByResults er; - - escapeByValue(e, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byexp.length) - return false; bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); - if (v.isDataseg()) - continue; Dsymbol p = v.toParent2(); @@ -1056,23 +989,18 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) !(p.parent == sc.func)) { // https://issues.dlang.org/show_bug.cgi?id=20868 - result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be copied into allocated memory", v); - continue; + result |= sc.setUnsafeDIP1000(gag, e.loc, "copying scope variable `%s` into allocated memory", v); + return; } } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - result |= sc.setUnsafeDIP1000(gag, e.loc, - "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e, v); - } else { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); - notMaybeScope(v, e); + doNotInferScope(v, e); } } - foreach (VarDeclaration v; er.byref) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) printf("byref `%s`\n", v.toChars()); @@ -1081,14 +1009,11 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) bool escapingRef(VarDeclaration v, FeatureState fs) { const(char)* msg = v.isParameter() ? - "copying `%s` into allocated memory escapes a reference to parameter `%s`" : - "copying `%s` into allocated memory escapes a reference to local variable `%s`"; - return sc.setUnsafePreview(fs, gag, e.loc, msg, e, v); + "escaping a reference to parameter `%s` by copying `%s` into allocated memory" : + "escaping a reference to local variable `%s` by copying `%s` into allocated memory"; + return setUnsafePreview(&sc, fs, gag, e.loc, msg, v, e); } - if (v.isDataseg()) - continue; - Dsymbol p = v.toParent2(); if (!v.isReference()) @@ -1096,7 +1021,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) if (p == sc.func) { result |= escapingRef(v, sc.useDIP1000); - continue; + return; } } @@ -1104,7 +1029,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) * Infer the addition of 'return', or set result to be the offending expression. */ if (!v.isReference()) - continue; + return; // https://dlang.org/spec/function.html#return-ref-parameters if (p == sc.func) @@ -1112,16 +1037,16 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) //printf("escaping reference to local ref variable %s\n", v.toChars()); //printf("storage class = x%llx\n", v.storage_class); result |= escapingRef(v, sc.useDIP25); - continue; + return; } // Don't need to be concerned if v's parent does not return a ref FuncDeclaration func = p.isFuncDeclaration(); if (!func || !func.type) - continue; + return; if (auto tf = func.type.isTypeFunction()) { - if (!tf.isref) - continue; + if (!tf.isRef) + return; const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape"; if (!gag) @@ -1135,15 +1060,25 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) } } - foreach (Expression ee; er.byexp) + void onFunc(FuncDeclaration fd, bool called) + { + if (called) + result |= sc.setUnsafeDIP1000(gag, e.loc, + "escaping a `scope` value returned from nested function `%s` into allocated memory", fd); + } + + void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp %s\n", ee.toChars()); if (!gag) - sc.eSink.error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape", + sc.eSink.error(ee.loc, "escaping reference to stack allocated value returned by `%s` into allocated memory", ee.toChars()); result = true; } + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + escapeByValue(e, er); + return result; } @@ -1160,7 +1095,7 @@ bool checkNewEscape(Scope* sc, Expression e, bool gag) * `true` if pointers to the stack can escape */ public -bool checkReturnEscape(Scope* sc, Expression e, bool gag) +bool checkReturnEscape(ref Scope sc, Expression e, bool gag) { //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars()); return checkReturnEscapeImpl(sc, e, false, gag); @@ -1178,7 +1113,7 @@ bool checkReturnEscape(Scope* sc, Expression e, bool gag) * `true` if references to the stack can escape */ public -bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag) +bool checkReturnEscapeRef(ref Scope sc, Expression e, bool gag) { version (none) { @@ -1200,26 +1135,15 @@ bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag) * Returns: * `true` if references to the stack can escape */ -private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) +private bool checkReturnEscapeImpl(ref Scope sc, Expression e, bool refs, bool gag) { enum log = false; if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars()); - EscapeByResults er; - - if (refs) - escapeByRef(e, &er); - else - escapeByValue(e, &er); - - if (!er.byref.length && !er.byvalue.length && !er.byexp.length) - return false; bool result = false; - foreach (VarDeclaration v; er.byvalue) + void onValue(VarDeclaration v) { if (log) printf("byvalue `%s`\n", v.toChars()); - if (v.isDataseg()) - continue; const vsr = buildScopeRef(v.storage_class); @@ -1227,17 +1151,23 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (p == sc.func && inferReturn(sc.func, v, /*returnScope:*/ true)) { - continue; + return; } - if (v.isScope()) + if (v.isTypesafeVariadicArray && p == sc.func) + { + if (!gag) + sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); + result = false; + } + else if (v.isScope()) { /* If `return scope` applies to v. */ if (vsr == ScopeRef.ReturnScope || vsr == ScopeRef.Ref_ReturnScope) { - continue; + return; } auto pfunc = p.isFuncDeclaration(); @@ -1251,15 +1181,20 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) * return s; // s is inferred as 'scope' but incorrectly tested in foo() * return null; } */ - !(!refs && p.parent == sc.func && pfunc.fes) && + !(!refs && p.parent == sc.func && pfunc.fes) + ) + { /* * auto p(scope string s) { * string scfunc() { return s; } * } */ - !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0) - ) - { + if (sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0 && + inferReturn(sc.func, sc.func.vthis, /*returnScope*/ !refs)) + { + return; + } + if (v.isParameter() && !v.isReturn()) { // https://issues.dlang.org/show_bug.cgi?id=23191 @@ -1269,31 +1204,25 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) "scope parameter `%s` may not be returned", v.toChars() ); result = true; - continue; + return; } } else { // https://issues.dlang.org/show_bug.cgi?id=17029 - result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be returned", v); - continue; + result |= sc.setUnsafeDIP1000(gag, e.loc, "returning scope variable `%s`", v); + return; } } } - else if (v.isTypesafeVariadicArray && p == sc.func) - { - if (!gag) - sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); - result = false; - } - else + else if (p == sc.func || !v.isParameter()) { //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); doNotInferScope(v, e); } } - foreach (i, VarDeclaration v; er.byref[]) + void onRef(VarDeclaration v, bool retRefTransition) { if (log) { @@ -1304,13 +1233,12 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) // depending on the flag passed to the CLI for DIP25 void escapingRef(VarDeclaration v, FeatureState featureState) { - const(char)* msg = v.isParameter() ? - "returning `%s` escapes a reference to parameter `%s`" : - "returning `%s` escapes a reference to local variable `%s`"; - + const(char)* safeMsg = v.isParameter() ? + "escaping a reference to parameter `%s` by returning `%s`" : + "escaping a reference to local variable `%s` by returning `%s` "; if (v.isParameter() && v.isReference()) { - if (sc.setUnsafePreview(featureState, gag, e.loc, msg, e, v) || + if (setUnsafePreview(&sc, featureState, gag, e.loc, safeMsg, v, e) || sc.func.isSafeBypassingInference()) { result = true; @@ -1329,12 +1257,15 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } else { - if (er.refRetRefTransition[i]) + if (retRefTransition) { - result |= sc.setUnsafeDIP1000(gag, e.loc, msg, e, v); + result |= sc.setUnsafeDIP1000(gag, e.loc, safeMsg, v, e); } else { + const(char)* msg = v.isParameter() ? + "returning `%s` escapes a reference to parameter `%s`" : + "returning `%s` escapes a reference to local variable `%s`"; if (!gag) previewErrorFunc(sc.isDeprecated(), featureState)(e.loc, msg, e.toChars(), v.toChars()); result = true; @@ -1342,24 +1273,15 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } } - if (v.isDataseg()) - continue; - const vsr = buildScopeRef(v.storage_class); Dsymbol p = v.toParent2(); // https://issues.dlang.org/show_bug.cgi?id=19965 - if (!refs) + if (!refs && checkScopeVarAddr(v, e, sc, gag)) { - if (sc.func.vthis == v) - notMaybeScope(v, e); - - if (checkScopeVarAddr(v, e, sc, gag)) - { - result = true; - continue; - } + result = true; + return; } if (!v.isReference()) @@ -1367,10 +1289,10 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) if (p == sc.func) { escapingRef(v, FeatureState.enabled); - continue; + return; } FuncDeclaration fd = p.isFuncDeclaration(); - if (fd && sc.func.returnInprocess) + if (fd && sc.func.scopeInprocess) { /* Code like: * int x; @@ -1386,15 +1308,14 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) /* Check for returning a ref variable by 'ref', but should be 'return ref' * Infer the addition of 'return', or set result to be the offending expression. */ - if ((vsr == ScopeRef.Ref || + if (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope || - vsr == ScopeRef.Ref_ReturnScope) && - !(v.storage_class & STC.foreach_)) + vsr == ScopeRef.Ref_ReturnScope) { if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) && inferReturn(sc.func, v, /*returnScope:*/ false)) { - continue; + return; } else { @@ -1405,20 +1326,20 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) //printf("escaping reference to local ref variable %s\n", v.toChars()); //printf("storage class = x%llx\n", v.storage_class); escapingRef(v, sc.useDIP25); - continue; + return; } // Don't need to be concerned if v's parent does not return a ref FuncDeclaration fd = p.isFuncDeclaration(); if (fd && fd.type && fd.type.ty == Tfunction) { TypeFunction tf = fd.type.isTypeFunction(); - if (tf.isref) + if (tf.isRef) { const(char)* msg = "escaping reference to outer local variable `%s`"; if (!gag) previewErrorFunc(sc.isDeprecated(), sc.useDIP25)(e.loc, msg, v.toChars()); result = true; - continue; + return; } } @@ -1426,10 +1347,16 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) } } - foreach (i, Expression ee; er.byexp[]) + void onFunc(FuncDeclaration fd, bool called) + { + if (called && fd.isNested()) + result |= sc.setUnsafeDIP1000(gag, e.loc, "escaping local variable through nested function `%s`", fd); + } + + void onExp(Expression ee, bool retRefTransition) { if (log) printf("byexp %s\n", ee.toChars()); - if (er.expRetRefTransition[i]) + if (retRefTransition) { result |= sc.setUnsafeDIP1000(gag, ee.loc, "escaping reference to stack allocated value returned by `%s`", ee); @@ -1441,6 +1368,15 @@ private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) result = true; } } + + + scope EscapeByResults er = EscapeByResults(&onRef, &onValue, &onFunc, &onExp); + + if (refs) + escapeByRef(e, er); + else + escapeByValue(e, er); + return result; } @@ -1483,7 +1419,7 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) if (!v.isParameter() || v.isTypesafeVariadicArray || (returnScope && v.doNotInferReturn)) return false; - if (!fd.returnInprocess) + if (!fd.scopeInprocess) return false; if (returnScope && !(v.isScope() || v.maybeScope)) @@ -1501,9 +1437,9 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) if (auto tf = fd.type.isTypeFunction()) { //printf("'this' too %p %s\n", tf, sc.func.toChars()); - tf.isreturnscope = returnScope; - tf.isreturn = true; - tf.isreturninferred = true; + tf.isReturnScope = returnScope; + tf.isReturn = true; + tf.isReturnInferred = true; } } else @@ -1541,11 +1477,20 @@ private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope) * Params: * e = expression to be returned by value * er = where to place collected data - * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. - * retRefTransition = if `e` is returned through a `return ref scope` function call */ public -void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false) +void escapeByValue(Expression e, ref scope EscapeByResults er) +{ + escapeExp(e, er, 0); +} + +// Unified implementation of `escapeByValue` and `escapeByRef` +// deref = derference level, if `p` has deref 0, then `*p` has deref 1, `&p` has -1, and `**p` has 2 etc. +// For escapeByValue, deref = 0 +// For escapeByRef, deref = -1 +// Currently, `scope` is not transitive, so deref > 0 means no escaping, but `@live` does do transitive checking, +// and future enhancements might add some form of transitive scope. +void escapeExp(Expression e, ref scope EscapeByResults er, int deref) { //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars()); @@ -1559,62 +1504,99 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re * allowed, but CTFE can generate one out of a new expression, * but it'll be placed in static data so no need to check it. */ - if (e.e1.op != EXP.structLiteral) - escapeByRef(e.e1, er, live, retRefTransition); + if (deref == 0 && e.e1.op != EXP.structLiteral) + escapeExp(e.e1, er, deref - 1); } void visitSymOff(SymOffExp e) { - VarDeclaration v = e.var.isVarDeclaration(); - if (v) - er.pushRef(v, retRefTransition); + if (VarDeclaration v = e.var.isVarDeclaration()) + er.varDeref(v, deref - 1); } void visitVar(VarExp e) { if (auto v = e.var.isVarDeclaration()) { - if (v.type.hasPointers() || // not tracking non-pointers - v.storage_class & STC.lazy_) // lazy variables are actually pointers - er.byvalue.push(v); + const refAddr = deref < 0 && v.storage_class & STC.ref_ ; + const tempVar = deref == 0 && v.storage_class & STC.temp; + if ((refAddr || tempVar) && v._init && v != er.lastTemp) + { + // If compiler generated ref temporary + // (ref v = ex; ex) + // e.g. to extract side effects of `Tuple!(int, int).modify().expand[0]` + // look at the initializer instead + if (ExpInitializer ez = v._init.isExpInitializer()) + { + // Prevent endless loops. Consider: + // `__field0 = (S __tup1 = S(x, y);) , __field0 = __tup1.__fields_field_0` + // escapeExp would recurse on the lhs of the last assignment, which is __field0 + // again. In this case, we want the rhs. + // Also consider appending a struct with a `return scope` constructor: + // __appendtmp34 = __appendtmp34.this(null) + // In that case we just break the cycle using `lastTemp`. + auto lc = ez.exp.lastComma(); + auto restoreLastTemp = er.lastTemp; + er.lastTemp = v; + // printf("%s %s TO %s\n", e.loc.toChars, e.toChars, lc.toChars); + if (lc.isAssignExp || lc.isConstructExp || lc.isBlitExp) + escapeExp(lc.isBinExp().e2, er, deref); + else + escapeExp(ez.exp, er, deref); + + er.lastTemp = restoreLastTemp; + return; + } + } + + if (deref < 0 || e.type.hasPointers()) + er.varDeref(v, deref); } } void visitThis(ThisExp e) { + // Special case because `__this2` isn't `ref` internally + if (deref == -1 && e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) + { + escapeByValue(e, er); + return; + } + if (e.var) - er.byvalue.push(e.var); + er.varDeref(e.var, deref); } void visitPtr(PtrExp e) { - if (live && e.type.hasPointers()) - escapeByValue(e.e1, er, live, retRefTransition); + if (deref < 0 || (er.live && e.type.hasPointers())) + escapeExp(e.e1, er, deref + 1); } void visitDotVar(DotVarExp e) { - auto t = e.e1.type.toBasetype(); - if (e.type.hasPointers() && (live || t.ty == Tstruct)) - { - escapeByValue(e.e1, er, live, retRefTransition); - } + auto t1b = e.e1.type.toBasetype(); + // Accessing a class field dereferences the `this` pointer + if (t1b.isTypeClass()) + escapeExp(e.e1, er, deref + 1); + else if (deref < 0 || e.type.hasPointers()) + escapeExp(e.e1, er, deref); } void visitDelegate(DelegateExp e) { Type t = e.e1.type.toBasetype(); - if (t.ty == Tclass || t.ty == Tpointer) - escapeByValue(e.e1, er, live, retRefTransition); + if (t.isTypeClass() || t.isTypePointer()) + escapeByValue(e.e1, er); else - escapeByRef(e.e1, er, live, retRefTransition); - er.byfunc.push(e.func); + escapeByRef(e.e1, er); + er.byFunc(e.func, false); } void visitFunc(FuncExp e) { if (e.fd.tok == TOK.delegate_) - er.byfunc.push(e.fd); + er.byFunc(e.fd, false); } void visitTuple(TupleExp e) @@ -1625,14 +1607,14 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re void visitArrayLiteral(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { if (e.basis) - escapeByValue(e.basis, er, live, retRefTransition); + escapeExp(e.basis, er, deref); foreach (el; *e.elements) { if (el) - escapeByValue(el, er, live, retRefTransition); + escapeExp(el, er, deref); } } } @@ -1644,120 +1626,130 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re foreach (ex; *e.elements) { if (ex) - escapeByValue(ex, er, live, retRefTransition); + escapeExp(ex, er, deref); } } + if (deref == -1) + { + er.byExp(e, er.inRetRefTransition > 0); // + } } void visitNew(NewExp e) { + if (e.placement) + escapeExp(e.placement, er, deref); + Type tb = e.newtype.toBasetype(); - if (tb.ty == Tstruct && !e.member && e.arguments) + if (tb.isTypeStruct() && !e.member && e.arguments) { foreach (ex; *e.arguments) { if (ex) - escapeByValue(ex, er, live, retRefTransition); + escapeExp(ex, er, deref); } } } void visitCast(CastExp e) { - if (!e.type.hasPointers()) + if (deref < 0 || !e.type.hasPointers()) return; Type tb = e.type.toBasetype(); - if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) - { - escapeByRef(e.e1, er, live, retRefTransition); - } + if (tb.isTypeDArray() && e.e1.type.toBasetype().isTypeSArray()) + escapeExp(e.e1, er, deref - 1); else - escapeByValue(e.e1, er, live, retRefTransition); + escapeExp(e.e1, er, deref); } void visitSlice(SliceExp e) { - if (auto ve = e.e1.isVarExp()) - { - VarDeclaration v = ve.var.isVarDeclaration(); - Type tb = e.type.toBasetype(); - if (v) - { - if (tb.ty == Tsarray) - return; - if (v.isTypesafeVariadicArray) - { - er.byvalue.push(v); - return; - } - } - } - Type t1b = e.e1.type.toBasetype(); - if (t1b.ty == Tsarray) - { - Type tb = e.type.toBasetype(); - if (tb.ty != Tsarray) - escapeByRef(e.e1, er, live, retRefTransition); - } - else - escapeByValue(e.e1, er, live, retRefTransition); + // Usually: slicing a static array escapes by ref, slicing a dynamic array escapes by value. + // However, slices with compile-time known length can implicitly converted to static arrays: + // int*[3] b = sa[0 .. 3]; + // So we need to compare the type before slicing and after slicing + const bool staticBefore = e.e1.type.toBasetype().isTypeSArray() !is null; + const bool staticAfter = e.type.toBasetype().isTypeSArray() !is null; + escapeExp(e.e1, er, deref + staticAfter - staticBefore); } void visitIndex(IndexExp e) { - if (e.e1.type.toBasetype().ty == Tsarray || - live && e.type.hasPointers()) + Type tb = e.e1.type.toBasetype(); + + if (tb.isTypeSArray()) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeExp(e.e1, er, deref); + } + else if (tb.isTypeDArray()) + { + escapeExp(e.e1, er, deref + 1); } } void visitBin(BinExp e) { - Type tb = e.type.toBasetype(); - if (tb.ty == Tpointer) + if (e.type.toBasetype().isTypePointer()) { - escapeByValue(e.e1, er, live, retRefTransition); - escapeByValue(e.e2, er, live, retRefTransition); + // The expression must be pointer arithmetic, e.g. `p + 1` or `1 + p` + escapeExp(e.e1, er, deref); + escapeExp(e.e2, er, deref); } } void visitBinAssign(BinAssignExp e) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeExp(e.e1, er, deref); } void visitAssign(AssignExp e) { - escapeByValue(e.e1, er, live, retRefTransition); + escapeExp(e.e1, er, deref); } void visitComma(CommaExp e) { - escapeByValue(e.e2, er, live, retRefTransition); + escapeExp(e.e2, er, deref); } void visitCond(CondExp e) { - escapeByValue(e.e1, er, live, retRefTransition); - escapeByValue(e.e2, er, live, retRefTransition); + escapeExp(e.e1, er, deref); + escapeExp(e.e2, er, deref); } void visitCall(CallExp e) { //printf("CallExp(): %s\n", e.toChars()); - /* Check each argument that is - * passed as 'return scope'. - */ + // Check each argument that is passed as 'return scope'. TypeFunction tf = e.calledFunctionType(); - if (!tf || !e.type.hasPointers()) + if (!tf) + return; + + if (deref < 0 && !tf.isRef) + { + er.byExp(e, er.inRetRefTransition > 0); return; + } + + // A function may have a return scope struct parameter, but only return an `int` field of that struct + if (deref >= 0 && !e.type.hasPointers()) + return; + + /// Given a `scope` / `return scope` / `return ref` annotation, + /// get the corresponding pointer dereference level + static int paramDeref(ScopeRef psr) + { + return + (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) ? -1 : + (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) ? 0 : + +1; + } if (e.arguments && e.arguments.length) { - /* j=1 if _arguments[] is first argument, - * skip it because it is not passed by ref - */ + // j=1 if _arguments[] is first argument, + // skip it because it is not passed by ref int j = tf.isDstyleVariadic(); for (size_t i = j; i < e.arguments.length; ++i) { @@ -1768,129 +1760,73 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re Parameter p = tf.parameterList[i - j]; const stc = tf.parameterStorageClass(null, p); ScopeRef psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (tf.isref) - { - /* ignore `ref` on struct constructor return because - * struct S { this(return scope int* q) { this.p = q; } int* p; } - * is different from: - * ref char* front(return scope char** q) { return *q; } - * https://github.com/dlang/dmd/pull/14869 - */ - if (auto dve = e.e1.isDotVarExp()) - if (auto fd = dve.var.isFuncDeclaration()) - if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct()) - { - escapeByValue(arg, er, live, retRefTransition); - } - } - else - escapeByValue(arg, er, live, retRefTransition); - } - else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - { - if (tf.isref) - { - /* Treat: - * ref P foo(return ref P p) - * as: - * p; - */ - escapeByValue(arg, er, live, retRefTransition); - } - else - escapeByRef(arg, er, live, retRefTransition); - } + + // For struct constructors, `tf.isRef` is true, but for escape analysis, + // it's as if they return `void` and escape through the first (`this`) parameter: + // void assign(ref S this, return scope constructorArgs...) + // If you then return the constructed result by value, it doesn't count + // as dereferencing the scope arguments, they're still escaped. + const isRef = tf.isRef && !(tf.isCtor && paramDeref(psr) == 0); + const maybeInaccurate = deref == 0 && paramDeref(psr) == 0; + er.inRetRefTransition += maybeInaccurate; + if (paramDeref(psr) <= 0) + escapeExp(arg, er, deref + paramDeref(psr) + isRef); + er.inRetRefTransition -= maybeInaccurate; } } } + // If 'this' is returned, check it too Type t1 = e.e1.type.toBasetype(); - if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction) + DotVarExp dve = e.e1.isDotVarExp(); + if (dve && t1.ty == Tfunction) { - DotVarExp dve = e.e1.isDotVarExp(); FuncDeclaration fd = dve.var.isFuncDeclaration(); - if (fd && fd.isThis()) - { - /* Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this` - */ - - /***************************** - * Concoct storage class for member function's implicit `this` parameter. - * Params: - * fd = member function - * Returns: - * storage class for fd's `this` - */ - StorageClass getThisStorageClass(FuncDeclaration fd) - { - StorageClass stc; - auto tf = fd.type.toBasetype().isTypeFunction(); - if (tf.isreturn) - stc |= STC.return_; - if (tf.isreturnscope) - stc |= STC.returnScope | STC.scope_; - auto ad = fd.isThis(); - if (ad.isClassDeclaration() || tf.isScopeQual) - stc |= STC.scope_; - if (ad.isStructDeclaration()) - stc |= STC.ref_; // `this` for a struct member function is passed by `ref` - return stc; - } - - const psr = buildScopeRef(getThisStorageClass(fd)); - if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (!tf.isref || tf.isctor) - escapeByValue(dve.e1, er, live, retRefTransition); - } - else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - { - if (tf.isref) - { - /* Treat calling: - * struct S { ref S foo() return; } - * as: - * this; - */ - escapeByValue(dve.e1, er, live, retRefTransition); - } - else - escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope); - } - } + if (!fd) + return; - // If it's also a nested function that is 'return scope' - if (fd && fd.isNested()) + // https://issues.dlang.org/show_bug.cgi?id=20149#c10 + if (deref < 0 && dve.var.isCtorDeclaration()) { - if (tf.isreturn && tf.isScopeQual) - er.pushExp(e, false); + er.byExp(e, false); + return; } + + // Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this` + const psr = buildScopeRef(getThisStorageClass(fd)); + er.inRetRefTransition += (psr == ScopeRef.ReturnRef_Scope); + if (paramDeref(psr) <= 0) + escapeExp(dve.e1, er, deref + paramDeref(psr) + (tf.isRef && !tf.isCtor)); + er.inRetRefTransition -= (psr == ScopeRef.ReturnRef_Scope); } - /* If returning the result of a delegate call, the .ptr - * field of the delegate must be checked. - */ - if (t1.isTypeDelegate()) + // The return value of a delegate call with return (scope) may point to a closure variable, + // so escape the delegate in case it's `scope` / stack allocated. + if (t1.isTypeDelegate() && tf.isReturn) { - if (tf.isreturn) - escapeByValue(e.e1, er, live, retRefTransition); + escapeExp(e.e1, er, deref + tf.isRef); } - /* If it's a nested function that is 'return scope' - */ + // If `fd` is a nested function that's return ref / return scope, check that + // it doesn't escape closure vars if (auto ve = e.e1.isVarExp()) { - FuncDeclaration fd = ve.var.isFuncDeclaration(); - if (fd && fd.isNested()) + if (FuncDeclaration fd = ve.var.isFuncDeclaration()) { - if (tf.isreturn && tf.isScopeQual) - er.pushExp(e, false); + if (fd.isNested() && tf.isReturn) + { + er.byFunc(fd, true); + } } } } + if (deref > 0 && !er.live) + return; // scope is not transitive currently, so dereferencing expressions don't escape + + if (deref >= 0 && er.live && !e.type.hasPointers()) + return; // can't escape non-pointer values by value + switch (e.op) { case EXP.address: return visitAddr(e.isAddrExp()); @@ -1915,14 +1851,36 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re case EXP.question: return visitCond(e.isCondExp()); case EXP.call: return visitCall(e.isCallExp()); default: - if (auto b = e.isBinExp()) - return visitBin(b); if (auto ba = e.isBinAssignExp()) return visitBinAssign(ba); + if (auto b = e.isBinExp()) + return visitBin(b); return visit(e); } } +/***************************** + * Concoct storage class for member function's implicit `this` parameter. + * Params: + * fd = member function + * Returns: + * storage class for fd's `this` + */ +STC getThisStorageClass(FuncDeclaration fd) +{ + STC stc; + auto tf = fd.type.toBasetype().isTypeFunction(); + if (tf.isReturn) + stc |= STC.return_; + if (tf.isReturnScope) + stc |= STC.returnScope | STC.scope_; + auto ad = fd.isThis(); + if ((ad && ad.isClassDeclaration()) || tf.isScopeQual) + stc |= STC.scope_; + if (ad && ad.isStructDeclaration()) + stc |= STC.ref_; // `this` for a struct member function is passed by `ref` + return stc; +} /**************************************** * e is an expression to be returned by 'ref'. @@ -1940,239 +1898,10 @@ void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool re * Params: * e = expression to be returned by 'ref' * er = where to place collected data - * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. - * retRefTransition = if `e` is returned through a `return ref scope` function call */ -private -void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false) +void escapeByRef(Expression e, ref scope EscapeByResults er) { - //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition); - void visit(Expression e) - { - } - - void visitVar(VarExp e) - { - auto v = e.var.isVarDeclaration(); - if (v) - { - if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init) - { - /* If compiler generated ref temporary - * (ref v = ex; ex) - * look at the initializer instead - */ - if (ExpInitializer ez = v._init.isExpInitializer()) - { - if (auto ce = ez.exp.isConstructExp()) - escapeByRef(ce.e2, er, live, retRefTransition); - else - escapeByRef(ez.exp, er, live, retRefTransition); - } - } - else - er.pushRef(v, retRefTransition); - } - } - - void visitThis(ThisExp e) - { - if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext()) - escapeByValue(e, er, live, retRefTransition); - else if (e.var) - er.pushRef(e.var, retRefTransition); - } - - void visitPtr(PtrExp e) - { - escapeByValue(e.e1, er, live, retRefTransition); - } - - void visitIndex(IndexExp e) - { - Type tb = e.e1.type.toBasetype(); - if (auto ve = e.e1.isVarExp()) - { - VarDeclaration v = ve.var.isVarDeclaration(); - if (v && v.isTypesafeVariadicArray) - { - er.pushRef(v, retRefTransition); - return; - } - } - if (tb.ty == Tsarray) - { - escapeByRef(e.e1, er, live, retRefTransition); - } - else if (tb.ty == Tarray) - { - escapeByValue(e.e1, er, live, retRefTransition); - } - } - - void visitStructLiteral(StructLiteralExp e) - { - if (e.elements) - { - foreach (ex; *e.elements) - { - if (ex) - escapeByRef(ex, er, live, retRefTransition); - } - } - er.pushExp(e, retRefTransition); - } - - void visitDotVar(DotVarExp e) - { - Type t1b = e.e1.type.toBasetype(); - if (t1b.ty == Tclass) - escapeByValue(e.e1, er, live, retRefTransition); - else - escapeByRef(e.e1, er, live, retRefTransition); - } - - void visitBinAssign(BinAssignExp e) - { - escapeByRef(e.e1, er, live, retRefTransition); - } - - void visitAssign(AssignExp e) - { - escapeByRef(e.e1, er, live, retRefTransition); - } - - void visitComma(CommaExp e) - { - escapeByRef(e.e2, er, live, retRefTransition); - } - - void visitCond(CondExp e) - { - escapeByRef(e.e1, er, live, retRefTransition); - escapeByRef(e.e2, er, live, retRefTransition); - } - - void visitCall(CallExp e) - { - //printf("escapeByRef.CallExp(): %s\n", e.toChars()); - /* If the function returns by ref, check each argument that is - * passed as 'return ref'. - */ - TypeFunction tf = e.calledFunctionType(); - if (!tf) - return; - if (tf.isref) - { - if (e.arguments && e.arguments.length) - { - /* j=1 if _arguments[] is first argument, - * skip it because it is not passed by ref - */ - int j = tf.isDstyleVariadic(); - for (size_t i = j; i < e.arguments.length; ++i) - { - Expression arg = (*e.arguments)[i]; - size_t nparams = tf.parameterList.length; - if (i - j < nparams && i >= j) - { - Parameter p = tf.parameterList[i - j]; - const stc = tf.parameterStorageClass(null, p); - ScopeRef psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(arg, er, live, retRefTransition); - else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - { - if (auto de = arg.isDelegateExp()) - { - if (de.func.isNested()) - er.pushExp(de, false); - } - else - escapeByValue(arg, er, live, retRefTransition); - } - } - } - } - // If 'this' is returned by ref, check it too - Type t1 = e.e1.type.toBasetype(); - if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction) - { - DotVarExp dve = e.e1.isDotVarExp(); - - // https://issues.dlang.org/show_bug.cgi?id=20149#c10 - if (dve.var.isCtorDeclaration()) - { - er.pushExp(e, false); - return; - } - - StorageClass stc = dve.var.storage_class & (STC.return_ | STC.scope_ | STC.ref_); - if (tf.isreturn) - stc |= STC.return_; - if (tf.isref) - stc |= STC.ref_; - if (tf.isScopeQual) - stc |= STC.scope_; - if (tf.isreturnscope) - stc |= STC.returnScope; - - const psr = buildScopeRef(stc); - if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope) - escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope); - else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope) - escapeByValue(dve.e1, er, live, retRefTransition); - - // If it's also a nested function that is 'return ref' - if (FuncDeclaration fd = dve.var.isFuncDeclaration()) - { - if (fd.isNested() && tf.isreturn) - { - er.pushExp(e, false); - } - } - } - // If it's a delegate, check it too - if (e.e1.op == EXP.variable && t1.ty == Tdelegate) - { - escapeByValue(e.e1, er, live, retRefTransition); - } - - /* If it's a nested function that is 'return ref' - */ - if (auto ve = e.e1.isVarExp()) - { - FuncDeclaration fd = ve.var.isFuncDeclaration(); - if (fd && fd.isNested()) - { - if (tf.isreturn) - er.pushExp(e, false); - } - } - } - else - er.pushExp(e, retRefTransition); - } - - switch (e.op) - { - case EXP.variable: return visitVar(e.isVarExp()); - case EXP.this_: return visitThis(e.isThisExp()); - case EXP.star: return visitPtr(e.isPtrExp()); - case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp()); - case EXP.dotVariable: return visitDotVar(e.isDotVarExp()); - case EXP.index: return visitIndex(e.isIndexExp()); - case EXP.blit: return visitAssign(e.isBlitExp()); - case EXP.construct: return visitAssign(e.isConstructExp()); - case EXP.assign: return visitAssign(e.isAssignExp()); - case EXP.comma: return visitComma(e.isCommaExp()); - case EXP.question: return visitCond(e.isCondExp()); - case EXP.call: return visitCall(e.isCallExp()); - default: - if (auto ba = e.isBinAssignExp()) - return visitBinAssign(ba); - return visit(e); - } + escapeExp(e, er, -1); } /************************************ @@ -2181,15 +1910,8 @@ void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retR public struct EscapeByResults { - VarDeclarations byref; // array into which variables being returned by ref are inserted - VarDeclarations byvalue; // array into which variables with values containing pointers are inserted - private FuncDeclarations byfunc; // nested functions that are turned into delegates - private Expressions byexp; // array into which temporaries being returned by ref are inserted - - import dmd.root.array: Array; - - /** - * Whether the variable / expression went through a `return ref scope` function call + /* + * retRefTransition = Whether the variable / expression went through a `return (ref) scope` function call * * This is needed for the dip1000 by default transition, since the rules for * disambiguating `return scope ref` have changed. Therefore, functions in legacy code @@ -2197,46 +1919,48 @@ struct EscapeByResults * are being escaped, which is an error even in `@system` code. By keeping track of this * information, variables escaped through `return ref` can be treated as a deprecation instead * of error, see test/fail_compilation/dip1000_deprecation.d + * + * Additionally, return scope can be inferred wrongly instead of scope, in which + * case the code could give false positives even without @safe or dip1000: + * https://issues.dlang.org/show_bug.cgi?id=23657 */ - private Array!bool refRetRefTransition; - private Array!bool expRetRefTransition; - - /** Reset arrays so the storage can be used again - */ - void reset() - { - byref.setDim(0); - byvalue.setDim(0); - byfunc.setDim(0); - byexp.setDim(0); - refRetRefTransition.setDim(0); - expRetRefTransition.setDim(0); - } + /// called on variables being returned by ref / address + void delegate(VarDeclaration, bool retRefTransition) byRef; + /// called on variables with values containing pointers + void delegate(VarDeclaration) byValue; - /** - * Escape variable `v` by reference - * Params: - * v = variable to escape - * retRefTransition = `v` is escaped through a `return ref scope` function call - */ - void pushRef(VarDeclaration v, bool retRefTransition) + /// Switch over `byValue` and `byRef` based on `deref` level (-1 = by ref, 0 = by value, 1 = only for live currently) + private void varDeref(VarDeclaration var, int deref) { - byref.push(v); - refRetRefTransition.push(retRefTransition); - } - - /** - * Escape a reference to expression `e` - * Params: - * e = expression to escape - * retRefTransition = `e` is escaped through a `return ref scope` function call - */ - void pushExp(Expression e, bool retRefTransition) - { - byexp.push(e); - expRetRefTransition.push(retRefTransition); - } + if (var.isDataseg()) + return; + if (deref == -1) + byRef(var, inRetRefTransition > 0); + else if (deref == 0) + byValue(var); + else if (deref > 0 && live) + byValue(var); + } + + /// called on nested functions that are turned into delegates + /// When `called` is true, it means the delegate escapes variables + /// from the closure through a call to it, while `false` means the + /// delegate itself escapes. + void delegate(FuncDeclaration, bool called) byFunc; + /// called when expression temporaries are being returned by ref / address + void delegate(Expression, bool retRefTransition) byExp; + + /// if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. + bool live = false; + + /// Incremented / decremented every time an ambiguous return ref/scope parameter is checked. + /// See retRefTransition above. + private int inRetRefTransition = 0; + + /// When forwarding a temp var to its initializer, + /// keep track of the temp var to break endless loops + private VarDeclaration lastTemp = null; } /************************* @@ -2280,7 +2004,7 @@ public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* v * - `VarDeclaration` of a non-scope parameter it was assigned to * - `null` for no reason */ -private void notMaybeScope(VarDeclaration v, RootObject o) +private void doNotInferScope(VarDeclaration v, RootObject o) { if (v.maybeScope) { @@ -2291,23 +2015,6 @@ private void notMaybeScope(VarDeclaration v, RootObject o) } /*********************************** - * Turn off `maybeScope` for variable `v` if it's not a parameter. - * - * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`, - * which is now just `VarDeclaration.maybeScope`. - * This function should probably be removed in future refactors. - * - * Params: - * v = variable - * o = reason for it being turned off - */ -private void doNotInferScope(VarDeclaration v, RootObject o) -{ - if (!v.isParameter) - notMaybeScope(v, o); -} - -/*********************************** * After semantic analysis of the function body, * try to infer `scope` / `return` on the parameters * @@ -2319,48 +2026,21 @@ private void doNotInferScope(VarDeclaration v, RootObject o) public void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) { - - if (funcdecl.returnInprocess) - { - funcdecl.returnInprocess = false; - if (funcdecl.storage_class & STC.return_) - { - if (funcdecl.type == f) - f = cast(TypeFunction)f.copy(); - f.isreturn = true; - f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope); - if (funcdecl.storage_class & STC.returninferred) - f.isreturninferred = true; - } - } - - if (!funcdecl.inferScope) + if (!funcdecl.scopeInprocess) return; - funcdecl.inferScope = false; + funcdecl.scopeInprocess = false; - // Eliminate maybescope's + if (funcdecl.storage_class & STC.return_) { - // Create and fill array[] with maybe candidates from the `this` and the parameters - VarDeclaration[10] tmp = void; - size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0); - - import dmd.common.smallbuffer : SmallBuffer; - auto sb = SmallBuffer!VarDeclaration(dim, tmp[]); - VarDeclaration[] array = sb[]; - - size_t n = 0; - if (funcdecl.vthis) - array[n++] = funcdecl.vthis; - if (funcdecl.parameters) - { - foreach (v; *funcdecl.parameters) - { - array[n++] = v; - } - } - eliminateMaybeScopes(array[0 .. n]); + if (funcdecl.type == f) + f = cast(TypeFunction)f.copy(); + f.isReturn = true; + f.isReturnScope = cast(bool) (funcdecl.storage_class & STC.returnScope); + if (funcdecl.storage_class & STC.returninferred) + f.isReturnInferred = true; } + // Infer STC.scope_ if (funcdecl.parameters && !funcdecl.errors) { @@ -2380,65 +2060,10 @@ void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f) { inferScope(funcdecl.vthis); f.isScopeQual = funcdecl.vthis.isScope(); - f.isscopeinferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred); + f.isScopeInferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred); } } -/********************************************** - * Have some variables that are maybescopes that were - * assigned values from other maybescope variables. - * Now that semantic analysis of the function is - * complete, we can finalize this by turning off - * maybescope for array elements that cannot be scope. - * - * $(TABLE2 Scope Table, - * $(THEAD `va`, `v`, =>, `va` , `v` ) - * $(TROW maybe, maybe, =>, scope, scope) - * $(TROW scope, scope, =>, scope, scope) - * $(TROW scope, maybe, =>, scope, scope) - * $(TROW maybe, scope, =>, scope, scope) - * $(TROW - , - , =>, - , - ) - * $(TROW - , maybe, =>, - , - ) - * $(TROW - , scope, =>, error, error) - * $(TROW maybe, - , =>, scope, - ) - * $(TROW scope, - , =>, scope, - ) - * ) - * Params: - * array = array of variables that were assigned to from maybescope variables - */ -private void eliminateMaybeScopes(VarDeclaration[] array) -{ - enum log = false; - if (log) printf("eliminateMaybeScopes()\n"); - bool changes; - do - { - changes = false; - foreach (va; array) - { - if (log) printf(" va = %s\n", va.toChars()); - if (!(va.maybeScope || va.isScope())) - { - if (va.maybes) - { - foreach (v; *va.maybes) - { - if (log) printf(" v = %s\n", v.toChars()); - if (v.maybeScope) - { - // v cannot be scope since it is assigned to a non-scope va - notMaybeScope(v, va); - if (!v.isReference()) - v.storage_class &= ~(STC.return_ | STC.returninferred); - changes = true; - } - } - } - } - } - } while (changes); -} - /************************************************ * Is type a reference to a mutable value? * @@ -2566,30 +2191,12 @@ private EnclosedBy enclosesLifetimeOf(VarDeclaration va, VarDeclaration v) return EnclosedBy.none; } -/*************************************** - * Add variable `v` to maybes[] - * - * When a maybescope variable `v` is assigned to a maybescope variable `va`, - * we cannot determine if `this` is actually scope until the semantic - * analysis for the function is completed. Thus, we save the data - * until then. - * Params: - * v = a variable with `maybeScope == true` that was assigned to `this` - */ -private void addMaybe(VarDeclaration va, VarDeclaration v) -{ - //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars()); - if (!va.maybes) - va.maybes = new VarDeclarations(); - va.maybes.push(v); -} - // `setUnsafePreview` partially evaluated for dip1000 public -bool setUnsafeDIP1000(Scope* sc, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) +bool setUnsafeDIP1000(ref Scope sc, bool gag, Loc loc, const(char)* msg, + RootObject[] args...) { - return setUnsafePreview(sc, sc.useDIP1000, gag, loc, msg, arg0, arg1, arg2); + return setUnsafePreview(&sc, sc.useDIP1000, gag, loc, msg, args); } /*************************************** @@ -2606,14 +2213,11 @@ bool setUnsafeDIP1000(Scope* sc, bool gag, Loc loc, const(char)* msg, * Returns: * true if taking the address of `v` is problematic because of the lack of transitive `scope` */ -private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool gag) +private bool checkScopeVarAddr(VarDeclaration v, Expression e, ref Scope sc, bool gag) { - if (v.storage_class & STC.temp) - return false; - if (!v.isScope()) { - notMaybeScope(v, e); + doNotInferScope(v, e); return false; } @@ -2629,7 +2233,7 @@ private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool g // take address of `scope` variable not allowed, requires transitive scope return sc.setUnsafeDIP1000(gag, e.loc, - "cannot take address of `scope` variable `%s` since `scope` applies to first indirection only", v); + "taking address of `scope` variable `%s` with pointers", v); } /**************************** @@ -2644,7 +2248,7 @@ private bool isTypesafeVariadicArray(VarDeclaration v) if (v.storage_class & STC.variadic) { Type tb = v.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) return true; } return false; diff --git a/gcc/d/dmd/expression.d b/gcc/d/dmd/expression.d index 479ad3a..d65b163 100644 --- a/gcc/d/dmd/expression.d +++ b/gcc/d/dmd/expression.d @@ -3,12 +3,12 @@ * * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/expression.d, _expression.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expression.d, _expression.d) * Documentation: https://dlang.org/phobos/dmd_expression.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expression.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/expression.d */ module dmd.expression; @@ -21,6 +21,7 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; +import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dimport; @@ -53,29 +54,6 @@ import dmd.visitor; enum LOGSEMANTIC = false; -/// Return value for `checkModifiable` -enum Modifiable -{ - /// Not modifiable - no, - /// Modifiable (the type is mutable) - yes, - /// Modifiable because it is initialization - initialization, -} -/** - * Specifies how the checkModify deals with certain situations - */ -enum ModifyFlags -{ - /// Issue error messages on invalid modifications of the variable - none, - /// No errors are emitted for invalid modifications - noError = 0x1, - /// The modification occurs for a subfield of the current variable - fieldAssign = 0x2, -} - /**************************************** * Find the last non-comma expression. * Params: @@ -225,7 +203,7 @@ TemplateDeclaration getFuncTemplateDecl(Dsymbol s) @safe * (foo).size * cast(foo).size */ -DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident) @safe +DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident) @safe { return new DotIdExp(loc, new TypeExp(loc, type), ident); } @@ -236,45 +214,49 @@ DotIdExp typeDotIdExp(const ref Loc loc, Type type, Identifier ident) @safe * For example, `a[index]` is really `a`, and `s.f` is really `s`. * Params: * e = Expression to look at + * deref = number of dereferences encountered * Returns: * variable if there is one, null if not */ -VarDeclaration expToVariable(Expression e) +VarDeclaration expToVariable(Expression e, out int deref) { + deref = 0; while (1) { switch (e.op) { case EXP.variable: - return (cast(VarExp)e).var.isVarDeclaration(); + return e.isVarExp().var.isVarDeclaration(); case EXP.dotVariable: - e = (cast(DotVarExp)e).e1; + e = e.isDotVarExp().e1; + if (e.type.toBasetype().isTypeClass()) + deref++; + continue; case EXP.index: { - IndexExp ei = cast(IndexExp)e; - e = ei.e1; - Type ti = e.type.toBasetype(); - if (ti.ty == Tsarray) - continue; - return null; + e = e.isIndexExp().e1; + if (!e.type.toBasetype().isTypeSArray()) + deref++; + + continue; } case EXP.slice: { - SliceExp ei = cast(SliceExp)e; - e = ei.e1; - Type ti = e.type.toBasetype(); - if (ti.ty == Tsarray) - continue; - return null; + e = e.isSliceExp().e1; + if (!e.type.toBasetype().isTypeSArray()) + deref++; + + continue; } - case EXP.this_: case EXP.super_: - return (cast(ThisExp)e).var.isVarDeclaration(); + return e.isSuperExp().var.isVarDeclaration(); + case EXP.this_: + return e.isThisExp().var.isVarDeclaration(); // Temporaries for rvalues that need destruction // are of form: (T s = rvalue, s). For these cases @@ -311,12 +293,24 @@ enum WANTexpand = 1; // expand const/immutable variables if possible */ extern (C++) abstract class Expression : ASTNode { - Type type; // !=null means that semantic() has been run + /// Usually, this starts out as `null` and gets set to the final expression type by + /// `expressionSemantic`. However, for some expressions (such as `TypeExp`,`RealExp`, + /// `VarExp`), the field can get set to an assigned type before running semantic. + /// See `expressionSemanticDone` + Type type; + Loc loc; // file location const EXP op; // to minimize use of dynamic_cast + + static struct BitFields + { bool parens; // if this is a parenthesized expression + bool rvalue; // true if this is considered to be an rvalue, even if it is an lvalue + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); - extern (D) this(const ref Loc loc, EXP op) scope @safe + extern (D) this(Loc loc, EXP op) scope @safe { //printf("Expression::Expression(op = %d) this = %p\n", op, this); this.loc = loc; @@ -387,8 +381,12 @@ extern (C++) abstract class Expression : ASTNode return DYNCAST.expression; } - override const(char)* toChars() const + final override const(char)* toChars() const { + // FIXME: mangling (see runnable/mangle.d) relies on toChars outputting __lambdaXXX here + if (auto fe = isFuncExp()) + return fe.fd.toChars(); + return .toChars(this); } @@ -426,7 +424,7 @@ extern (C++) abstract class Expression : ASTNode * is returned via e0. * Otherwise 'e' is directly returned and e0 is set to NULL. */ - extern (D) static Expression extractLast(Expression e, out Expression e0) @safe + extern (D) static Expression extractLast(Expression e, out Expression e0) @trusted { if (e.op != EXP.comma) { @@ -473,7 +471,7 @@ extern (C++) abstract class Expression : ASTNode dinteger_t toInteger() { //printf("Expression %s\n", EXPtoString(op).ptr); - if (!type.isTypeError()) + if (!type || !type.isTypeError()) error(loc, "integer constant expression expected instead of `%s`", toChars()); return 0; } @@ -528,117 +526,6 @@ extern (C++) abstract class Expression : ASTNode return false; } - /**************************************** - * Check that the expression has a valid value. - * If not, generates an error "... has no value". - * Returns: - * true if the expression is not valid or has void type. - */ - bool checkValue() - { - if (type && type.toBasetype().ty == Tvoid) - { - error(loc, "expression `%s` is `void` and has no value", toChars()); - //print(); assert(0); - if (!global.gag) - type = Type.terror; - return true; - } - return false; - } - - extern (D) final bool checkScalar() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isscalar()) - { - error(loc, "`%s` is not a scalar, it is a `%s`", toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - extern (D) final bool checkNoBool() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (type.toBasetype().ty == Tbool) - { - error(loc, "operation not allowed on `bool` `%s`", toChars()); - return true; - } - return false; - } - - extern (D) final bool checkIntegral() - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isintegral()) - { - error(loc, "`%s` is not of integral type, it is a `%s`", toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - extern (D) final bool checkArithmetic(EXP op) - { - if (op == EXP.error) - return true; - if (type.toBasetype().ty == Terror) - return true; - if (!type.isintegral() && !type.isfloating()) - { - // unary aggregate ops error here - const char* msg = type.isAggregate() ? - "operator `%s` is not defined for `%s` of type `%s`" : - "illegal operator `%s` for `%s` of type `%s`"; - error(loc, msg, EXPtoString(op).ptr, toChars(), type.toChars()); - return true; - } - return checkValue(); - } - - /******************************* - * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. - * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) - * Returns true if error occurs. - */ - extern (D) final bool checkReadModifyWrite(EXP rmwOp, Expression ex = null) - { - //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : ""); - if (!type || !type.isShared() || type.isTypeStruct() || type.isTypeClass()) - return false; - - // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. - switch (rmwOp) - { - case EXP.plusPlus: - case EXP.prePlusPlus: - rmwOp = EXP.addAssign; - break; - case EXP.minusMinus: - case EXP.preMinusMinus: - rmwOp = EXP.minAssign; - break; - default: - break; - } - - error(loc, "read-modify-write operations are not allowed for `shared` variables"); - errorSupplemental(loc, "Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", - EXPtoString(rmwOp).ptr, toChars(), ex ? ex.toChars() : "1"); - return true; - } - /****************************** * Take address of expression. */ @@ -709,7 +596,7 @@ extern (C++) abstract class Expression : ASTNode return true; } - final pure inout nothrow @nogc @safe + final pure inout nothrow @nogc @trusted { inout(IntegerExp) isIntegerExp() { return op == EXP.int64 ? cast(typeof(return))this : null; } inout(ErrorExp) isErrorExp() { return op == EXP.error ? cast(typeof(return))this : null; } @@ -742,7 +629,7 @@ extern (C++) abstract class Expression : ASTNode inout(TypeidExp) isTypeidExp() { return op == EXP.typeid_ ? cast(typeof(return))this : null; } inout(TraitsExp) isTraitsExp() { return op == EXP.traits ? cast(typeof(return))this : null; } inout(HaltExp) isHaltExp() { return op == EXP.halt ? cast(typeof(return))this : null; } - inout(IsExp) isExp() { return op == EXP.is_ ? cast(typeof(return))this : null; } + inout(IsExp) isIsExp() { return op == EXP.is_ ? cast(typeof(return))this : null; } inout(MixinExp) isMixinExp() { return op == EXP.mixin_ ? cast(typeof(return))this : null; } inout(ImportExp) isImportExp() { return op == EXP.import_ ? cast(typeof(return))this : null; } inout(AssertExp) isAssertExp() { return op == EXP.assert_ ? cast(typeof(return))this : null; } @@ -869,12 +756,12 @@ extern (C++) final class IntegerExp : Expression { private dinteger_t value; - extern (D) this(const ref Loc loc, dinteger_t value, Type type) + extern (D) this(Loc loc, dinteger_t value, Type type) { super(loc, EXP.int64); //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type.toChars() : ""); assert(type); - if (!type.isscalar()) + if (!type.isScalar()) { //printf("%s, loc = %d\n", toChars(), loc.linnum); if (type.ty != Terror) @@ -892,7 +779,7 @@ extern (C++) final class IntegerExp : Expression this.value = cast(int)value; } - static IntegerExp create(const ref Loc loc, dinteger_t value, Type type) + static IntegerExp create(Loc loc, dinteger_t value, Type type) { return new IntegerExp(loc, value, type); } @@ -1086,7 +973,7 @@ extern (C++) final class ErrorExp : Expression * and we need to set the error count to prevent bogus code * generation. At least give a message. */ - .error(Loc.initial, "unknown, please file report on issues.dlang.org"); + .error(Loc.initial, "unknown, please file report at https://github.com/dlang/dmd/issues/new"); } return errorexp; @@ -1133,7 +1020,7 @@ extern (C++) final class RealExp : Expression { real_t value; - extern (D) this(const ref Loc loc, real_t value, Type type) @safe + extern (D) this(Loc loc, real_t value, Type type) @safe { super(loc, EXP.float64); //printf("RealExp::RealExp(%Lg)\n", value); @@ -1141,7 +1028,7 @@ extern (C++) final class RealExp : Expression this.type = type; } - static RealExp create(const ref Loc loc, real_t value, Type type) @safe + static RealExp create(Loc loc, real_t value, Type type) @safe { return new RealExp(loc, value, type); } @@ -1194,12 +1081,12 @@ extern (C++) final class RealExp : Expression override real_t toReal() { - return type.isreal() ? value : CTFloat.zero; + return type.isReal() ? value : CTFloat.zero; } override real_t toImaginary() { - return type.isreal() ? CTFloat.zero : value; + return type.isReal() ? CTFloat.zero : value; } override complex_t toComplex() @@ -1225,7 +1112,7 @@ extern (C++) final class ComplexExp : Expression { complex_t value; - extern (D) this(const ref Loc loc, complex_t value, Type type) @safe + extern (D) this(Loc loc, complex_t value, Type type) @safe { super(loc, EXP.complex80); this.value = value; @@ -1233,7 +1120,7 @@ extern (C++) final class ComplexExp : Expression //printf("ComplexExp::ComplexExp(%s)\n", toChars()); } - static ComplexExp create(const ref Loc loc, complex_t value, Type type) @safe + static ComplexExp create(Loc loc, complex_t value, Type type) @safe { return new ComplexExp(loc, value, type); } @@ -1312,20 +1199,20 @@ extern (C++) class IdentifierExp : Expression { Identifier ident; - extern (D) this(const ref Loc loc, Identifier ident) scope @safe + extern (D) this(Loc loc, Identifier ident) scope @safe { super(loc, EXP.identifier); this.ident = ident; } - static IdentifierExp create(const ref Loc loc, Identifier ident) @safe + static IdentifierExp create(Loc loc, Identifier ident) @safe { return new IdentifierExp(loc, ident); } override final bool isLvalue() { - return true; + return !this.rvalue; } override void accept(Visitor v) @@ -1341,7 +1228,7 @@ extern (C++) class IdentifierExp : Expression */ extern (C++) final class DollarExp : IdentifierExp { - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, Id.dollar); } @@ -1360,7 +1247,7 @@ extern (C++) final class DsymbolExp : Expression Dsymbol s; bool hasOverloads; - extern (D) this(const ref Loc loc, Dsymbol s, bool hasOverloads = true) @safe + extern (D) this(Loc loc, Dsymbol s, bool hasOverloads = true) @safe { super(loc, EXP.dSymbol); this.s = s; @@ -1369,7 +1256,7 @@ extern (C++) final class DsymbolExp : Expression override bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -1385,13 +1272,13 @@ extern (C++) class ThisExp : Expression { VarDeclaration var; - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.this_); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); } - this(const ref Loc loc, const EXP tok) @safe + this(Loc loc, const EXP tok) @safe { super(loc, tok); //printf("ThisExp::ThisExp() loc = %d\n", loc.linnum); @@ -1415,7 +1302,7 @@ extern (C++) class ThisExp : Expression override final bool isLvalue() { // Class `this` should be an rvalue; struct `this` should be an lvalue. - return type.toBasetype().ty != Tclass; + return !rvalue && type.toBasetype().ty != Tclass; } override void accept(Visitor v) @@ -1429,7 +1316,7 @@ extern (C++) class ThisExp : Expression */ extern (C++) final class SuperExp : ThisExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.super_); } @@ -1447,7 +1334,7 @@ extern (C++) final class SuperExp : ThisExp */ extern (C++) final class NullExp : Expression { - extern (D) this(const ref Loc loc, Type type = null) scope @safe + extern (D) this(Loc loc, Type type = null) scope @safe { super(loc, EXP.null_); this.type = type; @@ -1521,7 +1408,7 @@ extern (C++) final class StringExp : Expression enum char NoPostfix = 0; - extern (D) this(const ref Loc loc, const(void)[] string) scope + extern (D) this(Loc loc, const(void)[] string) scope { super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const @@ -1529,7 +1416,7 @@ extern (C++) final class StringExp : Expression this.sz = 1; // work around LDC bug #1286 } - extern (D) this(const ref Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope + extern (D) this(Loc loc, const(void)[] string, size_t len, ubyte sz, char postfix = NoPostfix) scope { super(loc, EXP.string_); this.string = cast(char*)string.ptr; // note that this.string should be const @@ -1538,12 +1425,12 @@ extern (C++) final class StringExp : Expression this.postfix = postfix; } - static StringExp create(const ref Loc loc, const(char)* s) + static StringExp create(Loc loc, const(char)* s) { return new StringExp(loc, s.toDString()); } - static StringExp create(const ref Loc loc, const(void)* string, size_t len) + static StringExp create(Loc loc, const(void)* string, size_t len) { return new StringExp(loc, string[0 .. len]); } @@ -1800,7 +1687,7 @@ extern (C++) final class StringExp : Expression /* string literal is rvalue in default, but * conversion to reference of static array is only allowed. */ - return (type && type.toBasetype().ty == Tsarray); + return !rvalue && (type && type.toBasetype().ty == Tsarray); } /******************************** @@ -1875,7 +1762,7 @@ extern (C++) final class InterpExp : Expression enum char NoPostfix = 0; - extern (D) this(const ref Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope + extern (D) this(Loc loc, InterpolatedSet* set, char postfix = NoPostfix) scope @safe { super(loc, EXP.interpolated); this.interpolatedSet = set; @@ -1910,7 +1797,7 @@ extern (C++) final class TupleExp : Expression Expressions* exps; - extern (D) this(const ref Loc loc, Expression e0, Expressions* exps) @safe + extern (D) this(Loc loc, Expression e0, Expressions* exps) @safe { super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); @@ -1918,14 +1805,14 @@ extern (C++) final class TupleExp : Expression this.exps = exps; } - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, EXP.tuple); //printf("TupleExp(this = %p)\n", this); this.exps = exps; } - extern (D) this(const ref Loc loc, TupleDeclaration tup) + extern (D) this(Loc loc, TupleDeclaration tup) { super(loc, EXP.tuple); this.exps = new Expressions(); @@ -1959,7 +1846,7 @@ extern (C++) final class TupleExp : Expression } } - static TupleExp create(const ref Loc loc, Expressions* exps) @safe + static TupleExp create(Loc loc, Expressions* exps) @safe { return new TupleExp(loc, exps); } @@ -2015,14 +1902,14 @@ extern (C++) final class ArrayLiteralExp : Expression Expressions* elements; - extern (D) this(const ref Loc loc, Type type, Expressions* elements) @safe + extern (D) this(Loc loc, Type type, Expressions* elements) @safe { super(loc, EXP.arrayLiteral); this.type = type; this.elements = elements; } - extern (D) this(const ref Loc loc, Type type, Expression e) + extern (D) this(Loc loc, Type type, Expression e) { super(loc, EXP.arrayLiteral); this.type = type; @@ -2030,7 +1917,7 @@ extern (C++) final class ArrayLiteralExp : Expression elements.push(e); } - extern (D) this(const ref Loc loc, Type type, Expression basis, Expressions* elements) @safe + extern (D) this(Loc loc, Type type, Expression basis, Expressions* elements) @safe { super(loc, EXP.arrayLiteral); this.type = type; @@ -2038,7 +1925,7 @@ extern (C++) final class ArrayLiteralExp : Expression this.elements = elements; } - static ArrayLiteralExp create(const ref Loc loc, Expressions* elements) @safe + static ArrayLiteralExp create(Loc loc, Expressions* elements) @safe { return new ArrayLiteralExp(loc, null, elements); } @@ -2171,7 +2058,7 @@ extern (C++) final class AssocArrayLiteralExp : Expression /// Lower to core.internal.newaa for static initializaton Expression lowering; - extern (D) this(const ref Loc loc, Expressions* keys, Expressions* values) @safe + extern (D) this(Loc loc, Expressions* keys, Expressions* values) @safe { super(loc, EXP.assocArrayLiteral); assert(keys.length == values.length); @@ -2225,18 +2112,21 @@ extern (C++) final class AssocArrayLiteralExp : Expression } } -enum stageScrub = 0x1; /// scrubReturnValue is running -enum stageSearchPointers = 0x2; /// hasNonConstPointers is running -enum stageOptimize = 0x4; /// optimize is running -enum stageApply = 0x8; /// apply is running -enum stageInlineScan = 0x10; /// inlineScan is running -enum stageToCBuffer = 0x20; /// toCBuffer is running - /*********************************************************** * sd( e1, e2, e3, ... ) */ extern (C++) final class StructLiteralExp : Expression { + struct BitFields + { + bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol + bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` + OwnedBy ownedByCtfe = OwnedBy.code; + } + import dmd.common.bitfields; + mixin(generateBitFields!(BitFields, ubyte)); + StageFlags stageflags; + StructDeclaration sd; /// which aggregate this is for Expressions* elements; /// parallels sd.fields[] with null entries for fields to skip Type stype; /// final type of result (can be different from sd's type) @@ -2264,13 +2154,18 @@ extern (C++) final class StructLiteralExp : Expression * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' * (with infinite recursion) of this expression. */ - ubyte stageflags; - - bool useStaticInit; /// if this is true, use the StructDeclaration's init symbol - bool isOriginal = false; /// used when moving instances to indicate `this is this.origin` - OwnedBy ownedByCtfe = OwnedBy.code; + enum StageFlags : ubyte + { + none = 0x0, + scrub = 0x1, /// scrubReturnValue is running + searchPointers = 0x2, /// hasNonConstPointers is running + optimize = 0x4, /// optimize is running + apply = 0x8, /// apply is running + inlineScan = 0x10, /// inlineScan is running + toCBuffer = 0x20 /// toCBuffer is running + } - extern (D) this(const ref Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) @safe + extern (D) this(Loc loc, StructDeclaration sd, Expressions* elements, Type stype = null) @safe { super(loc, EXP.structLiteral); this.sd = sd; @@ -2282,7 +2177,7 @@ extern (C++) final class StructLiteralExp : Expression //printf("StructLiteralExp::StructLiteralExp(%s)\n", toChars()); } - static StructLiteralExp create(const ref Loc loc, StructDeclaration sd, void* elements, Type stype = null) + static StructLiteralExp create(Loc loc, StructDeclaration sd, void* elements, Type stype = null) { return new StructLiteralExp(loc, sd, cast(Expressions*)elements, stype); } @@ -2413,7 +2308,7 @@ extern (C++) final class CompoundLiteralExp : Expression { Initializer initializer; /// initializer-list - extern (D) this(const ref Loc loc, Type type_name, Initializer initializer) @safe + extern (D) this(Loc loc, Type type_name, Initializer initializer) @safe { super(loc, EXP.compoundLiteral); super.type = type_name; @@ -2432,7 +2327,7 @@ extern (C++) final class CompoundLiteralExp : Expression */ extern (C++) final class TypeExp : Expression { - extern (D) this(const ref Loc loc, Type type) @safe + extern (D) this(Loc loc, Type type) @safe { super(loc, EXP.type); //printf("TypeExp::TypeExp(%s)\n", type.toChars()); @@ -2450,12 +2345,6 @@ extern (C++) final class TypeExp : Expression return true; } - override bool checkValue() - { - error(loc, "type `%s` has no value", toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2474,7 +2363,7 @@ extern (C++) final class ScopeExp : Expression { ScopeDsymbol sds; - extern (D) this(const ref Loc loc, ScopeDsymbol sds) @safe + extern (D) this(Loc loc, ScopeDsymbol sds) @safe { super(loc, EXP.scope_); //printf("ScopeExp::ScopeExp(sds = '%s')\n", sds.toChars()); @@ -2509,12 +2398,6 @@ extern (C++) final class ScopeExp : Expression return false; } - override bool checkValue() - { - error(loc, "%s `%s` has no value", sds.kind(), sds.toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2529,7 +2412,7 @@ extern (C++) final class TemplateExp : Expression TemplateDeclaration td; FuncDeclaration fd; - extern (D) this(const ref Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) @safe + extern (D) this(Loc loc, TemplateDeclaration td, FuncDeclaration fd = null) @safe { super(loc, EXP.template_); //printf("TemplateExp(): %s\n", td.toChars()); @@ -2548,12 +2431,6 @@ extern (C++) final class TemplateExp : Expression return true; } - override bool checkValue() - { - error(loc, "%s `%s` has no value", td.kind(), toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -2569,6 +2446,7 @@ extern (C++) final class NewExp : Expression Type newtype; Expressions* arguments; // Array of Expression's Identifiers* names; // Array of names corresponding to expressions + Expression placement; // if !=null, then PlacementExpression Expression argprefix; // expression to be evaluated just before arguments[] CtorDeclaration member; // constructor function @@ -2581,23 +2459,25 @@ extern (C++) final class NewExp : Expression /// The fields are still separate for backwards compatibility extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); } - extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe + extern (D) this(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe { super(loc, EXP.new_); + this.placement = placement; this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; this.names = names; } - static NewExp create(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments) @safe + static NewExp create(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments) @safe { - return new NewExp(loc, thisexp, newtype, arguments); + return new NewExp(loc, placement, thisexp, newtype, arguments); } override NewExp syntaxCopy() { return new NewExp(loc, + placement ? placement.syntaxCopy() : null, thisexp ? thisexp.syntaxCopy() : null, newtype.syntaxCopy(), arraySyntaxCopy(arguments), @@ -2618,10 +2498,12 @@ extern (C++) final class NewAnonClassExp : Expression Expression thisexp; // if !=null, 'this' for class being allocated ClassDeclaration cd; // class being instantiated Expressions* arguments; // Array of Expression's to call class constructor + Expression placement; // if !=null, then PlacementExpression - extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe + extern (D) this(Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe { super(loc, EXP.newAnonymousClass); + this.placement = placement; this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -2629,7 +2511,9 @@ extern (C++) final class NewAnonClassExp : Expression override NewAnonClassExp syntaxCopy() { - return new NewAnonClassExp(loc, thisexp ? thisexp.syntaxCopy() : null, cd.syntaxCopy(null), arraySyntaxCopy(arguments)); + return new NewAnonClassExp(loc, placement ? placement.syntaxCopy : null, + thisexp ? thisexp.syntaxCopy() : null, + cd.syntaxCopy(null), arraySyntaxCopy(arguments)); } override void accept(Visitor v) @@ -2646,7 +2530,7 @@ extern (C++) class SymbolExp : Expression Dsymbol originalScope; // original scope before inlining bool hasOverloads; - extern (D) this(const ref Loc loc, EXP op, Declaration var, bool hasOverloads) @safe + extern (D) this(Loc loc, EXP op, Declaration var, bool hasOverloads) @safe { super(loc, op); assert(var); @@ -2667,7 +2551,7 @@ extern (C++) final class SymOffExp : SymbolExp { dinteger_t offset; - extern (D) this(const ref Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true) + extern (D) this(Loc loc, Declaration var, dinteger_t offset, bool hasOverloads = true) { if (auto v = var.isVarDeclaration()) { @@ -2702,7 +2586,7 @@ extern (C++) final class SymOffExp : SymbolExp extern (C++) final class VarExp : SymbolExp { bool delegateWasExtracted; - extern (D) this(const ref Loc loc, Declaration var, bool hasOverloads = true) @safe + extern (D) this(Loc loc, Declaration var, bool hasOverloads = true) @safe { if (var.isVarDeclaration()) hasOverloads = false; @@ -2713,7 +2597,7 @@ extern (C++) final class VarExp : SymbolExp this.type = var.type; } - static VarExp create(const ref Loc loc, Declaration var, bool hasOverloads = true) @safe + static VarExp create(Loc loc, Declaration var, bool hasOverloads = true) @safe { return new VarExp(loc, var, hasOverloads); } @@ -2734,7 +2618,7 @@ extern (C++) final class VarExp : SymbolExp override bool isLvalue() { - if (var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)) + if (rvalue || var.storage_class & (STC.lazy_ | STC.rvalue | STC.manifest)) return false; return true; } @@ -2752,7 +2636,7 @@ extern (C++) final class OverExp : Expression { OverloadSet vars; - extern (D) this(const ref Loc loc, OverloadSet s) + extern (D) this(Loc loc, OverloadSet s) { super(loc, EXP.overloadSet); //printf("OverExp(this = %p, '%s')\n", this, var.toChars()); @@ -2781,7 +2665,7 @@ extern (C++) final class FuncExp : Expression TemplateDeclaration td; TOK tok; // TOK.reserved, TOK.delegate_, TOK.function_ - extern (D) this(const ref Loc loc, Dsymbol s) + extern (D) this(Loc loc, Dsymbol s) { super(loc, EXP.function_); this.td = s.isTemplateDeclaration(); @@ -2814,16 +2698,11 @@ extern (C++) final class FuncExp : Expression { if (td) return new FuncExp(loc, td.syntaxCopy(null)); - else if (fd.semanticRun == PASS.initial) + if (fd.semanticRun == PASS.initial) return new FuncExp(loc, fd.syntaxCopy(null)); - else // https://issues.dlang.org/show_bug.cgi?id=13481 - // Prevent multiple semantic analysis of lambda body. - return new FuncExp(loc, fd); - } - - override const(char)* toChars() const - { - return fd.toChars(); + // https://issues.dlang.org/show_bug.cgi?id=13481 + // Prevent multiple semantic analysis of lambda body. + return new FuncExp(loc, fd); } override bool checkType() @@ -2836,16 +2715,6 @@ extern (C++) final class FuncExp : Expression return false; } - override bool checkValue() - { - if (td) - { - error(loc, "template lambda has no value"); - return true; - } - return false; - } - override void accept(Visitor v) { v.visit(this); @@ -2863,7 +2732,7 @@ extern (C++) final class DeclarationExp : Expression { Dsymbol declaration; - extern (D) this(const ref Loc loc, Dsymbol declaration) @safe + extern (D) this(Loc loc, Dsymbol declaration) @safe { super(loc, EXP.declaration); this.declaration = declaration; @@ -2896,7 +2765,7 @@ extern (C++) final class TypeidExp : Expression { RootObject obj; - extern (D) this(const ref Loc loc, RootObject o) @safe + extern (D) this(Loc loc, RootObject o) @safe { super(loc, EXP.typeid_); this.obj = o; @@ -2921,7 +2790,7 @@ extern (C++) final class TraitsExp : Expression Identifier ident; Objects* args; - extern (D) this(const ref Loc loc, Identifier ident, Objects* args) @safe + extern (D) this(Loc loc, Identifier ident, Objects* args) @safe { super(loc, EXP.traits); this.ident = ident; @@ -2946,7 +2815,7 @@ extern (C++) final class TraitsExp : Expression */ extern (C++) final class HaltExp : Expression { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.halt); } @@ -2970,7 +2839,7 @@ extern (C++) final class IsExp : Expression TOK tok; // ':' or '==' TOK tok2; // 'struct', 'union', etc. - extern (D) this(const ref Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope @safe + extern (D) this(Loc loc, Type targ, Identifier id, TOK tok, Type tspec, TOK tok2, TemplateParameters* parameters) scope @safe { super(loc, EXP.is_); this.targ = targ; @@ -3009,7 +2878,7 @@ extern (C++) abstract class UnaExp : Expression { Expression e1; - extern (D) this(const ref Loc loc, EXP op, Expression e1) scope @safe + extern (D) this(Loc loc, EXP op, Expression e1) scope @safe { super(loc, op); this.e1 = e1; @@ -3023,28 +2892,6 @@ extern (C++) abstract class UnaExp : Expression return e; } - /******************************** - * The type for a unary expression is incompatible. - * Print error message. - * Returns: - * ErrorExp - */ - extern (D) final Expression incompatibleTypes() - { - if (e1.type.toBasetype() == Type.terror) - return e1; - - if (e1.op == EXP.type) - { - error(loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(op).ptr, e1.toChars(), EXPtoString(op).ptr); - } - else - { - error(loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(op).ptr, e1.toChars(), e1.type.toChars()); - } - return ErrorExp.get(); - } - /********************* * Mark the operand as will never be dereferenced, * which is useful info for @safe checks. @@ -3070,10 +2917,8 @@ extern (C++) abstract class BinExp : Expression { Expression e1; Expression e2; - Type att1; // Save alias this type to detect recursion - Type att2; // Save alias this type to detect recursion - extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) scope @safe { super(loc, op); this.e1 = e1; @@ -3089,54 +2934,6 @@ extern (C++) abstract class BinExp : Expression return e; } - /******************************** - * The types for a binary expression are incompatible. - * Print error message. - * Returns: - * ErrorExp - */ - extern (D) final Expression incompatibleTypes() - { - if (e1.type.toBasetype() == Type.terror) - return e1; - if (e2.type.toBasetype() == Type.terror) - return e2; - - // CondExp uses 'a ? b : c' but we're comparing 'b : c' - const(char)* thisOp = (op == EXP.question) ? ":" : EXPtoString(op).ptr; - if (e1.op == EXP.type || e2.op == EXP.type) - { - error(loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types", - e1.toChars(), thisOp, e2.toChars(), EXPtoString(op).ptr); - } - else if (e1.type.equals(e2.type)) - { - error(loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`", - e1.toChars(), thisOp, e2.toChars(), e1.type.toChars()); - } - else - { - auto ts = toAutoQualChars(e1.type, e2.type); - error(loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`", - e1.toChars(), thisOp, e2.toChars(), ts[0], ts[1]); - } - return ErrorExp.get(); - } - - extern (D) final bool checkIntegralBin() - { - bool r1 = e1.checkIntegral(); - bool r2 = e2.checkIntegral(); - return (r1 || r2); - } - - extern (D) final bool checkArithmeticBin() - { - bool r1 = e1.checkArithmetic(this.op); - bool r2 = e2.checkArithmetic(this.op); - return (r1 || r2); - } - /********************* * Mark the operands as will never be dereferenced, * which is useful info for @safe checks. @@ -3162,14 +2959,14 @@ extern (C++) abstract class BinExp : Expression */ extern (C++) class BinAssignExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) scope @safe { super(loc, op, e1, e2); } override final bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -3187,7 +2984,7 @@ extern (C++) final class MixinExp : Expression { Expressions* exps; - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, EXP.mixin_); this.exps = exps; @@ -3235,7 +3032,7 @@ extern (C++) final class MixinExp : Expression */ extern (C++) final class ImportExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.import_, e); } @@ -3255,7 +3052,7 @@ extern (C++) final class AssertExp : UnaExp { Expression msg; - extern (D) this(const ref Loc loc, Expression e, Expression msg = null) @safe + extern (D) this(Loc loc, Expression e, Expression msg = null) @safe { super(loc, EXP.assert_, e); this.msg = msg; @@ -3280,10 +3077,9 @@ extern (C++) final class AssertExp : UnaExp */ extern (C++) final class ThrowExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) + extern (D) this(Loc loc, Expression e) { super(loc, EXP.throw_, e); - this.type = Type.tnoreturn; } override ThrowExp syntaxCopy() @@ -3306,13 +3102,13 @@ extern (C++) final class DotIdExp : UnaExp bool wantsym; // do not replace Symbol with its initializer during semantic() bool arrow; // ImportC: if -> instead of . - extern (D) this(const ref Loc loc, Expression e, Identifier ident) @safe + extern (D) this(Loc loc, Expression e, Identifier ident) @safe { super(loc, EXP.dotIdentifier, e); this.ident = ident; } - static DotIdExp create(const ref Loc loc, Expression e, Identifier ident) @safe + static DotIdExp create(Loc loc, Expression e, Identifier ident) @safe { return new DotIdExp(loc, e, ident); } @@ -3330,7 +3126,7 @@ extern (C++) final class DotTemplateExp : UnaExp { TemplateDeclaration td; - extern (D) this(const ref Loc loc, Expression e, TemplateDeclaration td) @safe + extern (D) this(Loc loc, Expression e, TemplateDeclaration td) @safe { super(loc, EXP.dotTemplateDeclaration, e); this.td = td; @@ -3342,12 +3138,6 @@ extern (C++) final class DotTemplateExp : UnaExp return true; } - override bool checkValue() - { - error(loc, "%s `%s` has no value", td.kind(), toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -3361,7 +3151,7 @@ extern (C++) final class DotVarExp : UnaExp Declaration var; bool hasOverloads; - extern (D) this(const ref Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe + extern (D) this(Loc loc, Expression e, Declaration var, bool hasOverloads = true) @safe { if (var.isVarDeclaration()) hasOverloads = false; @@ -3374,6 +3164,8 @@ extern (C++) final class DotVarExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; if (e1.op != EXP.structLiteral) return true; auto vd = var.isVarDeclaration(); @@ -3393,14 +3185,14 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp { TemplateInstance ti; - extern (D) this(const ref Loc loc, Expression e, Identifier name, Objects* tiargs) + extern (D) this(Loc loc, Expression e, Identifier name, Objects* tiargs) { super(loc, EXP.dotTemplateInstance, e); //printf("DotTemplateInstanceExp()\n"); this.ti = new TemplateInstance(loc, name, tiargs); } - extern (D) this(const ref Loc loc, Expression e, TemplateInstance ti) @safe + extern (D) this(Loc loc, Expression e, TemplateInstance ti) @safe { super(loc, EXP.dotTemplateInstance, e); this.ti = ti; @@ -3424,18 +3216,6 @@ extern (C++) final class DotTemplateInstanceExp : UnaExp return false; } - override bool checkValue() - { - if (ti.tempdecl && - ti.semantictiargsdone && - ti.semanticRun == PASS.initial) - - error(loc, "partial %s `%s` has no value", ti.kind(), toChars()); - else - error(loc, "%s `%s` has no value", ti.kind(), ti.toChars()); - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -3450,7 +3230,7 @@ extern (C++) final class DelegateExp : UnaExp bool hasOverloads; VarDeclaration vthis2; // container for multi-context - extern (D) this(const ref Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) @safe + extern (D) this(Loc loc, Expression e, FuncDeclaration f, bool hasOverloads = true, VarDeclaration vthis2 = null) @safe { super(loc, EXP.delegate_, e); this.func = f; @@ -3470,7 +3250,7 @@ extern (C++) final class DotTypeExp : UnaExp { Dsymbol sym; // symbol that represents a type - extern (D) this(const ref Loc loc, Expression e, Dsymbol s) @safe + extern (D) this(Loc loc, Expression e, Dsymbol s) @safe { super(loc, EXP.dotType, e); this.sym = s; @@ -3525,19 +3305,19 @@ extern (C++) final class CallExp : UnaExp /// The fields are still separate for backwards compatibility extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); } - extern (D) this(const ref Loc loc, Expression e, Expressions* exps, Identifiers* names = null) @safe + extern (D) this(Loc loc, Expression e, Expressions* exps, Identifiers* names = null) @safe { super(loc, EXP.call, e); this.arguments = exps; this.names = names; } - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.call, e); } - extern (D) this(const ref Loc loc, Expression e, Expression earg1) + extern (D) this(Loc loc, Expression e, Expression earg1) { super(loc, EXP.call, e); this.arguments = new Expressions(); @@ -3545,7 +3325,7 @@ extern (C++) final class CallExp : UnaExp this.arguments.push(earg1); } - extern (D) this(const ref Loc loc, Expression e, Expression earg1, Expression earg2) + extern (D) this(Loc loc, Expression e, Expression earg1, Expression earg2) { super(loc, EXP.call, e); auto arguments = new Expressions(2); @@ -3561,23 +3341,23 @@ extern (C++) final class CallExp : UnaExp * fd = the declaration of the function to call * earg1 = the function argument */ - extern(D) this(const ref Loc loc, FuncDeclaration fd, Expression earg1) + extern(D) this(Loc loc, FuncDeclaration fd, Expression earg1) { this(loc, new VarExp(loc, fd, false), earg1); this.f = fd; } - static CallExp create(const ref Loc loc, Expression e, Expressions* exps) @safe + static CallExp create(Loc loc, Expression e, Expressions* exps) @safe { return new CallExp(loc, e, exps); } - static CallExp create(const ref Loc loc, Expression e) @safe + static CallExp create(Loc loc, Expression e) @safe { return new CallExp(loc, e); } - static CallExp create(const ref Loc loc, Expression e, Expression earg1) + static CallExp create(Loc loc, Expression e, Expression earg1) { return new CallExp(loc, e, earg1); } @@ -3589,7 +3369,7 @@ extern (C++) final class CallExp : UnaExp * fd = the declaration of the function to call * earg1 = the function argument */ - static CallExp create(const ref Loc loc, FuncDeclaration fd, Expression earg1) + static CallExp create(Loc loc, FuncDeclaration fd, Expression earg1) { return new CallExp(loc, fd, earg1); } @@ -3601,11 +3381,13 @@ extern (C++) final class CallExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; Type tb = e1.type.toBasetype(); if (tb.ty == Tdelegate || tb.ty == Tpointer) tb = tb.nextOf(); auto tf = tb.isTypeFunction(); - if (tf && tf.isref) + if (tf && tf.isRef) { if (auto dve = e1.isDotVarExp()) if (dve.var.isCtorDeclaration()) @@ -3635,10 +3417,9 @@ TypeFunction calledFunctionType(CallExp ce) t = t.toBasetype(); if (auto tf = t.isTypeFunction()) return tf; - else if (auto td = t.isTypeDelegate()) + if (auto td = t.isTypeDelegate()) return td.nextOf().isTypeFunction(); - else - return null; + return null; } FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe @@ -3682,12 +3463,12 @@ FuncDeclaration isFuncAddress(Expression e, bool* hasOverloads = null) @safe */ extern (C++) final class AddrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.address, e); } - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @safe { this(loc, e); type = t; @@ -3704,14 +3485,14 @@ extern (C++) final class AddrExp : UnaExp */ extern (C++) final class PtrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.star, e); //if (e.type) // type = ((TypePointer *)e.type).next; } - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @safe { super(loc, EXP.star, e); type = t; @@ -3719,7 +3500,7 @@ extern (C++) final class PtrExp : UnaExp override bool isLvalue() { - return true; + return !rvalue; } override void accept(Visitor v) @@ -3733,7 +3514,7 @@ extern (C++) final class PtrExp : UnaExp */ extern (C++) final class NegExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.negate, e); } @@ -3749,7 +3530,7 @@ extern (C++) final class NegExp : UnaExp */ extern (C++) final class UAddExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) scope @safe + extern (D) this(Loc loc, Expression e) scope @safe { super(loc, EXP.uadd, e); } @@ -3765,7 +3546,7 @@ extern (C++) final class UAddExp : UnaExp */ extern (C++) final class ComExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.tilde, e); } @@ -3781,7 +3562,7 @@ extern (C++) final class ComExp : UnaExp */ extern (C++) final class NotExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e) @safe + extern (D) this(Loc loc, Expression e) @safe { super(loc, EXP.not, e); } @@ -3801,7 +3582,7 @@ extern (C++) final class DeleteExp : UnaExp { bool isRAII; // true if called automatically as a result of scoped destruction - extern (D) this(const ref Loc loc, Expression e, bool isRAII) @safe + extern (D) this(Loc loc, Expression e, bool isRAII) @safe { super(loc, EXP.delete_, e); this.isRAII = isRAII; @@ -3824,8 +3605,9 @@ extern (C++) final class CastExp : UnaExp { Type to; // type to cast to ubyte mod = cast(ubyte)~0; // MODxxxxx + bool trusted; // assume cast is safe - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @safe { super(loc, EXP.cast_, e); this.to = t; @@ -3833,7 +3615,7 @@ extern (C++) final class CastExp : UnaExp /* For cast(const) and cast(immutable) */ - extern (D) this(const ref Loc loc, Expression e, ubyte mod) @safe + extern (D) this(Loc loc, Expression e, ubyte mod) @safe { super(loc, EXP.cast_, e); this.mod = mod; @@ -3847,9 +3629,10 @@ extern (C++) final class CastExp : UnaExp override bool isLvalue() { //printf("e1.type = %s, to.type = %s\n", e1.type.toChars(), to.toChars()); - if (!e1.isLvalue()) + if (rvalue || !e1.isLvalue()) return false; return (to.ty == Tsarray && (e1.type.ty == Tvector || e1.type.ty == Tsarray)) || + (to.ty == Taarray && e1.type.ty == Taarray) || e1.type.mutableOf.unSharedOf().equals(to.mutableOf().unSharedOf()); } @@ -3867,14 +3650,14 @@ extern (C++) final class VectorExp : UnaExp uint dim = ~0; // number of elements in the vector OwnedBy ownedByCtfe = OwnedBy.code; - extern (D) this(const ref Loc loc, Expression e, Type t) @safe + extern (D) this(Loc loc, Expression e, Type t) @trusted { super(loc, EXP.vector, e); assert(t.ty == Tvector); to = cast(TypeVector)t; } - static VectorExp create(const ref Loc loc, Expression e, Type t) @safe + static VectorExp create(Loc loc, Expression e, Type t) @safe { return new VectorExp(loc, e, t); } @@ -3897,14 +3680,14 @@ extern (C++) final class VectorExp : UnaExp */ extern (C++) final class VectorArrayExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.vectorArray, e1); } override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -3935,14 +3718,14 @@ extern (C++) final class SliceExp : UnaExp mixin(generateBitFields!(BitFields, ubyte)); /************************************************************/ - extern (D) this(const ref Loc loc, Expression e1, IntervalExp ie) @safe + extern (D) this(Loc loc, Expression e1, IntervalExp ie) @safe { super(loc, EXP.slice, e1); this.upr = ie ? ie.upr : null; this.lwr = ie ? ie.lwr : null; } - extern (D) this(const ref Loc loc, Expression e1, Expression lwr, Expression upr) @safe + extern (D) this(Loc loc, Expression e1, Expression lwr, Expression upr) @safe { super(loc, EXP.slice, e1); this.upr = upr; @@ -3961,7 +3744,7 @@ extern (C++) final class SliceExp : UnaExp /* slice expression is rvalue in default, but * conversion to reference of static array is only allowed. */ - return (type && type.toBasetype().ty == Tsarray); + return !rvalue && (type && type.toBasetype().ty == Tsarray); } override Optional!bool toBool() @@ -3980,7 +3763,7 @@ extern (C++) final class SliceExp : UnaExp */ extern (C++) final class ArrayLengthExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.arrayLength, e1); } @@ -4003,7 +3786,7 @@ extern (C++) final class ArrayExp : UnaExp size_t currentDimension; // for opDollar VarDeclaration lengthVar; - extern (D) this(const ref Loc loc, Expression e1, Expression index = null) + extern (D) this(Loc loc, Expression e1, Expression index = null) { super(loc, EXP.array, e1); arguments = new Expressions(); @@ -4011,7 +3794,7 @@ extern (C++) final class ArrayExp : UnaExp arguments.push(index); } - extern (D) this(const ref Loc loc, Expression e1, Expressions* args) @safe + extern (D) this(Loc loc, Expression e1, Expressions* args) @safe { super(loc, EXP.array, e1); arguments = args; @@ -4026,6 +3809,8 @@ extern (C++) final class ArrayExp : UnaExp override bool isLvalue() { + if (rvalue) + return false; if (type && type.toBasetype().ty == Tvoid) return false; return true; @@ -4041,7 +3826,7 @@ extern (C++) final class ArrayExp : UnaExp */ extern (C++) final class DotExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.dot, e1, e2); } @@ -4067,7 +3852,7 @@ extern (C++) final class CommaExp : BinExp bool allowCommaExp; - extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool generated = true) @safe + extern (D) this(Loc loc, Expression e1, Expression e2, bool generated = true) @safe { super(loc, EXP.comma, e1, e2); allowCommaExp = isGenerated = generated; @@ -4075,7 +3860,7 @@ extern (C++) final class CommaExp : BinExp override bool isLvalue() { - return e2.isLvalue(); + return !rvalue && e2.isLvalue(); } override Optional!bool toBool() @@ -4118,7 +3903,7 @@ extern (C++) final class IntervalExp : Expression Expression lwr; Expression upr; - extern (D) this(const ref Loc loc, Expression lwr, Expression upr) @safe + extern (D) this(Loc loc, Expression lwr, Expression upr) @safe { super(loc, EXP.interval); this.lwr = lwr; @@ -4143,14 +3928,14 @@ extern (C++) final class IntervalExp : Expression */ extern (C++) final class DelegatePtrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.delegatePointer, e1); } override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -4166,14 +3951,14 @@ extern (C++) final class DelegatePtrExp : UnaExp */ extern (C++) final class DelegateFuncptrExp : UnaExp { - extern (D) this(const ref Loc loc, Expression e1) @safe + extern (D) this(Loc loc, Expression e1) @safe { super(loc, EXP.delegateFunctionPointer, e1); } override bool isLvalue() { - return e1.isLvalue(); + return !rvalue && e1.isLvalue(); } override void accept(Visitor v) @@ -4191,13 +3976,13 @@ extern (C++) final class IndexExp : BinExp bool modifiable = false; // assume it is an rvalue bool indexIsInBounds; // true if 0 <= e2 && e2 <= e1.length - 1 - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.index, e1, e2); //printf("IndexExp::IndexExp('%s')\n", toChars()); } - extern (D) this(const ref Loc loc, Expression e1, Expression e2, bool indexIsInBounds) @safe + extern (D) this(Loc loc, Expression e1, Expression e2, bool indexIsInBounds) @safe { super(loc, EXP.index, e1, e2); this.indexIsInBounds = indexIsInBounds; @@ -4213,10 +3998,11 @@ extern (C++) final class IndexExp : BinExp override bool isLvalue() { - if (e1.op == EXP.assocArrayLiteral) + if (rvalue) return false; - if (e1.type.ty == Tsarray || - (e1.op == EXP.index && e1.type.ty != Tarray)) + auto t1b = e1.type.toBasetype(); + if (t1b.isTypeAArray() || t1b.isTypeSArray() || + (e1.isIndexExp() && t1b != t1b.isTypeDArray())) { return e1.isLvalue(); } @@ -4257,7 +4043,7 @@ extern (C++) final class IndexExp : BinExp */ extern (C++) final class PostExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e) + extern (D) this(EXP op, Loc loc, Expression e) { super(loc, op, e, IntegerExp.literal!1); assert(op == EXP.minusMinus || op == EXP.plusPlus); @@ -4274,7 +4060,7 @@ extern (C++) final class PostExp : BinExp */ extern (C++) final class PreExp : UnaExp { - extern (D) this(EXP op, const ref Loc loc, Expression e) @safe + extern (D) this(EXP op, Loc loc, Expression e) @safe { super(loc, op, e); assert(op == EXP.preMinusMinus || op == EXP.prePlusPlus); @@ -4304,12 +4090,12 @@ extern (C++) class AssignExp : BinExp /************************************************************/ /* op can be EXP.assign, EXP.construct, or EXP.blit */ - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.assign, e1, e2); } - this(const ref Loc loc, EXP tok, Expression e1, Expression e2) @safe + this(Loc loc, EXP tok, Expression e1, Expression e2) @safe { super(loc, tok, e1, e2); } @@ -4322,7 +4108,7 @@ extern (C++) class AssignExp : BinExp { return false; } - return true; + return !rvalue; } override void accept(Visitor v) @@ -4347,10 +4133,6 @@ extern (C++) final class LoweredAssignExp : AssignExp this.lowering = lowering; } - override const(char)* toChars() const - { - return lowering.toChars(); - } override void accept(Visitor v) { v.visit(this); @@ -4361,14 +4143,14 @@ extern (C++) final class LoweredAssignExp : AssignExp */ extern (C++) final class ConstructExp : AssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.construct, e1, e2); } // Internal use only. If `v` is a reference variable, the assignment // will become a reference initialization automatically. - extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2) @safe + extern (D) this(Loc loc, VarDeclaration v, Expression e2) @safe { auto ve = new VarExp(loc, v); assert(v.type && ve.type); @@ -4390,14 +4172,14 @@ extern (C++) final class ConstructExp : AssignExp */ extern (C++) final class BlitExp : AssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.blit, e1, e2); } // Internal use only. If `v` is a reference variable, the assinment // will become a reference rebinding automatically. - extern (D) this(const ref Loc loc, VarDeclaration v, Expression e2) @safe + extern (D) this(Loc loc, VarDeclaration v, Expression e2) @safe { auto ve = new VarExp(loc, v); assert(v.type && ve.type); @@ -4419,7 +4201,7 @@ extern (C++) final class BlitExp : AssignExp */ extern (C++) final class AddAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.addAssign, e1, e2); } @@ -4435,7 +4217,7 @@ extern (C++) final class AddAssignExp : BinAssignExp */ extern (C++) final class MinAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.minAssign, e1, e2); } @@ -4451,7 +4233,7 @@ extern (C++) final class MinAssignExp : BinAssignExp */ extern (C++) final class MulAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.mulAssign, e1, e2); } @@ -4467,7 +4249,7 @@ extern (C++) final class MulAssignExp : BinAssignExp */ extern (C++) final class DivAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.divAssign, e1, e2); } @@ -4483,7 +4265,7 @@ extern (C++) final class DivAssignExp : BinAssignExp */ extern (C++) final class ModAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.modAssign, e1, e2); } @@ -4499,7 +4281,7 @@ extern (C++) final class ModAssignExp : BinAssignExp */ extern (C++) final class AndAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.andAssign, e1, e2); } @@ -4515,7 +4297,7 @@ extern (C++) final class AndAssignExp : BinAssignExp */ extern (C++) final class OrAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.orAssign, e1, e2); } @@ -4531,7 +4313,7 @@ extern (C++) final class OrAssignExp : BinAssignExp */ extern (C++) final class XorAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.xorAssign, e1, e2); } @@ -4547,7 +4329,7 @@ extern (C++) final class XorAssignExp : BinAssignExp */ extern (C++) final class PowAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.powAssign, e1, e2); } @@ -4563,7 +4345,7 @@ extern (C++) final class PowAssignExp : BinAssignExp */ extern (C++) final class ShlAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.leftShiftAssign, e1, e2); } @@ -4579,7 +4361,7 @@ extern (C++) final class ShlAssignExp : BinAssignExp */ extern (C++) final class ShrAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.rightShiftAssign, e1, e2); } @@ -4595,7 +4377,7 @@ extern (C++) final class ShrAssignExp : BinAssignExp */ extern (C++) final class UshrAssignExp : BinAssignExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.unsignedRightShiftAssign, e1, e2); } @@ -4622,12 +4404,12 @@ extern (C++) class CatAssignExp : BinAssignExp { Expression lowering; // lowered druntime hook `_d_arrayappend{cTX,T}` - extern (D) this(const ref Loc loc, Expression e1, Expression e2) + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateAssign, e1, e2); } - extern (D) this(const ref Loc loc, EXP tok, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, EXP tok, Expression e1, Expression e2) @safe { super(loc, tok, e1, e2); } @@ -4643,7 +4425,7 @@ extern (C++) class CatAssignExp : BinAssignExp */ extern (C++) final class CatElemAssignExp : CatAssignExp { - extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Type type, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateElemAssign, e1, e2); this.type = type; @@ -4660,7 +4442,7 @@ extern (C++) final class CatElemAssignExp : CatAssignExp */ extern (C++) final class CatDcharAssignExp : CatAssignExp { - extern (D) this(const ref Loc loc, Type type, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Type type, Expression e1, Expression e2) @safe { super(loc, EXP.concatenateDcharAssign, e1, e2); this.type = type; @@ -4679,7 +4461,7 @@ extern (C++) final class CatDcharAssignExp : CatAssignExp */ extern (C++) final class AddExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.add, e1, e2); } @@ -4697,7 +4479,7 @@ extern (C++) final class AddExp : BinExp */ extern (C++) final class MinExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.min, e1, e2); } @@ -4717,7 +4499,7 @@ extern (C++) final class CatExp : BinExp { Expression lowering; // call to druntime hook `_d_arraycatnTX` - extern (D) this(const ref Loc loc, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, Expression e1, Expression e2) scope @safe { super(loc, EXP.concatenate, e1, e2); } @@ -4735,7 +4517,7 @@ extern (C++) final class CatExp : BinExp */ extern (C++) final class MulExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.mul, e1, e2); } @@ -4753,7 +4535,7 @@ extern (C++) final class MulExp : BinExp */ extern (C++) final class DivExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.div, e1, e2); } @@ -4771,7 +4553,7 @@ extern (C++) final class DivExp : BinExp */ extern (C++) final class ModExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.mod, e1, e2); } @@ -4789,7 +4571,7 @@ extern (C++) final class ModExp : BinExp */ extern (C++) final class PowExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.pow, e1, e2); } @@ -4807,7 +4589,7 @@ extern (C++) final class PowExp : BinExp */ extern (C++) final class ShlExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.leftShift, e1, e2); } @@ -4825,7 +4607,7 @@ extern (C++) final class ShlExp : BinExp */ extern (C++) final class ShrExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.rightShift, e1, e2); } @@ -4843,7 +4625,7 @@ extern (C++) final class ShrExp : BinExp */ extern (C++) final class UshrExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.unsignedRightShift, e1, e2); } @@ -4861,7 +4643,7 @@ extern (C++) final class UshrExp : BinExp */ extern (C++) final class AndExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.and, e1, e2); } @@ -4879,7 +4661,7 @@ extern (C++) final class AndExp : BinExp */ extern (C++) final class OrExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.or, e1, e2); } @@ -4897,7 +4679,7 @@ extern (C++) final class OrExp : BinExp */ extern (C++) final class XorExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.xor, e1, e2); } @@ -4916,7 +4698,7 @@ extern (C++) final class XorExp : BinExp */ extern (C++) final class LogicalExp : BinExp { - extern (D) this(const ref Loc loc, EXP op, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, EXP op, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.andAnd || op == EXP.orOr); @@ -4938,7 +4720,7 @@ extern (C++) final class LogicalExp : BinExp */ extern (C++) final class CmpExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.lessThan || op == EXP.lessOrEqual || op == EXP.greaterThan || op == EXP.greaterOrEqual); @@ -4959,7 +4741,7 @@ extern (C++) final class CmpExp : BinExp */ extern (C++) final class InExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(Loc loc, Expression e1, Expression e2) @safe { super(loc, EXP.in_, e1, e2); } @@ -4977,7 +4759,7 @@ extern (C++) final class InExp : BinExp */ extern (C++) final class RemoveExp : BinExp { - extern (D) this(const ref Loc loc, Expression e1, Expression e2) + extern (D) this(Loc loc, Expression e1, Expression e2) { super(loc, EXP.remove, e1, e2); type = Type.tbool; @@ -4998,7 +4780,7 @@ extern (C++) final class RemoveExp : BinExp */ extern (C++) final class EqualExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.equal || op == EXP.notEqual); @@ -5019,7 +4801,7 @@ extern (C++) final class EqualExp : BinExp */ extern (C++) final class IdentityExp : BinExp { - extern (D) this(EXP op, const ref Loc loc, Expression e1, Expression e2) @safe + extern (D) this(EXP op, Loc loc, Expression e1, Expression e2) @safe { super(loc, op, e1, e2); assert(op == EXP.identity || op == EXP.notIdentity); @@ -5040,7 +4822,7 @@ extern (C++) final class CondExp : BinExp { Expression econd; - extern (D) this(const ref Loc loc, Expression econd, Expression e1, Expression e2) scope @safe + extern (D) this(Loc loc, Expression econd, Expression e1, Expression e2) scope @safe { super(loc, EXP.question, e1, e2); this.econd = econd; @@ -5053,7 +4835,7 @@ extern (C++) final class CondExp : BinExp override bool isLvalue() { - return e1.isLvalue() && e2.isLvalue(); + return !rvalue && e1.isLvalue() && e2.isLvalue(); } override void accept(Visitor v) @@ -5084,7 +4866,7 @@ extern (C++) class DefaultInitExp : Expression * op = EXP.prettyFunction, EXP.functionString, EXP.moduleString, * EXP.line, EXP.file, EXP.fileFullPath */ - extern (D) this(const ref Loc loc, EXP op) @safe + extern (D) this(Loc loc, EXP op) @safe { super(loc, op); } @@ -5100,7 +4882,7 @@ extern (C++) class DefaultInitExp : Expression */ extern (C++) final class FileInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc, EXP tok) @safe + extern (D) this(Loc loc, EXP tok) @safe { super(loc, tok); } @@ -5116,7 +4898,7 @@ extern (C++) final class FileInitExp : DefaultInitExp */ extern (C++) final class LineInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.line); } @@ -5132,7 +4914,7 @@ extern (C++) final class LineInitExp : DefaultInitExp */ extern (C++) final class ModuleInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.moduleString); } @@ -5148,7 +4930,7 @@ extern (C++) final class ModuleInitExp : DefaultInitExp */ extern (C++) final class FuncInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.functionString); } @@ -5164,7 +4946,7 @@ extern (C++) final class FuncInitExp : DefaultInitExp */ extern (C++) final class PrettyFuncInitExp : DefaultInitExp { - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, EXP.prettyFunction); } @@ -5183,7 +4965,7 @@ extern (C++) final class ClassReferenceExp : Expression { StructLiteralExp value; - extern (D) this(const ref Loc loc, StructLiteralExp lit, Type type) @safe + extern (D) this(Loc loc, StructLiteralExp lit, Type type) @safe { super(loc, EXP.classReference); assert(lit && lit.sd && lit.sd.isClassDeclaration()); @@ -5256,27 +5038,6 @@ extern (C++) final class CTFEExp : Expression type = Type.tvoid; } - override const(char)* toChars() const - { - switch (op) - { - case EXP.cantExpression: - return "<cant>"; - case EXP.voidExpression: - return "cast(void)0"; - case EXP.showCtfeContext: - return "<error>"; - case EXP.break_: - return "<break>"; - case EXP.continue_: - return "<continue>"; - case EXP.goto_: - return "<goto>"; - default: - assert(0); - } - } - extern (D) __gshared CTFEExp cantexp; extern (D) __gshared CTFEExp voidexp; extern (D) __gshared CTFEExp breakexp; @@ -5306,18 +5067,13 @@ extern (C++) final class ThrownExceptionExp : Expression { ClassReferenceExp thrown; // the thing being tossed - extern (D) this(const ref Loc loc, ClassReferenceExp victim) @safe + extern (D) this(Loc loc, ClassReferenceExp victim) @safe { super(loc, EXP.thrownException); this.thrown = victim; this.type = victim.type; } - override const(char)* toChars() const - { - return "CTFE ThrownException"; - } - override void accept(Visitor v) { v.visit(this); @@ -5333,7 +5089,7 @@ extern (C++) final class ObjcClassReferenceExp : Expression { ClassDeclaration classDeclaration; - extern (D) this(const ref Loc loc, ClassDeclaration classDeclaration) + extern (D) this(Loc loc, ClassDeclaration classDeclaration) @safe { super(loc, EXP.objcClassReference); this.classDeclaration = classDeclaration; @@ -5355,7 +5111,7 @@ extern (C++) final class GenericExp : Expression Types* types; /// type-names for generic associations (null entry for `default`) Expressions* exps; /// 1:1 mapping of typeNames to exps - extern (D) this(const ref Loc loc, Expression cntlExp, Types* types, Expressions* exps) @safe + extern (D) this(Loc loc, Expression cntlExp, Types* types, Expressions* exps) @safe { super(loc, EXP._Generic); this.cntlExp = cntlExp; diff --git a/gcc/d/dmd/expression.h b/gcc/d/dmd/expression.h index 2f6bb84..3c8d90d 100644 --- a/gcc/d/dmd/expression.h +++ b/gcc/d/dmd/expression.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -40,6 +40,7 @@ class OverloadSet; class StringExp; class InterpExp; class LoweredAssignExp; +class StaticForeach; #ifdef IN_GCC typedef union tree_node Symbol; #else @@ -50,8 +51,9 @@ namespace dmd { // in expressionsem.d Expression *expressionSemantic(Expression *e, Scope *sc); + void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc); // in typesem.d - Expression *defaultInit(Type *mt, const Loc &loc, const bool isCfile = false); + Expression *defaultInit(Type *mt, Loc loc, const bool isCfile = false); // Entry point for CTFE. // A compile-time result is required. Give an error if not possible @@ -71,26 +73,18 @@ enum #define WANTvalue 0 // default #define WANTexpand 1 // expand const/immutable variables if possible -/** - * Specifies how the checkModify deals with certain situations - */ -enum class ModifyFlags -{ - /// Issue error messages on invalid modifications of the variable - none, - /// No errors are emitted for invalid modifications - noError = 0x1, - /// The modification occurs for a subfield of the current variable - fieldAssign = 0x2, -}; - class Expression : public ASTNode { public: Type *type; // !=NULL means that semantic() has been run Loc loc; // file location EXP op; // to minimize use of dynamic_cast - d_bool parens; // if this is a parenthesized expression + uint8_t bitFields; + + bool parens() const; + bool parens(bool v); + bool rvalue() const; + bool rvalue(bool v); size_t size() const; static void _init(); @@ -99,7 +93,7 @@ public: // kludge for template.isExpression() DYNCAST dyncast() const override final { return DYNCAST_EXPRESSION; } - const char *toChars() const override; + const char* toChars() const final override; virtual dinteger_t toInteger(); virtual uinteger_t toUInteger(); @@ -109,7 +103,6 @@ public: virtual StringExp *toStringExp(); virtual bool isLvalue(); virtual bool checkType(); - virtual bool checkValue(); Expression *addressOf(); Expression *deref(); @@ -151,7 +144,7 @@ public: TypeidExp* isTypeidExp(); TraitsExp* isTraitsExp(); HaltExp* isHaltExp(); - IsExp* isExp(); + IsExp* isIsExp(); MixinExp* isMixinExp(); ImportExp* isImportExp(); AssertExp* isAssertExp(); @@ -242,7 +235,7 @@ class IntegerExp final : public Expression public: dinteger_t value; - static IntegerExp *create(const Loc &loc, dinteger_t value, Type *type); + static IntegerExp *create(Loc loc, dinteger_t value, Type *type); bool equals(const RootObject * const o) const override; dinteger_t toInteger() override; real_t toReal() override; @@ -268,7 +261,7 @@ class RealExp final : public Expression public: real_t value; - static RealExp *create(const Loc &loc, real_t value, Type *type); + static RealExp *create(Loc loc, real_t value, Type *type); bool equals(const RootObject * const o) const override; bool isIdentical(const Expression *e) const override; dinteger_t toInteger() override; @@ -285,7 +278,7 @@ class ComplexExp final : public Expression public: complex_t value; - static ComplexExp *create(const Loc &loc, complex_t value, Type *type); + static ComplexExp *create(Loc loc, complex_t value, Type *type); bool equals(const RootObject * const o) const override; bool isIdentical(const Expression *e) const override; dinteger_t toInteger() override; @@ -302,7 +295,7 @@ class IdentifierExp : public Expression public: Identifier *ident; - static IdentifierExp *create(const Loc &loc, Identifier *ident); + static IdentifierExp *create(Loc loc, Identifier *ident); bool isLvalue() override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -362,8 +355,8 @@ public: d_bool committed; // if type is committed d_bool hexString; // if string is parsed from a hex string literal - static StringExp *create(const Loc &loc, const char *s); - static StringExp *create(const Loc &loc, const void *s, d_size_t len); + static StringExp *create(Loc loc, const char *s); + static StringExp *create(Loc loc, const void *s, d_size_t len); bool equals(const RootObject * const o) const override; char32_t getCodeUnit(d_size_t i) const; dinteger_t getIndex(d_size_t i) const; @@ -400,7 +393,7 @@ public: */ Expressions *exps; - static TupleExp *create(const Loc &loc, Expressions *exps); + static TupleExp *create(Loc loc, Expressions *exps); TupleExp *syntaxCopy() override; bool equals(const RootObject * const o) const override; @@ -415,7 +408,7 @@ public: Expression *basis; Expressions *elements; - static ArrayLiteralExp *create(const Loc &loc, Expressions *elements); + static ArrayLiteralExp *create(Loc loc, Expressions *elements); ArrayLiteralExp *syntaxCopy() override; bool equals(const RootObject * const o) const override; Expression *getElement(d_size_t i); @@ -443,6 +436,24 @@ public: class StructLiteralExp final : public Expression { public: + uint8_t bitFields; + + // if this is true, use the StructDeclaration's init symbol + bool useStaticInit() const; + bool useStaticInit(bool v); + // used when moving instances to indicate `this is this.origin` + bool isOriginal() const; + bool isOriginal(bool v); + OwnedBy ownedByCtfe() const; + OwnedBy ownedByCtfe(OwnedBy v); + + /** anytime when recursive function is calling, 'stageflags' marks with bit flag of + * current stage and unmarks before return from this function. + * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' + * (with infinite recursion) of this expression. + */ + uint8_t stageflags; + StructDeclaration *sd; // which aggregate this is for Expressions *elements; // parallels sd->fields[] with NULL entries for fields to skip Type *stype; // final type of result (can be different from sd's type) @@ -463,18 +474,7 @@ public: StructLiteralExp *origin; - /** anytime when recursive function is calling, 'stageflags' marks with bit flag of - * current stage and unmarks before return from this function. - * 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' - * (with infinite recursion) of this expression. - */ - uint8_t stageflags; - - d_bool useStaticInit; // if this is true, use the StructDeclaration's init symbol - d_bool isOriginal; // used when moving instances to indicate `this is this.origin` - OwnedBy ownedByCtfe; - - static StructLiteralExp *create(const Loc &loc, StructDeclaration *sd, void *elements, Type *stype = NULL); + static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = nullptr); bool equals(const RootObject * const o) const override; StructLiteralExp *syntaxCopy() override; @@ -486,7 +486,6 @@ class TypeExp final : public Expression public: TypeExp *syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -497,7 +496,6 @@ public: ScopeExp *syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -509,7 +507,6 @@ public: bool isLvalue() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -522,6 +519,7 @@ public: Type *newtype; Expressions *arguments; // Array of Expression's Identifiers *names; // Array of names corresponding to expressions + Expression *placement; // if !NULL, placement expression Expression *argprefix; // expression to be evaluated just before arguments[] @@ -531,7 +529,7 @@ public: Expression *lowering; // lowered druntime hook: `_d_newclass` - static NewExp *create(const Loc &loc, Expression *thisexp, Type *newtype, Expressions *arguments); + static NewExp *create(Loc loc, Expression *placement, Expression *thisexp, Type *newtype, Expressions *arguments); NewExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -545,6 +543,7 @@ public: Expression *thisexp; // if !NULL, 'this' for class being allocated ClassDeclaration *cd; // class being instantiated Expressions *arguments; // Array of Expression's to call class constructor + Expression *placement; // if !NULL, placement expression NewAnonClassExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -578,7 +577,7 @@ class VarExp final : public SymbolExp { public: d_bool delegateWasExtracted; - static VarExp *create(const Loc &loc, Declaration *var, bool hasOverloads = true); + static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = true); bool equals(const RootObject * const o) const override; bool isLvalue() override; @@ -607,9 +606,7 @@ public: bool equals(const RootObject * const o) const override; FuncExp *syntaxCopy() override; - const char *toChars() const override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -691,9 +688,6 @@ public: Expression *e1; Expression *e2; - Type *att1; // Save alias this type to detect recursion - Type *att2; // Save alias this type to detect recursion - BinExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -746,7 +740,7 @@ public: d_bool wantsym; // do not replace Symbol with its initializer during semantic() d_bool arrow; // ImportC: if -> instead of . - static DotIdExp *create(const Loc &loc, Expression *e, Identifier *ident); + static DotIdExp *create(Loc loc, Expression *e, Identifier *ident); void accept(Visitor *v) override { v->visit(this); } }; @@ -756,7 +750,6 @@ public: TemplateDeclaration *td; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -777,7 +770,6 @@ public: DotTemplateInstanceExp *syntaxCopy() override; bool checkType() override; - bool checkValue() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -827,10 +819,10 @@ public: d_bool isUfcsRewrite; // the first argument was pushed in here by a UFCS rewrite VarDeclaration *vthis2; // container for multi-context - static CallExp *create(const Loc &loc, Expression *e, Expressions *exps); - static CallExp *create(const Loc &loc, Expression *e); - static CallExp *create(const Loc &loc, Expression *e, Expression *earg1); - static CallExp *create(const Loc &loc, FuncDeclaration *fd, Expression *earg1); + static CallExp *create(Loc loc, Expression *e, Expressions *exps); + static CallExp *create(Loc loc, Expression *e); + static CallExp *create(Loc loc, Expression *e, Expression *earg1); + static CallExp *create(Loc loc, FuncDeclaration *fd, Expression *earg1); CallExp *syntaxCopy() override; bool isLvalue() override; @@ -889,6 +881,7 @@ public: // Possible to cast to one type while painting to another type Type *to; // type to cast to unsigned char mod; // MODxxxxx + d_bool trusted; // assume cast is safe CastExp *syntaxCopy() override; bool isLvalue() override; @@ -903,7 +896,7 @@ public: unsigned dim; // number of elements in the vector OwnedBy ownedByCtfe; - static VectorExp *create(const Loc &loc, Expression *e, Type *t); + static VectorExp *create(Loc loc, Expression *e, Type *t); VectorExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -1059,7 +1052,6 @@ class LoweredAssignExp final : public AssignExp public: Expression *lowering; - const char *toChars() const override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/expressionsem.d b/gcc/d/dmd/expressionsem.d index 7ae7f40..b02f6ea 100644 --- a/gcc/d/dmd/expressionsem.d +++ b/gcc/d/dmd/expressionsem.d @@ -3,12 +3,12 @@ * * Specification: ($LINK2 https://dlang.org/spec/expression.html, Expressions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/expressionsem.d, _expressionsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/expressionsem.d, _expressionsem.d) * Documentation: https://dlang.org/phobos/dmd_expressionsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/expressionsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/expressionsem.d */ module dmd.expressionsem; @@ -25,6 +25,7 @@ import dmd.astcodegen; import dmd.astenums; import dmd.canthrow; import dmd.chkformat; +import dmd.cond; import dmd.ctorflow; import dmd.dscope; import dmd.dsymbol; @@ -33,9 +34,9 @@ import dmd.dclass; import dmd.dcast; import dmd.delegatize; import dmd.denum; +import dmd.deps; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dstruct; import dmd.dsymbolsem; @@ -59,15 +60,16 @@ import dmd.initsem; import dmd.inline; import dmd.intrange; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.mustuse; import dmd.nspace; +import dmd.nogc; import dmd.objc; import dmd.opover; import dmd.optimize; import dmd.parse; import dmd.printast; -import dmd.postordervisitor; import dmd.root.array; import dmd.root.ctfloat; import dmd.root.filename; @@ -80,6 +82,7 @@ import dmd.semantic3; import dmd.sideeffect; import dmd.safe; import dmd.target; +import dmd.templatesem : matchWithInstance; import dmd.tokens; import dmd.traits; import dmd.typesem; @@ -87,6 +90,7 @@ import dmd.typinf; import dmd.utils; import dmd.utils : arrayCastBigEndian; import dmd.visitor; +import dmd.visitor.postorder; enum LOGSEMANTIC = false; @@ -115,10 +119,9 @@ private bool isNeedThisScope(Scope* sc, Declaration d) { if (ad2 == ad) return false; - else if (ad2.isNested()) + if (ad2.isNested()) continue; - else - return true; + return true; } if (FuncDeclaration f = s.isFuncDeclaration()) { @@ -137,16 +140,26 @@ private bool isNeedThisScope(Scope* sc, Declaration d) * buf = append generated string to buffer * sc = context * exps = array of Expressions + * loc = location of the pragma / mixin where this conversion was requested, for supplemental error + * fmt = format string for supplemental error. May contain 1 `%s` which prints the faulty expression + * expandTuples = whether tuples should be expanded rather than printed as tuple syntax * Returns: * true on error */ -bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps) +bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps, + Loc loc, const(char)* fmt, bool expandTuples) { if (!exps) return false; foreach (ex; *exps) { + bool error() + { + if (loc != Loc.initial && fmt) + errorSupplemental(loc, fmt, ex.toChars()); + return true; + } if (!ex) continue; auto sc2 = sc.startCTFE(); @@ -159,15 +172,16 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps) // allowed to contain types as well as expressions auto e4 = ctfeInterpretForPragmaMsg(e3); if (!e4 || e4.op == EXP.error) - return true; + return error(); // expand tuple - if (auto te = e4.isTupleExp()) - { - if (expressionsToString(buf, sc, te.exps)) - return true; - continue; - } + if (expandTuples) + if (auto te = e4.isTupleExp()) + { + if (expressionsToString(buf, sc, te.exps, loc, fmt, true)) + return error(); + continue; + } // char literals exp `.toStringExp` return `null` but we cant override it // because in most contexts we don't want the conversion to succeed. IntegerExp ie = e4.isIntegerExp(); @@ -178,9 +192,11 @@ bool expressionsToString(ref OutBuffer buf, Scope* sc, Expressions* exps) e4 = new ArrayLiteralExp(ex.loc, tsa, ie); } - if (StringExp se = e4.toStringExp()) + StringExp se = e4.toStringExp(); + + if (se && se.type.nextOf().ty.isSomeChar) buf.writestring(se.toUTF8(sc).peekString()); - else + else if (!(se && se.len == 0)) // don't print empty array literal `[]` buf.writestring(e4.toString()); } return false; @@ -293,7 +309,7 @@ extern (D) bool findTempDecl(DotTemplateInstanceExp exp, Scope* sc) * Returns: * String literal, or `null` if error happens. */ -StringExp semanticString(Scope *sc, Expression exp, const char* s) +StringExp semanticString(Scope* sc, Expression exp, const char* s) { sc = sc.startCTFE(); exp = exp.expressionSemantic(sc); @@ -311,14 +327,11 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s) return null; } - auto se = e.toStringExp(); - if (!se) - { - error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`", - s, exp.toChars(), exp.type.toChars()); - return null; - } - return se; + if (auto se = e.toStringExp()) + return se; + error(exp.loc, "`string` expected for %s, not `(%s)` of type `%s`", + s, exp.toChars(), exp.type.toChars()); + return null; } /**************************************** @@ -326,17 +339,75 @@ StringExp semanticString(Scope *sc, Expression exp, const char* s) */ StringExp toUTF8(StringExp se, Scope* sc) { - if (se.sz != 1) + if (se.sz == 1) + return se; + // Convert to UTF-8 string + se.committed = false; + Expression e = castTo(se, sc, Type.tchar.arrayOf()); + e = e.optimize(WANTvalue); + auto result = e.isStringExp(); + assert(result); + assert(result.sz == 1); + return result; +} +/******************************** + * The type for a unary expression is incompatible. + * Print error message. + * Returns: + * ErrorExp + */ +private Expression incompatibleTypes(UnaExp e) +{ + if (e.e1.type.toBasetype() == Type.terror) + return e.e1; + + if (e.e1.op == EXP.type) { - // Convert to UTF-8 string - se.committed = false; - Expression e = castTo(se, sc, Type.tchar.arrayOf()); - e = e.optimize(WANTvalue); - auto result = e.isStringExp(); - assert(result.sz == 1); - return result; + error(e.loc, "incompatible type for `%s(%s)`: cannot use `%s` with types", EXPtoString(e.op).ptr, e.e1.toErrMsg(), EXPtoString(e.op).ptr); + } + else + { + error(e.loc, "incompatible type for `%s(%s)`: `%s`", EXPtoString(e.op).ptr, e.e1.toErrMsg(), e.e1.type.toChars()); } - return se; + return ErrorExp.get(); +} + +/******************************** + * The types for a binary expression are incompatible. + * Print error message. + * Returns: + * ErrorExp + */ +extern (D) Expression incompatibleTypes(BinExp e, Scope* sc = null) +{ + if (e.e1.type.toBasetype() == Type.terror) + return e.e1; + if (e.e2.type.toBasetype() == Type.terror) + return e.e2; + + // CondExp uses 'a ? b : c' but we're comparing 'b : c' + const(char)* thisOp = (e.op == EXP.question) ? ":" : EXPtoString(e.op).ptr; + + if (sc && suggestBinaryOverloads(e, sc)) + return ErrorExp.get(); + + if (e.e1.op == EXP.type || e.e2.op == EXP.type) + { + error(e.loc, "incompatible types for `(%s) %s (%s)`: cannot use `%s` with types", + e.e1.toErrMsg(), thisOp, e.e2.toErrMsg(), EXPtoString(e.op).ptr); + } + else if (e.e1.type.equals(e.e2.type)) + { + error(e.loc, "incompatible types for `(%s) %s (%s)`: both operands are of type `%s`", + e.e1.toErrMsg(), thisOp, e.e2.toErrMsg(), e.e1.type.toChars()); + } + else + { + auto ts = toAutoQualChars(e.e1.type, e.e2.type); + error(e.loc, "incompatible types for `(%s) %s (%s)`: `%s` and `%s`", + e.e1.toErrMsg(), thisOp, e.e2.toErrMsg(), ts[0], ts[1]); + } + return ErrorExp.get(); } private Expression reorderSettingAAElem(BinExp exp, Scope* sc) @@ -401,12 +472,12 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) // T opAssign floating yields a floating. Prevent truncating conversions (float to int). // See https://issues.dlang.org/show_bug.cgi?id=3841. - // Should we also prevent double to float (type.isfloating() && type.size() < t2.size()) ? + // Should we also prevent double to float (type.isFloating() && type.size() < t2.size()) ? if (op == EXP.addAssign || op == EXP.minAssign || op == EXP.mulAssign || op == EXP.divAssign || op == EXP.modAssign || op == EXP.powAssign) { - if ((type.isintegral() && t2.isfloating())) + if ((type.isIntegral() && t2.isFloating())) { warning(loc, "`%s %s %s` is performing truncating conversion", type.toChars(), EXPtoString(op).ptr, t2.toChars()); } @@ -418,17 +489,17 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) // Any multiplication by an imaginary or complex number yields a complex result. // r *= c, i*=c, r*=i, i*=i are all forbidden operations. const(char)* opstr = EXPtoString(op).ptr; - if (t1.isreal() && t2.iscomplex()) + if (t1.isReal() && t2.isComplex()) { error(loc, "`%s %s %s` is undefined. Did you mean `%s %s %s.re`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars()); return ErrorExp.get(); } - else if (t1.isimaginary() && t2.iscomplex()) + else if (t1.isImaginary() && t2.isComplex()) { error(loc, "`%s %s %s` is undefined. Did you mean `%s %s %s.im`?", t1.toChars(), opstr, t2.toChars(), t1.toChars(), opstr, t2.toChars()); return ErrorExp.get(); } - else if ((t1.isreal() || t1.isimaginary()) && t2.isimaginary()) + else if ((t1.isReal() || t1.isImaginary()) && t2.isImaginary()) { error(loc, "`%s %s %s` is an undefined operation", t1.toChars(), opstr, t2.toChars()); return ErrorExp.get(); @@ -440,98 +511,92 @@ private Expression checkOpAssignTypes(BinExp binExp, Scope* sc) { // Addition or subtraction of a real and an imaginary is a complex result. // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations. - if ((t1.isreal() && (t2.isimaginary() || t2.iscomplex())) || (t1.isimaginary() && (t2.isreal() || t2.iscomplex()))) + if ((t1.isReal() && (t2.isImaginary() || t2.isComplex())) || (t1.isImaginary() && (t2.isReal() || t2.isComplex()))) { error(loc, "`%s %s %s` is undefined (result is complex)", t1.toChars(), EXPtoString(op).ptr, t2.toChars()); return ErrorExp.get(); } - if (type.isreal() || type.isimaginary()) + if (type.isReal() || type.isImaginary()) { - assert(global.errors || t2.isfloating()); + assert(global.errors || t2.isFloating()); e2 = e2.castTo(sc, t1); } } - if (op == EXP.mulAssign) + if (op == EXP.mulAssign && t2.isFloating()) { - if (t2.isfloating()) + if (t1.isReal()) { - if (t1.isreal()) + if (t2.isImaginary() || t2.isComplex()) { - if (t2.isimaginary() || t2.iscomplex()) - { - e2 = e2.castTo(sc, t1); - } - } - else if (t1.isimaginary()) - { - if (t2.isimaginary() || t2.iscomplex()) - { - switch (t1.ty) - { - case Timaginary32: - t2 = Type.tfloat32; - break; - - case Timaginary64: - t2 = Type.tfloat64; - break; - - case Timaginary80: - t2 = Type.tfloat80; - break; - - default: - assert(0); - } - e2 = e2.castTo(sc, t2); - } + e2 = e2.castTo(sc, t1); } } - } - else if (op == EXP.divAssign) - { - if (t2.isimaginary()) + else if (t1.isImaginary()) { - if (t1.isreal()) - { - // x/iv = i(-x/v) - // Therefore, the result is 0 - e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1)); - e2.type = t1; - Expression e = new AssignExp(loc, e1, e2); - e.type = t1; - return e; - } - else if (t1.isimaginary()) + if (t2.isImaginary() || t2.isComplex()) { - Type t3; switch (t1.ty) { case Timaginary32: - t3 = Type.tfloat32; + t2 = Type.tfloat32; break; case Timaginary64: - t3 = Type.tfloat64; + t2 = Type.tfloat64; break; case Timaginary80: - t3 = Type.tfloat80; + t2 = Type.tfloat80; break; default: assert(0); } - e2 = e2.castTo(sc, t3); - Expression e = new AssignExp(loc, e1, e2); - e.type = t1; - return e; + e2 = e2.castTo(sc, t2); + } + } + } + else if (op == EXP.divAssign && t2.isImaginary()) + { + if (t1.isReal()) + { + // x/iv = i(-x/v) + // Therefore, the result is 0 + e2 = new CommaExp(loc, e2, new RealExp(loc, CTFloat.zero, t1)); + e2.type = t1; + Expression e = new AssignExp(loc, e1, e2); + e.type = t1; + return e; + } + else if (t1.isImaginary()) + { + Type t3; + switch (t1.ty) + { + case Timaginary32: + t3 = Type.tfloat32; + break; + + case Timaginary64: + t3 = Type.tfloat64; + break; + + case Timaginary80: + t3 = Type.tfloat80; + break; + + default: + assert(0); } + e2 = e2.castTo(sc, t3); + Expression e = new AssignExp(loc, e1, e2); + e.type = t1; + return e; } } else if (op == EXP.modAssign) { - if (t2.iscomplex()) + if (t2.isComplex()) { error(loc, "cannot perform modulo complex arithmetic"); return ErrorExp.get(); @@ -547,7 +612,7 @@ private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) // https://issues.dlang.org/show_bug.cgi?id=12585 // Extract the side effect part if ue.e1 is comma. - if ((sc.flags & SCOPE.ctfe) ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() + if (sc.ctfe ? hasSideEffect(e1) : !isTrivialExp(e1)) // match logic in extractSideEffect() { /* Even if opDollar is needed, 'e1' should be evaluate only once. So * Rewrite: @@ -602,25 +667,27 @@ TupleDeclaration isAliasThisTuple(Expression e) * Runs semantic on ae.arguments. Declares temporary variables * if '$' was used. */ -Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) +Expression resolveOpDollar(Scope* sc, ArrayExp ae, out Expression pe0) { assert(!ae.lengthVar); - *pe0 = null; AggregateDeclaration ad = isAggregate(ae.e1.type); - Dsymbol slice = search_function(ad, Id.slice); + Dsymbol slice = search_function(ad, Id.opSlice); //printf("slice = %s %s\n", slice.kind(), slice.toChars()); + Expression fallback() + { + if (ae.arguments.length == 1) + return null; + error(ae.loc, "multi-dimensional slicing requires template `opSlice`"); + return ErrorExp.get(); + } foreach (i, e; *ae.arguments) { if (i == 0) - *pe0 = extractOpDollarSideEffect(sc, ae); + pe0 = extractOpDollarSideEffect(sc, ae); if (e.op == EXP.interval && !(slice && slice.isTemplateDeclaration())) { - Lfallback: - if (ae.arguments.length == 1) - return null; - error(ae.loc, "multi-dimensional slicing requires template `opSlice`"); - return ErrorExp.get(); + return fallback(); } //printf("[%d] e = %s\n", i, e.toChars()); @@ -639,7 +706,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) // If $ was used, declare it now Expression de = new DeclarationExp(ae.loc, ae.lengthVar); de = de.expressionSemantic(sc); - *pe0 = Expression.combine(*pe0, de); + pe0 = Expression.combine(pe0, de); } sc = sc.pop(); @@ -654,22 +721,22 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) (*fargs)[0] = ie.lwr; (*fargs)[1] = ie.upr; - uint xerrors = global.startGagging(); + const xerrors = global.startGagging(); sc = sc.push(); FuncDeclaration fslice = resolveFuncCall(ae.loc, sc, slice, tiargs, ae.e1.type, ArgumentList(fargs), FuncResolveFlag.quiet); sc = sc.pop(); global.endGagging(xerrors); if (!fslice) - goto Lfallback; + return fallback(); - e = new DotTemplateInstanceExp(ae.loc, ae.e1, slice.ident, tiargs); + e = new DotTemplateInstanceExp(ae.loc, ae.e1, Id.opSlice, tiargs); e = new CallExp(ae.loc, e, fargs); e = e.expressionSemantic(sc); } if (!e.type) { - error(ae.loc, "`%s` has no value", e.toChars()); + error(ae.loc, "`%s` has no value", e.toErrMsg()); e = ErrorExp.get(); } if (e.op == EXP.error) @@ -686,7 +753,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, Expression* pe0) * Returns: * ae, or ErrorExp if errors occurred */ -Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* pe0) +Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, ref Expression pe0) { //assert(!ae.lengthVar); if (!ie) @@ -706,7 +773,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* p e = resolveProperties(sc, e); if (!e.type) { - error(ae.loc, "`%s` has no value", e.toChars()); + error(ae.loc, "`%s` has no value", e.toErrMsg()); errors = true; } return e; @@ -723,7 +790,7 @@ Expression resolveOpDollar(Scope* sc, ArrayExp ae, IntervalExp ie, Expression* p // If $ was used, declare it now Expression de = new DeclarationExp(ae.loc, ae.lengthVar); de = de.expressionSemantic(sc); - *pe0 = Expression.combine(*pe0, de); + pe0 = Expression.combine(pe0, de); } sc = sc.pop(); @@ -756,21 +823,58 @@ extern(D) bool arrayExpressionSemantic( * Params: * sc = the scope where the expression is encountered * e = the expression the needs to be moved or copied (source) - * t = if the struct defines a copy constructor, the type of the destination - * + * t = if the struct defines a copy constructor, the type of the destination (can be NULL) + * nrvo = true if the generated copy can be treated as NRVO + * move = true to allow a move constructor to be used, false to prevent infinite recursion * Returns: * The expression that copy constructs or moves the value. */ -extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) +extern (D) Expression doCopyOrMove(Scope* sc, Expression e, Type t, bool nrvo, bool move = false) { + //printf("doCopyOrMove() %s\n", toChars(e)); + StructDeclaration sd; + if (t) + { + if (auto ts = t.isTypeStruct()) + sd = ts.sym; + } + if (auto ce = e.isCondExp()) { - ce.e1 = doCopyOrMove(sc, ce.e1); - ce.e2 = doCopyOrMove(sc, ce.e2); + ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo); + ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo); + } + else if (e.isLvalue()) + { + e = callCpCtor(sc, e, t, nrvo); + } + else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp()) + { + // #move + /* Rewrite as: + * S __copyrvalue; + * __copyrvalue.moveCtor(e); + * __copyrvalue; + */ + VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null); + if (nrvo) + vd.nrvo = true; + vd.storage_class |= STC.nodtor; + vd.dsymbolSemantic(sc); + Expression de = new DeclarationExp(e.loc, vd); + Expression ve = new VarExp(e.loc, vd); + + Expression er; + er = new DotIdExp(e.loc, ve, Id.ctor); // ve.ctor + er = new CallExp(e.loc, er, e); // ve.ctor(e) + er = new CommaExp(e.loc, er, new VarExp(e.loc, vd)); // ve.ctor(e),vd + er = Expression.combine(de, er); // de,ve.ctor(e),vd + + e = er.expressionSemantic(sc); } else { - e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e); + e = valueNoDtor(e); } return e; } @@ -779,47 +883,51 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) * If e is an instance of a struct, and that struct has a copy constructor, * rewrite e as: * (tmp = e),tmp - * Input: + * Params: * sc = just used to specify the scope of created temporary variable * destinationType = the type of the object on which the copy constructor is called; * may be null if the struct defines a postblit + * nrvo = true if the generated copy can be treated as NRVO */ -private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) +private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo) { - if (auto ts = e.type.baseElemOf().isTypeStruct()) - { - StructDeclaration sd = ts.sym; - if (sd.postblit || sd.hasCopyCtor) - { - /* Create a variable tmp, and replace the argument e with: - * (tmp = e),tmp - * and let AssignExp() handle the construction. - * This is not the most efficient, ideally tmp would be constructed - * directly onto the stack. - */ - auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); - if (sd.hasCopyCtor && destinationType) - { - // https://issues.dlang.org/show_bug.cgi?id=22619 - // If the destination type is inout we can preserve it - // only if inside an inout function; if we are not inside - // an inout function, then we will preserve the type of - // the source - if (destinationType.hasWild && !(sc.func.storage_class & STC.wild)) - tmp.type = e.type; - else - tmp.type = destinationType; - } - tmp.storage_class |= STC.nodtor; - tmp.dsymbolSemantic(sc); - Expression de = new DeclarationExp(e.loc, tmp); - Expression ve = new VarExp(e.loc, tmp); - de.type = Type.tvoid; - ve.type = e.type; - return Expression.combine(de, ve); - } - } - return e; + //printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType)); + auto ts = e.type.baseElemOf().isTypeStruct(); + + if (!ts) + return e; + StructDeclaration sd = ts.sym; + if (!sd.postblit && !sd.hasCopyCtor) + return e; + + /* Create a variable tmp, and replace the argument e with: + * (tmp = e),tmp + * and let AssignExp() handle the construction. + * This is not the most efficient, ideally tmp would be constructed + * directly onto the stack. + */ + VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e); + if (nrvo) + tmp.nrvo = true; + if (sd.hasCopyCtor && destinationType) + { + // https://issues.dlang.org/show_bug.cgi?id=22619 + // If the destination type is inout we can preserve it + // only if inside an inout function; if we are not inside + // an inout function, then we will preserve the type of + // the source + if (destinationType.hasWild && !(sc.func.storage_class & STC.wild)) + tmp.type = e.type; + else + tmp.type = destinationType; + } + tmp.storage_class |= STC.nodtor; + tmp.dsymbolSemantic(sc); + Expression de = new DeclarationExp(e.loc, tmp); + Expression ve = new VarExp(e.loc, tmp); + de.type = Type.tvoid; + ve.type = e.type; + return Expression.combine(de, ve); } /************************************************ @@ -828,6 +936,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) */ Expression valueNoDtor(Expression e) { + //printf("valueNoDtor() %s\n", toChars(e)); auto ex = lastComma(e); if (auto ce = ex.isCallExp()) @@ -948,7 +1057,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) SearchOptFlags flags = SearchOpt.all; Dsymbol s; - if (sc.flags & SCOPE.ignoresymbolvisibility) + if (sc.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; // First look in local scopes @@ -965,8 +1074,7 @@ private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) FuncDeclaration f = s.isFuncDeclaration(); if (f) { - TemplateDeclaration td = getFuncTemplateDecl(f); - if (td) + if (TemplateDeclaration td = getFuncTemplateDecl(f)) { if (td.overroot) td = td.overroot; @@ -1033,41 +1141,41 @@ private void hookDtors(CondExp ce, Scope* sc) override void visit(DeclarationExp e) { auto v = e.declaration.isVarDeclaration(); - if (v && !v.isDataseg()) + if (!v || v.isDataseg()) + return; + + if (v._init) { - if (v._init) - { - if (auto ei = v._init.isExpInitializer()) - walkPostorder(ei.exp, this); - } + if (auto ei = v._init.isExpInitializer()) + walkPostorder(ei.exp, this); + } - if (v.edtor) - walkPostorder(v.edtor, this); + if (v.edtor) + walkPostorder(v.edtor, this); - if (v.needsScopeDtor()) - { - if (!vcond) - { - vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd); - vcond.dsymbolSemantic(sc); + if (!v.needsScopeDtor()) + return; - Expression de = new DeclarationExp(ce.econd.loc, vcond); - de = de.expressionSemantic(sc); + if (!vcond) + { + vcond = copyToTemp(STC.volatile_ | STC.const_, "__cond", ce.econd); + vcond.dsymbolSemantic(sc); - Expression ve = new VarExp(ce.econd.loc, vcond); - ce.econd = Expression.combine(de, ve); - } + Expression de = new DeclarationExp(ce.econd.loc, vcond); + de = de.expressionSemantic(sc); - //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); - Expression ve = new VarExp(vcond.loc, vcond); - if (isThen) - v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor); - else - v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor); - v.edtor = v.edtor.expressionSemantic(sc); - //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); - } + Expression ve = new VarExp(ce.econd.loc, vcond); + ce.econd = Expression.combine(de, ve); } + + //printf("\t++v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); + Expression ve = new VarExp(vcond.loc, vcond); + if (isThen) + v.edtor = new LogicalExp(v.edtor.loc, EXP.andAnd, ve, v.edtor); + else + v.edtor = new LogicalExp(v.edtor.loc, EXP.orOr, ve, v.edtor); + v.edtor = v.edtor.expressionSemantic(sc); + //printf("\t--v = %s, v.edtor = %s\n", v.toChars(), v.edtor.toChars()); } } @@ -1103,7 +1211,7 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) eleft = die.e1; Type t = eleft.type.toBasetype(); - if (t.ty == Tarray || t.ty == Tsarray || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid)) + if (t.isStaticOrDynamicArray() || t.ty == Tnull || (t.isTypeBasic() && t.ty != Tvoid)) { /* Built-in types and arrays have no callable properties, so do shortcut. * It is necessary in: e.init() @@ -1143,6 +1251,9 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) } else { + if (arrayExpressionSemantic(ce.arguments.peekSlice(), sc)) + return ErrorExp.get(); + if (Expression ey = die.dotIdSemanticProp(sc, 1)) { if (ey.op == EXP.error) @@ -1150,19 +1261,11 @@ private Expression resolveUFCS(Scope* sc, CallExp ce) ce.e1 = ey; if (isDotOpDispatch(ey)) { - // even opDispatch and UFCS must have valid arguments, - // so now that we've seen indication of a problem, - // check them for issues. - Expressions* originalArguments = Expression.arraySyntaxCopy(ce.arguments); - - uint errors = global.startGagging(); + const errors = global.startGagging(); e = ce.expressionSemantic(sc); if (!global.endGagging(errors)) return e; - if (arrayExpressionSemantic(originalArguments.peekSlice(), sc)) - return ErrorExp.get(); - /* fall down to UFCS */ } else @@ -1349,11 +1452,6 @@ private Expression resolveUFCSProperties(Scope* sc, Expression e1, Expression e2 auto arguments = new Expressions(1); (*arguments)[0] = eleft; e = new CallExp(loc, e, arguments); - - // https://issues.dlang.org/show_bug.cgi?id=24017 - if (sc.flags & SCOPE.debug_) - e.isCallExp().inDebugStatement = true; - e = e.expressionSemantic(sc); return e; } @@ -1375,12 +1473,12 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) auto td = s.isTemplateDeclaration(); if (fd) { - if (fd.type.isTypeFunction().isproperty) + if (fd.type.isTypeFunction().isProperty) return resolveProperties(sc, e1); } else if (td && td.onemember && (fd = td.onemember.isFuncDeclaration()) !is null) { - if (fd.type.isTypeFunction().isproperty || + if (fd.type.isTypeFunction().isProperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) return resolveProperties(sc, e1); @@ -1396,7 +1494,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) { if (auto fd = td.onemember.isFuncDeclaration()) { - if (fd.type.isTypeFunction().isproperty || + if (fd.type.isTypeFunction().isProperty || (fd.storage_class2 & STC.property) || (td._scope.stc & STC.property)) return resolveProperties(sc, e1); @@ -1408,7 +1506,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) Expression handleFuncDecl(FuncDeclaration fd) { assert(fd); - if (fd.type.isTypeFunction().isproperty) + if (fd.type.isTypeFunction().isProperty) return resolveProperties(sc, e1); return e1; } @@ -1420,7 +1518,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) } else if (auto oe = e1.isOverExp()) return handleOverloadSet(oe.vars); - else if (auto dti = e1.isDotTemplateInstanceExp()) + if (auto dti = e1.isDotTemplateInstanceExp()) { if (dti.ti.tempdecl) if (auto td = dti.ti.tempdecl.isTemplateDeclaration()) @@ -1428,7 +1526,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) } else if (auto dte = e1.isDotTemplateExp()) return handleTemplateDecl(dte.td); - else if (auto se = e1.isScopeExp()) + if (auto se = e1.isScopeExp()) { Dsymbol s = se.sds; TemplateInstance ti = s.isTemplateInstance(); @@ -1438,7 +1536,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) } else if (auto et = e1.isTemplateExp()) return handleTemplateDecl(et.td); - else if (e1.isDotVarExp() && e1.type.isTypeFunction()) + if (e1.isDotVarExp() && e1.type.isTypeFunction()) { DotVarExp dve = e1.isDotVarExp(); return handleFuncDecl(dve.var.isFuncDeclaration()); @@ -1461,7 +1559,7 @@ Expression resolvePropertiesOnly(Scope* sc, Expression e1) * Returns: * `s` turned into an expression, `ErrorExp` if an error occurred */ -Expression symbolToExp(Dsymbol s, const ref Loc loc, Scope *sc, bool hasOverloads) +Expression symbolToExp(Dsymbol s, Loc loc, Scope* sc, bool hasOverloads) { static if (LOGSEMANTIC) { @@ -1506,8 +1604,8 @@ Lagain: { if (sd.isSystem()) { - if (sc.setUnsafePreview(global.params.systemVariables, false, loc, - "cannot access `@system` variable `%s` in @safe code", sd)) + if (sc.setUnsafePreview(sc.previews.systemVariables, false, loc, + "access `@system` variable `%s`", sd)) { if (auto v = sd.isVarDeclaration()) { @@ -1690,7 +1788,7 @@ Lagain: * Returns: * Expression representing the `this` for the var */ -private Expression getRightThis(const ref Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0) +private Expression getRightThis(Loc loc, Scope* sc, AggregateDeclaration ad, Expression e1, Dsymbol var, int flag = 0) { //printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars()); L1: @@ -1857,7 +1955,7 @@ private bool haveSameThis(FuncDeclaration outerFunc, FuncDeclaration calledFunc) * we can only call other pure functions. * Returns true if error occurs. */ -private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) +private bool checkPurity(FuncDeclaration f, Loc loc, Scope* sc) { if (!sc.func) return false; @@ -1865,7 +1963,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; // If the call has a pure parent, then the called func must be pure. @@ -1876,7 +1974,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) f.toPrettyChars()); if (!f.isDtorDeclaration()) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_); + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.pure_, global.errorSink); f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().purity != PURE.impure, "impure"); return true; @@ -1898,7 +1996,7 @@ private bool checkPurity(FuncDeclaration f, const ref Loc loc, Scope* sc) * check = current check (e.g. whether it's pure) * checkName = the kind of check (e.g. `"pure"`) */ -void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, +void checkOverriddenDtor(FuncDeclaration f, Scope* sc, Loc loc, scope bool function(DtorDeclaration) check, const string checkName) { auto dd = f.isDtorDeclaration(); @@ -1959,12 +2057,67 @@ void checkOverriddenDtor(FuncDeclaration f, Scope* sc, const ref Loc loc, } } +/******************************************** + * Print the reason why `fd` was inferred `@system` as a supplemental error + * Params: + * fd = function to check + * maxDepth = up to how many functions deep to report errors + * deprecation = print deprecations instead of errors + * stc = storage class of attribute to check + * eSink = where the error messages go + */ +public void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc, ErrorSink eSink) +{ + auto errorFunc = deprecation ? &eSink.deprecationSupplemental : &eSink.errorSupplemental; + + AttributeViolation* s; + string attr; + if (stc & STC.safe) + { + s = fd.safetyViolation; + attr = "@safe"; + } + else if (stc & STC.pure_) + { + s = fd.pureViolation; + attr = "pure"; + } + else if (stc & STC.nothrow_) + { + s = fd.nothrowViolation; + attr = "nothrow"; + } + else if (stc & STC.nogc) + { + s = fd.nogcViolation; + attr = "@nogc"; + } + + if (!s) + return; + + if (s.action.length > 0) + { + errorFunc(s.loc, "and %.*s makes it fail to infer `%.*s`", s.action.fTuple.expand, attr.fTuple.expand); + } + else if (s.fd) + { + if (maxDepth > 0) + { + errorFunc(s.loc, "which calls `%s`", s.fd.toErrMsg()); + errorSupplementalInferredAttr(s.fd, maxDepth - 1, deprecation, stc, eSink); + } + } + else + assert(0); +} + /******************************************* * Accessing variable v. * Check for purity and safety violations. * Returns true if error occurs. */ -private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) +private bool checkPurity(VarDeclaration v, Loc loc, Scope* sc) { //printf("v = %s %s\n", v.type.toChars(), v.toChars()); /* Look for purity and safety violations when accessing variable v @@ -1974,7 +2127,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; // allow violations inside typeof(expression) - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; // allow violations inside compile-time evaluated expressions and debug conditionals if (v.ident == Id.ctfe) return false; // magic variable never violates pure and safe @@ -2009,7 +2162,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) if (v.ident == Id.gate) return false; - if (checkImpure(sc, loc, "`pure` %s `%s` cannot access mutable static data `%s`", v)) + if (checkImpure(sc, loc, "accessing mutable static data `%s`", v)) { error(loc, "`pure` %s `%s` cannot access mutable static data `%s`", sc.func.kind(), sc.func.toPrettyChars(), v.toChars()); @@ -2047,23 +2200,21 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) FuncDeclaration ff = s.isFuncDeclaration(); if (!ff) break; - if (ff.isNested() || ff.isThis()) - { - if (ff.type.isImmutable() || - ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) - { - OutBuffer ffbuf; - OutBuffer vbuf; - MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); - MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); - error(loc, "%s%s `%s` cannot access %sdata `%s`", - ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); - err = true; - break; - } - continue; + if (!ff.isNested() && !ff.isThis()) + break; + if (ff.type.isImmutable() || + ff.type.isShared() && !MODimplicitConv(ff.type.mod, v.type.mod)) + { + OutBuffer ffbuf; + OutBuffer vbuf; + MODMatchToBuffer(&ffbuf, ff.type.mod, v.type.mod); + MODMatchToBuffer(&vbuf, v.type.mod, ff.type.mod); + error(loc, "%s%s `%s` cannot access %sdata `%s`", + ffbuf.peekChars(), ff.kind(), ff.toPrettyChars(), vbuf.peekChars(), v.toChars()); + err = true; + break; } - break; + continue; } } @@ -2071,8 +2222,7 @@ private bool checkPurity(VarDeclaration v, const ref Loc loc, Scope* sc) */ if (v.storage_class & STC.gshared) { - if (sc.setUnsafe(false, loc, - "`@safe` function `%s` cannot access `__gshared` data `%s`", sc.func, v)) + if (sc.setUnsafe(false, loc, "accessing `__gshared` data `%s`", v)) { err = true; } @@ -2104,9 +2254,9 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & SCOPE.debug_) + if (sc.debug_) return false; - if ((sc.flags & SCOPE.ctfe) && sc.func) + if (sc.ctfe && sc.func) return false; if (!sc.func) @@ -2140,7 +2290,7 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) sc.func.kind(), sc.func.toPrettyChars(), f.kind(), prettyChars); if (!f.isDtorDeclaration) - errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe); + errorSupplementalInferredAttr(f, /*max depth*/ 10, /*deprecation*/ false, STC.safe, global.errorSink); .errorSupplemental(f.loc, "`%s` is declared here", prettyChars); f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().trust > TRUST.system, "@system"); @@ -2154,12 +2304,12 @@ private bool checkSafety(FuncDeclaration f, ref Loc loc, Scope* sc) if (sc.func.isSafeBypassingInference()) { .deprecation(loc, "`@safe` function `%s` calling `%s`", sc.func.toChars(), f.toChars()); - errorSupplementalInferredAttr(f, 10, true, STC.safe); + errorSupplementalInferredAttr(f, 10, true, STC.safe, global.errorSink); } else if (!sc.func.safetyViolation) { import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, null, f, null, null); + sc.func.safetyViolation = new AttributeViolation(loc, f); } } return false; @@ -2179,7 +2329,7 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) return false; if (sc.intypeof == 1) return false; - if (sc.flags & (SCOPE.ctfe | SCOPE.debug_)) + if (sc.ctfe || sc.debug_) return false; /* The original expressions (`new S(...)` or `new S[...]``) will be * verified instead. This is to keep errors related to the original code @@ -2188,70 +2338,76 @@ private bool checkNogc(FuncDeclaration f, ref Loc loc, Scope* sc) if (f.ident == Id._d_newitemT || f.ident == Id._d_newarrayT || f.ident == Id._d_newarraymTX) return false; - if (!f.isNogc()) - { - if (isRootTraitsCompilesScope(sc) ? sc.func.isNogcBypassingInference() : sc.func.setGCCall(f)) - { - if (loc.linnum == 0) // e.g. implicitly generated dtor - loc = sc.func.loc; + if (f.isNogc()) + return false; - // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), - // so don't print anything to avoid double error messages. - if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT - || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX - || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) - { - error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); + if (isRootTraitsCompilesScope(sc) ? !sc.func.isNogcBypassingInference() : !sc.func.setGCCall(f)) + return false; - if (!f.isDtorDeclaration) - f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc); - } + if (loc == Loc.initial) // e.g. implicitly generated dtor + loc = sc.func.loc; - f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isnogc, "non-@nogc"); + // Lowered non-@nogc'd hooks will print their own error message inside of nogc.d (NOGCVisitor.visit(CallExp e)), + // so don't print anything to avoid double error messages. + if (!(f.ident == Id._d_HookTraceImpl || f.ident == Id._d_arraysetlengthT + || f.ident == Id._d_arrayappendT || f.ident == Id._d_arrayappendcTX + || f.ident == Id._d_arraycatnTX || f.ident == Id._d_newclassT)) + { + error(loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", + sc.func.kind(), sc.func.toPrettyChars(), f.kind(), f.toPrettyChars()); - return true; - } + if (!f.isDtorDeclaration) + f.errorSupplementalInferredAttr(/*max depth*/ 10, /*deprecation*/ false, STC.nogc, global.errorSink); } - return false; + + f.checkOverriddenDtor(sc, loc, dd => dd.type.toTypeFunction().isNogc, "non-@nogc"); + + return true; } /******************************************** - * Check that the postblit is callable if t is an array of structs. - * Returns true if error happens. + * Check that the postblit of `t` isn't @disabled and has the right + * function attributes for this scope. + * + * Params: + * t = struct type, or static array of struct type to check + * loc = error message location + * sc = scope in which attributes are checked + * Returns: true if there's an error */ private bool checkPostblit(Type t, ref Loc loc, Scope* sc) { - if (auto ts = t.baseElemOf().isTypeStruct()) + auto ts = t.baseElemOf().isTypeStruct(); + if (!ts) + return false; + + if (global.params.useTypeInfo && Type.dtypeinfo) { - if (global.params.useTypeInfo && Type.dtypeinfo) - { - // https://issues.dlang.org/show_bug.cgi?id=11395 - // Require TypeInfo generation for array concatenation - semanticTypeInfo(sc, t); - } + // https://issues.dlang.org/show_bug.cgi?id=11395 + // Require TypeInfo generation for array concatenation + semanticTypeInfo(sc, t); + } - StructDeclaration sd = ts.sym; - if (sd.postblit) - { - if (sd.postblit.checkDisabled(loc, sc)) - return true; + StructDeclaration sd = ts.sym; + if (!sd.postblit) + return false; - //checkDeprecated(sc, sd.postblit); // necessary? - sd.postblit.checkPurity(loc, sc); - sd.postblit.checkSafety(loc, sc); - sd.postblit.checkNogc(loc, sc); - //checkAccess(sd, loc, sc, sd.postblit); // necessary? - return false; - } - } - return false; + if (sd.postblit.checkDisabled(loc, sc)) + return true; + + //checkDeprecated(sc, sd.postblit); // necessary? + //checkAccess(sd, loc, sc, sd.postblit); // necessary? + bool result = false; + result |= sd.postblit.checkPurity(loc, sc); + result |= sd.postblit.checkSafety(loc, sc); + result |= sd.postblit.checkNogc(loc, sc); + return result; } /*************************************** * Pull out any properties. */ -private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, BinExp saveAtts = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null, Type[2]* aliasThisStop = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", EXPtoString(e1.op).ptr, e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -2304,37 +2460,33 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return e.expressionSemantic(sc); } } + for (size_t i = 0; i < os.a.length; i++) { - for (size_t i = 0; i < os.a.length; i++) + FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); + if (!f) + continue; + if (f.errors) + return ErrorExp.get(); + fd = f; + assert(fd.type.ty == Tfunction); + auto tf = fd.type.isTypeFunction(); + if (!tf.isRef && e2) { - if (FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet)) - { - if (f.errors) - return ErrorExp.get(); - fd = f; - assert(fd.type.ty == Tfunction); - auto tf = fd.type.isTypeFunction(); - if (!tf.isref && e2) - { - error(loc, "%s is not an lvalue", e1.toChars()); - return ErrorExp.get(); - } - } + error(loc, "%s is not an lvalue", e1.toErrMsg()); + return ErrorExp.get(); } - if (fd) + } + if (fd) + { + Expression e = new CallExp(loc, e1); + if (e2) { - Expression e = new CallExp(loc, e1); - if (e2) - { - e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } - } + e = new AssignExp(loc, e, e2); + if (aliasThisStop) + return e.expressionSemantic(sc, *aliasThisStop); return e.expressionSemantic(sc); } + return e.expressionSemantic(sc); } if (e2) goto Leprop; @@ -2391,7 +2543,7 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = tthis = dve.e1.type; goto Lfd; } - else if (sc && sc.flags & SCOPE.Cfile && e1.isVarExp() && !e2) + else if (sc && sc.inCfile && e1.isVarExp() && !e2) { // ImportC: do not implicitly call function if no ( ) are present } @@ -2422,33 +2574,28 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = return e.expressionSemantic(sc); } } + FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); + if (fd && fd.type) { - FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, ArgumentList(), FuncResolveFlag.quiet); - if (fd && fd.type) + if (fd.errors) + return ErrorExp.get(); + TypeFunction tf = fd.type.isTypeFunction(); + if (!e2 || tf.isRef) { - if (fd.errors) - return ErrorExp.get(); - TypeFunction tf = fd.type.isTypeFunction(); - if (!e2 || tf.isref) + Expression e = new CallExp(loc, e1); + if (e2) { - Expression e = new CallExp(loc, e1); - if (e2) - { - e = new AssignExp(loc, e, e2); - if (saveAtts) - { - (cast(BinExp)e).att1 = saveAtts.att1; - (cast(BinExp)e).att2 = saveAtts.att2; - } - } - return e.expressionSemantic(sc); + e = new AssignExp(loc, e, e2); + if (aliasThisStop) + return e.expressionSemantic(sc, *aliasThisStop); } + return e.expressionSemantic(sc); } } - if (FuncDeclaration fd = s.isFuncDeclaration()) + if (FuncDeclaration fd2 = s.isFuncDeclaration()) { // Keep better diagnostic message for invalid property usage of functions - assert(fd.type.ty == Tfunction); + assert(fd2.type.ty == Tfunction); Expression e = new CallExp(loc, e1, e2); return e.expressionSemantic(sc); } @@ -2493,13 +2640,13 @@ private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = if (!e1.type) { - error(loc, "cannot resolve type for %s", e1.toChars()); + error(loc, "cannot resolve type for %s", e1.toErrMsg()); e1 = ErrorExp.get(); } return e1; Leprop: - error(loc, "not a property %s", e1.toChars()); + error(loc, "not a property %s", e1.toErrMsg()); return ErrorExp.get(); } @@ -2507,20 +2654,19 @@ private bool checkRightThis(Expression e, Scope* sc) { if (e.op == EXP.error) return true; - if (e.op == EXP.variable && e.type.ty != Terror) - { - VarExp ve = cast(VarExp)e; - if (isNeedThisScope(sc, ve.var)) - { - //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n", - // sc.intypeof, sc.getStructClassScope(), func, fdthis); - auto t = ve.var.isThis(); - assert(t); - error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars()); - return true; - } - } - return false; + if (e.op != EXP.variable || e.type.ty == Terror) + return false; + + VarExp ve = cast(VarExp)e; + if (!isNeedThisScope(sc, ve.var)) + return false; + + //printf("checkRightThis sc.intypeof = %d, ad = %p, func = %p, fdthis = %p\n", + // sc.intypeof, sc.getStructClassScope(), func, fdthis); + auto t = ve.var.isThis(); + assert(t); + error(e.loc, "accessing non-static variable `%s` requires an instance of `%s`", ve.var.toChars(), t.toChars()); + return true; } Expression resolveProperties(Scope* sc, Expression e) @@ -2564,7 +2710,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) e = resolveProperties(sc, e); if (!e.type) { - error(e.loc, "`%s` has no value", e.toChars()); + error(e.loc, "`%s` has no value", e.toErrMsg()); t0 = Type.terror; continue; } @@ -2587,7 +2733,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) continue; } - e = doCopyOrMove(sc, e); + e = doCopyOrMove(sc, e, null, false); if (!foundType && t0 && !t0.equals(e.type)) { @@ -2643,7 +2789,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps) return t0; } -private Expression opAssignToOp(const ref Loc loc, EXP op, Expression e1, Expression e2) @safe +private Expression opAssignToOp(Loc loc, EXP op, Expression e1, Expression e2) @safe { Expression e; switch (op) @@ -2733,56 +2879,57 @@ private Expression rewriteOpAssign(BinExp exp) * Params: * sc = scope * argumentList = arguments to function - * reportErrors = whether or not to report errors here. Some callers are not + * eSink = if not null, used to report errors. Some callers are not * checking actual function params, so they'll do their own error reporting * Returns: * `true` when a semantic error occurred */ -private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, const bool reportErrors = true) +private bool preFunctionParameters(Scope* sc, ArgumentList argumentList, ErrorSink eSink) { Expressions* exps = argumentList.arguments; + if (!exps) + return false; + + expandTuples(exps, argumentList.names); + bool err = false; - if (exps) + for (size_t i = 0; i < exps.length; i++) { - expandTuples(exps, argumentList.names); - - for (size_t i = 0; i < exps.length; i++) + Expression arg = (*exps)[i]; + arg = resolveProperties(sc, arg); + arg = arg.arrayFuncConv(sc); + if (arg.op == EXP.type) { - Expression arg = (*exps)[i]; - arg = resolveProperties(sc, arg); - arg = arg.arrayFuncConv(sc); - if (arg.op == EXP.type) - { - // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 - arg = resolveAliasThis(sc, arg); + // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 + arg = resolveAliasThis(sc, arg); - if (arg.op == EXP.type) - { - if (reportErrors) - { - error(arg.loc, "cannot pass type `%s` as a function argument", arg.toChars()); - arg = ErrorExp.get(); - } - err = true; - } - } - else if (arg.type.toBasetype().ty == Tfunction) + if (arg.op == EXP.type) { - if (reportErrors) + if (eSink) { - error(arg.loc, "cannot pass function `%s` as a function argument", arg.toChars()); + eSink.error(arg.loc, "cannot pass type `%s` as a function argument", arg.toErrMsg()); arg = ErrorExp.get(); } err = true; } - else if (checkNonAssignmentArrayOp(arg)) + } + else if (arg.type.toBasetype().ty == Tfunction) + { + if (eSink) { + eSink.error(arg.loc, "cannot pass function `%s` as a function argument", arg.toErrMsg()); arg = ErrorExp.get(); - err = true; } - (*exps)[i] = arg; + err = true; } + else if (checkNonAssignmentArrayOp(arg)) + { + arg = ErrorExp.get(); + err = true; + } + (*exps)[i] = arg; } + return err; } @@ -2827,12 +2974,12 @@ private bool checkDefCtor(Loc loc, Type t) * Returns: * true errors happened */ -private bool functionParameters(const ref Loc loc, Scope* sc, +private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Expression ethis, Type tthis, ArgumentList argumentList, FuncDeclaration fd, Type* prettype, Expression* peprefix) { Expressions* arguments = argumentList.arguments; - //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); + //printf("functionParameters() fd: %s tf: %s\n", fd ? fd.ident.toChars() : "", toChars(tf)); assert(arguments); assert(fd || tf.next); const size_t nparams = tf.parameterList.length; @@ -2843,14 +2990,14 @@ private bool functionParameters(const ref Loc loc, Scope* sc, if (argumentList.names) { - const(char)* msg = null; - auto resolvedArgs = tf.resolveNamedArgs(argumentList, &msg); + OutBuffer buf; + auto resolvedArgs = tf.resolveNamedArgs(argumentList, &buf); if (!resolvedArgs) { // while errors are usually already caught by `tf.callMatch`, // this can happen when calling `typeof(freefunc)` - if (msg) - error(loc, "%s", msg); + if (buf.length) + error(loc, "%s", buf.peekChars()); return true; } // note: the argument list should be mutated with named arguments / default arguments, @@ -2907,149 +3054,143 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { Expression arg = (i < nargs) ? (*arguments)[i] : null; - if (i < nparams) + if (i >= nparams) + break; + + bool errorArgs() { - bool errorArgs() - { - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; - } + error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); + return true; + } - Parameter p = tf.parameterList[i]; + Parameter p = tf.parameterList[i]; - if (!arg) + if (!arg) + { + if (!p.defaultArg) { - if (!p.defaultArg) - { - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) - goto L2; - return errorArgs(); - } - arg = p.defaultArg; - if (!arg.type) - arg = arg.expressionSemantic(sc); - arg = inlineCopy(arg, sc); - // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ - arg = arg.resolveLoc(loc, sc); - if (i >= nargs) - { - arguments.push(arg); - nargs++; - } - else - (*arguments)[i] = arg; + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) + goto L2; + return errorArgs(); } - else + arg = p.defaultArg; + if (!arg.type) + arg = arg.expressionSemantic(sc); + arg = inlineCopy(arg, sc); + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg.resolveLoc(loc, sc); + if (i >= nargs) { - if (arg.isDefaultInitExp()) - { - arg = arg.resolveLoc(loc, sc); - (*arguments)[i] = arg; - } + arguments.push(arg); + nargs++; } + else + (*arguments)[i] = arg; + } + else if (arg.isDefaultInitExp()) + { + arg = arg.resolveLoc(loc, sc); + (*arguments)[i] = arg; + } - - if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic + if (tf.parameterList.varargs == VarArg.typesafe && i + 1 == nparams) // https://dlang.org/spec/function.html#variadic + { + //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); + if (MATCH m = arg.implicitConvTo(p.type)) { - //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); - { - MATCH m; - if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) - { - if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) - goto L2; - else if (nargs != nparams) - return errorArgs(); - goto L1; - } - } - L2: - Type tb = p.type.toBasetype(); - switch (tb.ty) + if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) + goto L2; + else if (nargs != nparams) + return errorArgs(); + goto L1; + } + L2: + Type tb = p.type.toBasetype(); + switch (tb.ty) + { + case Tsarray: + case Tarray: { - case Tsarray: - case Tarray: - { - /* Create a static array variable v of type arg.type: - * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; - * - * The array literal in the initializer of the hidden variable - * is now optimized. - * https://issues.dlang.org/show_bug.cgi?id=2356 - */ - Type tbn = (cast(TypeArray)tb).next; // array element type - Type tret = p.isLazyArray(); + /* Create a static array variable v of type arg.type: + * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; + * + * The array literal in the initializer of the hidden variable + * is now optimized. + * https://issues.dlang.org/show_bug.cgi?id=2356 + */ + Type tbn = (cast(TypeArray)tb).next; // array element type + Type tret = p.isLazyArray(); - auto elements = new Expressions(nargs - i); - foreach (u; 0 .. elements.length) - { - Expression a = (*arguments)[i + u]; - assert(a); - if (tret && a.implicitConvTo(tret)) - { - // p is a lazy array of delegates, tret is return type of the delegates - a = a.implicitCastTo(sc, tret) - .optimize(WANTvalue) - .toDelegate(tret, sc); - } - else - a = a.implicitCastTo(sc, tbn); - a = a.addDtorHook(sc); - (*elements)[u] = a; - } - // https://issues.dlang.org/show_bug.cgi?id=14395 - // Convert to a static array literal, or its slice. - arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); - if (tb.ty == Tarray) + auto elements = new Expressions(nargs - i); + foreach (u; 0 .. elements.length) + { + Expression a = (*arguments)[i + u]; + assert(a); + if (tret && a.implicitConvTo(tret)) { - arg = new SliceExp(loc, arg, null, null); - arg.type = p.type; + // p is a lazy array of delegates, tret is return type of the delegates + a = a.implicitCastTo(sc, tret) + .optimize(WANTvalue) + .toDelegate(tret, sc); } - break; - } - case Tclass: - { - /* Set arg to be: - * new Tclass(arg0, arg1, ..., argn) - */ - auto args = new Expressions(nargs - i); - foreach (u; i .. nargs) - (*args)[u - i] = (*arguments)[u]; - arg = new NewExp(loc, null, p.type, args); - break; + else + a = a.implicitCastTo(sc, tbn); + a = a.addDtorHook(sc); + (*elements)[u] = a; } - default: - if (!arg) + // https://issues.dlang.org/show_bug.cgi?id=14395 + // Convert to a static array literal, or its slice. + arg = new ArrayLiteralExp(loc, tbn.sarrayOf(nargs - i), elements); + if (tb.ty == Tarray) { - error(loc, "not enough arguments"); - return true; + arg = new SliceExp(loc, arg, null, null); + arg.type = p.type; } break; } - arg = arg.expressionSemantic(sc); - //printf("\targ = '%s'\n", arg.toChars()); - arguments.setDim(i + 1); - (*arguments)[i] = arg; - nargs = i + 1; - done = true; + case Tclass: + { + /* Set arg to be: + * new Tclass(arg0, arg1, ..., argn) + */ + auto args = new Expressions(nargs - i); + foreach (u; i .. nargs) + (*args)[u - i] = (*arguments)[u]; + arg = new NewExp(loc, null, null, p.type, args); + break; + } + default: + if (!arg) + { + error(loc, "not enough arguments"); + return true; + } + break; } + arg = arg.expressionSemantic(sc); + //printf("\targ = '%s'\n", arg.toChars()); + arguments.setDim(i + 1); + (*arguments)[i] = arg; + nargs = i + 1; + done = true; + } - L1: - if (!(p.isLazy() && p.type.ty == Tvoid)) + L1: + if (!(p.isLazy() && p.type.ty == Tvoid)) + { + if (ubyte wm = arg.type.deduceWild(p.type, p.isReference())) { - if (ubyte wm = arg.type.deduceWild(p.type, p.isReference())) - { - wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; - //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); - } + wildmatch = wildmatch ? MODmerge(wildmatch, wm) : wm; + //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); } } + if (done) break; } if ((wildmatch == MODFlags.mutable || wildmatch == MODFlags.immutable_) && tf.next && tf.next.hasWild() && - (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf()))) + (tf.isRef || !tf.next.implicitConvTo(tf.next.immutableOf()))) { bool errorInout(MOD wildmatch) { @@ -3172,13 +3313,13 @@ private bool functionParameters(const ref Loc loc, Scope* sc, } else if (p.storageClass & STC.ref_) { - if (global.params.rvalueRefParam == FeatureState.enabled && + if (sc.previews.rvalueRefParam && !arg.isLvalue() && targ.isCopyable()) { /* allow rvalues to be passed to ref parameters by copying * them to a temp, then pass the temp as the argument */ - auto v = copyToTemp(0, "__rvalue", arg); + auto v = copyToTemp(STC.none, "__rvalue", arg); Expression ev = new DeclarationExp(arg.loc, v); ev = new CommaExp(arg.loc, ev, new VarExp(arg.loc, v)); arg = ev.expressionSemantic(sc); @@ -3222,7 +3363,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Argument value can be assigned to firstArg. * Check arg to see if it matters. */ - err |= checkParamArgumentReturn(sc, firstArg, arg, p, false); + err |= checkParamArgumentReturn(*sc, firstArg, arg, p, false); } // Allow 'lazy' to imply 'scope' - lazy parameters can be passed along // as lazy parameters to the next function, but that isn't escaping. @@ -3236,7 +3377,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, * Check arg to see if it matters. */ VarDeclaration vPar = fd ? (fd.parameters ? (*fd.parameters)[i] : null) : null; - err |= checkParamArgumentEscape(sc, fd, p.ident, vPar, cast(STC) pStc, arg, false, false); + err |= checkParamArgumentEscape(*sc, fd, p.ident, vPar, cast(STC) pStc, arg, false, false); } // Turning heap allocations into stack allocations is dangerous without dip1000, since `scope` inference @@ -3260,7 +3401,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { // allocate the array literal as temporary static array on the stack ale.type = ale.type.nextOf().sarrayOf(ale.elements.length); - auto tmp = copyToTemp(0, "__arrayliteral_on_stack", ale); + auto tmp = copyToTemp(STC.none, "__arrayliteral_on_stack", ale); tmp.storage_class |= STC.exptemp; auto declareTmp = new DeclarationExp(ale.loc, tmp); auto castToSlice = new CastExp(ale.loc, new VarExp(ale.loc, tmp), @@ -3369,13 +3510,13 @@ private bool functionParameters(const ref Loc loc, Scope* sc, { if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique()) { - error(arg.loc, "function `%s` is overloaded", arg.toChars()); + error(arg.loc, "function `%s` is overloaded", arg.toErrMsg()); err = true; } } err |= arg.checkValue(); err |= arg.checkSharedAccess(sc); - err |= checkParamArgumentEscape(sc, fd, Id.dotdotdot, null, cast(STC) tf.parameterList.stc, arg, false, false); + err |= checkParamArgumentEscape(*sc, fd, Id.dotdotdot, null, cast(STC) tf.parameterList.stc, arg, false, false); arg = arg.optimize(WANTvalue); } (*arguments)[i] = arg; @@ -3563,7 +3704,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, */ Type tv = arg.type.baseElemOf(); if (!isRef && tv.ty == Tstruct) - arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null); + arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null, false); } (*arguments)[i] = arg; @@ -3573,9 +3714,9 @@ private bool functionParameters(const ref Loc loc, Scope* sc, /* Test compliance with DIP1021 Argument Ownership and Function Calls */ - if (global.params.useDIP1021 && (tf.trust == TRUST.safe || tf.trust == TRUST.default_) || - tf.islive) - err |= checkMutableArguments(sc, fd, tf, ethis, arguments, false); + if (sc.previews.dip1021 && (tf.trust == TRUST.safe || tf.trust == TRUST.default_) || + tf.isLive) + err |= checkMutableArguments(*sc, fd, tf, ethis, arguments, false); // If D linkage and variadic, add _arguments[] as first argument if (tf.isDstyleVariadic()) @@ -3674,6 +3815,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Scope* sc; Expression result; + // For binary expressions, stores recursive 'alias this' types of lhs and rhs to prevent endless loops. + // See tryAliasThisSemantic + Type[2] aliasThisStop; + + // (Optional) the expression this was lowered from, for better error messages + Expression parent; + this(Scope* sc) scope @safe { this.sc = sc; @@ -3724,37 +3872,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!e.type) e.type = Type.tfloat64; - else if (e.type.isimaginary && sc.flags & SCOPE.Cfile) + else if (!e.type.isImaginary || !sc.inCfile) { - /* Convert to core.stdc.config.complex - */ - Type t = getComplexLibraryType(e.loc, sc, e.type.ty); - if (t.ty == Terror) - return setError(); + e.type = e.type.typeSemantic(e.loc, sc); + result = e; + return; + } - Type tf; - switch (e.type.ty) - { - case Timaginary32: tf = Type.tfloat32; break; - case Timaginary64: tf = Type.tfloat64; break; - case Timaginary80: tf = Type.tfloat80; break; - default: - assert(0); - } + /* Convert to core.stdc.config.complex + */ + Type t = getComplexLibraryType(e.loc, sc, e.type.ty); + if (t.ty == Terror) + return setError(); - /* Construct ts{re : 0.0, im : e} - */ - TypeStruct ts = t.isTypeStruct; - Expressions* elements = new Expressions(2); - (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); - (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); - Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); - result = sle.expressionSemantic(sc); - return; + Type tf; + switch (e.type.ty) + { + case Timaginary32: tf = Type.tfloat32; break; + case Timaginary64: tf = Type.tfloat64; break; + case Timaginary80: tf = Type.tfloat80; break; + default: + assert(0); } - else - e.type = e.type.typeSemantic(e.loc, sc); - result = e; + + /* Construct ts{re : 0.0, im : e} + */ + TypeStruct ts = t.isTypeStruct; + Expressions* elements = new Expressions(2); + (*elements)[0] = new RealExp(e.loc, CTFloat.zero, tf); + (*elements)[1] = new RealExp(e.loc, e.toImaginary(), tf); + Expression sle = new StructLiteralExp(e.loc, ts.sym, elements); + result = sle.expressionSemantic(sc); } override void visit(ComplexExp e) @@ -3772,11 +3920,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IdentifierExp::semantic('%s')\n", exp.ident.toChars()); } - if (exp.type) // This is used as the dummy expression - { - result = exp; - return; - } + + scope (exit) result.rvalue = exp.rvalue; Dsymbol scopesym; Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); @@ -3859,10 +4004,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if (global.params.fixAliasThis) + if (sc.previews.fixAliasThis) { - ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol(); - if (expDsym) + if (ExpressionDsymbol expDsym = scopesym.isExpressionDsymbol()) { //printf("expDsym = %s\n", expDsym.exp.toChars()); result = expDsym.exp.expressionSemantic(sc); @@ -3876,7 +4020,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (!global.params.fixAliasThis && hasThis(sc)) + if (!sc.previews.fixAliasThis && hasThis(sc)) { for (AggregateDeclaration ad = sc.getStructClassScope(); ad;) { @@ -3906,7 +4050,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.ident == Id.ctfe) { - if (sc.flags & SCOPE.ctfe) + if (sc.ctfe) { error(exp.loc, "variable `__ctfe` cannot be read at compile time"); return setError(); @@ -3934,35 +4078,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!sc2.scopesym) continue; - if (auto ss = sc2.scopesym.isWithScopeSymbol()) + auto ss = sc2.scopesym.isWithScopeSymbol(); + if (!ss) + continue; + + if (ss.withstate.wthis) { - if (ss.withstate.wthis) + Expression e; + e = new VarExp(exp.loc, ss.withstate.wthis); + e = new DotIdExp(exp.loc, e, exp.ident); + e = e.trySemantic(sc); + if (e) { - Expression e; - e = new VarExp(exp.loc, ss.withstate.wthis); - e = new DotIdExp(exp.loc, e, exp.ident); - e = e.trySemantic(sc); - if (e) - { - result = e; - return; - } + result = e; + return; } - // Try Type.opDispatch (so the static version) - else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type) + } + // Try Type.opDispatch (so the static version) + else if (ss.withstate.exp && ss.withstate.exp.op == EXP.type) + { + Type t = ss.withstate.exp.isTypeExp().type; + if (!t) + continue; + + Expression e; + e = new TypeExp(exp.loc, t); + e = new DotIdExp(exp.loc, e, exp.ident); + e = e.trySemantic(sc); + if (e) { - if (Type t = ss.withstate.exp.isTypeExp().type) - { - Expression e; - e = new TypeExp(exp.loc, t); - e = new DotIdExp(exp.loc, e, exp.ident); - e = e.trySemantic(sc); - if (e) - { - result = e; - return; - } - } + result = e; + return; } } } @@ -3994,11 +4140,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ThisExp::semantic()\n"); } - if (e.type) - { - result = e; - return; - } FuncDeclaration fd = hasThis(sc); // fd is the uplevel function with the 'this' variable AggregateDeclaration ad; @@ -4013,7 +4154,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!s) { - error(e.loc, "`%s` is not in a class or struct scope", e.toChars()); + error(e.loc, "`%s` is not in a class or struct scope", e.toErrMsg()); return setError(); } ClassDeclaration cd = s.isClassDeclaration(); @@ -4059,16 +4200,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("SuperExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } FuncDeclaration fd = hasThis(sc); ClassDeclaration cd; Dsymbol s; + void err() + { + error(e.loc, "`super` is only allowed in non-static class member functions"); + result = ErrorExp.get(); + } /* Special case for typeof(this) and typeof(super) since both * should work even if they are not inside a non-static member function */ @@ -4079,26 +4220,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (!s) { - error(e.loc, "`%s` is not in a class scope", e.toChars()); + error(e.loc, "`%s` is not in a class scope", e.toErrMsg()); return setError(); } cd = s.isClassDeclaration(); - if (cd) + if (!cd) + continue; + + cd = cd.baseClass; + if (!cd) { - cd = cd.baseClass; - if (!cd) - { - error(e.loc, "class `%s` has no `super`", s.toChars()); - return setError(); - } - e.type = cd.type; - result = e; - return; + error(e.loc, "class `%s` has no `super`", s.toChars()); + return setError(); } + e.type = cd.type; + result = e; + return; } } if (!fd) - goto Lerr; + return err(); e.var = fd.vthis; assert(e.var && e.var.parent); @@ -4110,7 +4251,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor cd = s.isClassDeclaration(); //printf("parent is %s %s\n", fd.toParent().kind(), fd.toParent().toChars()); if (!cd) - goto Lerr; + return err(); if (!cd.baseClass) { error(e.loc, "no base class for `%s`", cd.toChars()); @@ -4126,11 +4267,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); result = e; - return; - - Lerr: - error(e.loc, "`super` is only allowed in non-static class member functions"); - result = ErrorExp.get(); } override void visit(NullExp e) @@ -4140,11 +4276,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("NullExp::semantic('%s')\n", e.toChars()); } // NULL is the same as (void *)0 - if (e.type) - { - result = e; - return; - } e.type = Type.tnull; result = e; } @@ -4233,11 +4364,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("StringExp::semantic() %s\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } OutBuffer buffer; size_t newlen = 0; @@ -4290,7 +4416,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } buffer.write4(0); e.setData(buffer.extractData(), newlen, 4); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tuns32.sarrayOf(e.len + 1); else e.type = Type.tdchar.immutableOf().arrayOf(); @@ -4315,7 +4441,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } buffer.writeUTF16(0); e.setData(buffer.extractData(), newlen, 2); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tuns16.sarrayOf(e.len + 1); else e.type = Type.twchar.immutableOf().arrayOf(); @@ -4327,7 +4453,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto default; default: - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) e.type = Type.tchar.sarrayOf(e.len + 1); else e.type = Type.tchar.immutableOf().arrayOf(); @@ -4346,11 +4472,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("+TupleExp::semantic(%s)\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (exp.e0) exp.e0 = exp.e0.expressionSemantic(sc); @@ -4363,7 +4484,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e = e.expressionSemantic(sc); if (!e.type) { - error(exp.loc, "`%s` has no value", e.toChars()); + error(exp.loc, "`%s` has no value", e.toErrMsg()); err = true; } else if (e.op == EXP.error) @@ -4388,11 +4509,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } /* Perhaps an empty array literal [ ] should be rewritten as null? */ @@ -4419,7 +4535,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (e.elements.length > 0 && t0.ty == Tvoid) { - error(e.loc, "`%s` of type `%s` has no value", e.toChars(), e.type.toChars()); + error(e.loc, "`%s` of type `%s` has no value", e.toErrMsg(), e.type.toChars()); return setError(); } @@ -4435,11 +4551,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AssocArrayLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } // Run semantic() on each element bool err_keys = arrayExpressionSemantic(e.keys.peekSlice(), sc); @@ -4466,7 +4577,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor semanticTypeInfo(sc, e.type); - if (checkAssocArrayLiteralEscape(sc, e, false)) + if (checkAssocArrayLiteralEscape(*sc, e, false)) return setError(); result = e; @@ -4478,11 +4589,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("StructLiteralExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } e.sd.size(e.loc); if (e.sd.sizeok != Sizeok.done) @@ -4526,7 +4632,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type t = cle.type.typeSemantic(cle.loc, sc); auto init = initializerSemantic(cle.initializer, sc, t, INITnointerpret); - auto e = initializerToExpression(init, t, (sc.flags & SCOPE.Cfile) != 0); + auto e = initializerToExpression(init, t, sc.inCfile); if (!e) { error(cle.loc, "cannot convert initializer `%s` to expression", toChars(init)); @@ -4587,11 +4693,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("+ScopeExp::semantic(%p '%s')\n", exp, exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } ScopeDsymbol sds2 = exp.sds; TemplateInstance ti = sds2.isTemplateInstance(); @@ -4758,14 +4859,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotTemplateInstanceExp(ne.loc, id, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(ne.loc, ne.loc.filename.toDString())); - arguments.push(new IntegerExp(ne.loc, ne.loc.linnum, Type.tint32)); - arguments.push(new StringExp(ne.loc, funcname.toDString())); - } id = new CallExp(ne.loc, id, arguments); ne.lowering = id.expressionSemantic(sc); @@ -4780,10 +4873,27 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("\tthisexp = %s\n", exp.thisexp.toChars()); printf("\tnewtype: %s\n", exp.newtype.toChars()); } - if (exp.type) // if semantic() already run + + if (exp.placement) { - result = exp; - return; + exp.placement = exp.placement.expressionSemantic(sc); + auto p = exp.placement; + if (p.op == EXP.error) + return setError(); + if (!p.isLvalue()) + { + error(p.loc, "PlacementExpression `%s` is an rvalue, but must be an lvalue", p.toChars()); + return setError(); + } + if (sc.setUnsafe(false, p.loc, "`@safe` function `%s` cannot use placement `new`", sc.func)) + { + return setError(); + } + if (!exp.placement.type.isNaked()) + { + error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type)); + return setError(); + } } //for error messages if the argument in [] is not convertible to size_t @@ -4861,7 +4971,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { return setError(); } - if (preFunctionParameters(sc, exp.argumentList)) + if (preFunctionParameters(sc, exp.argumentList, global.errorSink)) { return setError(); } @@ -4872,6 +4982,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } + uinteger_t placementSize; + if (exp.placement) + { + placementSize = size(exp.placement.type, exp.placement.loc); + auto objectSize = size(tb, exp.placement.loc); + //printf("placementSize: %lld objectSize: %lld\n", placementSize, objectSize); + if (!tb.isTypeClass && placementSize < objectSize) + { + error(exp.placement.loc, "new placement size %llu must be >= object size %llu", placementSize, objectSize); + return setError(); + } + } + const size_t nargs = exp.arguments ? exp.arguments.length : 0; Expression newprefix = null; @@ -4880,9 +5003,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto cd = tc.sym; if (cd.errors) return setError(); - cd.size(exp.loc); + auto objectSize = cd.size(exp.loc); if (cd.sizeok != Sizeok.done) return setError(); + if (exp.placement && placementSize < objectSize) + { + error(exp.placement.loc, "new placement size %llu must be >= class object size %llu", placementSize, objectSize); + return setError(); + } if (!cd.ctor) cd.ctor = cd.searchCtor(); if (cd.noDefaultCtor && !nargs && !cd.defaultCtor) @@ -5071,7 +5199,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // When using `@nogc` exception handling, lower `throw new E(args)` to // `throw (__tmp = _d_newThrowable!E(), __tmp.__ctor(args), __tmp)`. - if (global.params.ehnogc && exp.thrownew && + if (sc.previews.dip1008 && exp.thrownew && !cd.isCOMclass() && !cd.isCPPclass()) { assert(cd.ctor); @@ -5081,25 +5209,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto tiargs = new Objects(); tiargs.push(exp.newtype); - id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs); - id = new CallExp(exp.loc, id).expressionSemantic(sc); - Expression idVal; - Expression tmp = extractSideEffect(sc, "__tmpThrowable", idVal, id, true); - // auto castTmp = new CastExp(exp.loc, tmp, exp.type); + id = new DotTemplateInstanceExp(exp.loc, id, Id._d_newThrowable, tiargs); - auto ctor = new DotIdExp(exp.loc, tmp, Id.ctor).expressionSemantic(sc); - auto ctorCall = new CallExp(exp.loc, ctor, exp.arguments); + id = new CallExp(exp.loc, id).expressionSemantic(sc); - id = Expression.combine(idVal, exp.argprefix).expressionSemantic(sc); - id = Expression.combine(id, ctorCall).expressionSemantic(sc); - // id = Expression.combine(id, castTmp).expressionSemantic(sc); + exp.lowering = id.expressionSemantic(sc); - result = id.expressionSemantic(sc); + result = exp; return; } else if (sc.needsCodegen() && // interpreter doesn't need this lowered - !exp.onstack && !exp.type.isscope()) // these won't use the GC + !exp.placement && + !exp.onstack && !exp.type.isScopeClass()) // these won't use the GC { /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` * or `_d_newclassTTrace` @@ -5116,14 +5238,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tiargs.push(t); id = new DotTemplateInstanceExp(exp.loc, id, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } id = new CallExp(exp.loc, id, arguments); exp.lowering = id.expressionSemantic(sc); @@ -5206,16 +5320,22 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ foreach (arg; *exp.arguments) { - if (arg && checkNewEscape(sc, arg, false)) + if (arg && checkNewEscape(*sc, arg, false)) return setError(); } } exp.type = exp.type.pointerTo(); - tryLowerToNewItem(exp); + if (!exp.placement) + tryLowerToNewItem(exp); } else if (tb.ty == Tarray) { + if (exp.placement) + { + error(exp.placement.loc, "placement new cannot be used with dynamic arrays"); + return setError(); + } if (!nargs) { // https://issues.dlang.org/show_bug.cgi?id=20422 @@ -5265,9 +5385,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!global.params.useGC && sc.needsCodegen()) { version(IN_GCC) - error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-fno-rtti`", exp.toChars()); + error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-fno-rtti`", exp.toErrMsg()); else - error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-betterC`", exp.toChars()); + error(exp.loc, "expression `%s` allocates with the GC and cannot be used with switch `-betterC`", exp.toErrMsg()); return setError(); } @@ -5286,7 +5406,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto LskipNewArrayLowering; } - if (nargs == 1) + if (exp.placement) // no need to lower + { + } + else if (nargs == 1) { auto hook = global.params.tracegc ? Id._d_newarrayTTrace : Id._d_newarrayT; if (!verifyHookExist(exp.loc, *sc, hook, "new array")) @@ -5308,14 +5431,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } arguments.push((*exp.arguments)[0]); arguments.push(new IntegerExp(exp.loc, isShared, Type.tbool)); @@ -5335,7 +5450,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor lowering = new DotIdExp(exp.loc, lowering, Id.object); auto tbn = exp.type.nextOf(); - while (tbn.ty == Tarray) + size_t i = nargs; + while (tbn.ty == Tarray && --i) tbn = tbn.nextOf(); auto unqualTbn = tbn.unqualify(MODFlags.wild | MODFlags.const_ | MODFlags.immutable_ | MODFlags.shared_); @@ -5346,14 +5462,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor lowering = new DotTemplateInstanceExp(exp.loc, lowering, hook, tiargs); auto arguments = new Expressions(); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } arguments.push(new ArrayLiteralExp(exp.loc, Type.tsize_t.sarrayOf(nargs), exp.arguments)); arguments.push(new IntegerExp(exp.loc, tbn.isShared(), Type.tbool)); @@ -5362,7 +5470,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.lowering = lowering.expressionSemantic(sc); } } - else if (tb.isscalar()) + else if (tb.isScalar()) { if (!nargs) { @@ -5385,10 +5493,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.type = exp.type.pointerTo(); - tryLowerToNewItem(exp); + if (!exp.placement) + tryLowerToNewItem(exp); } else if (tb.ty == Taarray) { + if (exp.placement) + { + error(exp.placement.loc, "placement new cannot be used with associative arrays"); + return setError(); + } // e.g. `new Alias(args)` if (nargs) { @@ -5426,7 +5540,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression d = new DeclarationExp(e.loc, e.cd); sc = sc.push(); // just create new scope - sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE + sc.ctfe = false; // temporary stop CTFE d = d.expressionSemantic(sc); sc = sc.pop(); @@ -5438,7 +5552,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor sds.members.push(e.cd); } - Expression n = new NewExp(e.loc, e.thisexp, e.cd.type, e.arguments); + Expression n = new NewExp(e.loc, e.placement, e.thisexp, e.cd.type, e.arguments); Expression c = new CommaExp(e.loc, d, n); result = c.expressionSemantic(sc); @@ -5461,7 +5575,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (auto f = e.var.isFuncDeclaration()) { - if (f.checkNestedReference(sc, e.loc)) + if (f.checkNestedFuncReference(sc, e.loc)) return setError(); } @@ -5515,10 +5629,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (fd) { - // TODO: If fd isn't yet resolved its overload, the checkNestedReference + // TODO: If fd isn't yet resolved its overload, the checkNestedFuncReference // call would cause incorrect validation. // Maybe here should be moved in CallExp, or AddrExp for functions. - if (fd.checkNestedReference(sc, e.loc)) + if (fd.checkNestedFuncReference(sc, e.loc)) return setError(); } else if (auto od = e.var.isOverDeclaration()) @@ -5531,48 +5645,48 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor private void genIdent(FuncExp exp, Scope* sc) { - if (exp.fd.ident == Id.empty) - { - const(char)[] s; - if (exp.fd.fes) - s = "__foreachbody"; - else if (exp.fd.tok == TOK.reserved) - s = "__lambda"; - else if (exp.fd.tok == TOK.delegate_) - s = "__dgliteral"; - else - s = "__funcliteral"; + if (exp.fd.ident != Id.empty) + return; + + string s; + if (exp.fd.fes) + s = "__foreachbody"; + else if (exp.fd.tok == TOK.reserved) + s = "__lambda"; + else if (exp.fd.tok == TOK.delegate_) + s = "__dgliteral"; + else + s = "__funcliteral"; - DsymbolTable symtab; - if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + DsymbolTable symtab; + if (FuncDeclaration func = sc.parent.isFuncDeclaration()) + { + if (func.localsymtab is null) { - if (func.localsymtab is null) - { - // Inside template constraint, symtab is not set yet. - // Initialize it lazily. - func.localsymtab = new DsymbolTable(); - } - symtab = func.localsymtab; + // Inside template constraint, symtab is not set yet. + // Initialize it lazily. + func.localsymtab = new DsymbolTable(); } - else + symtab = func.localsymtab; + } + else + { + ScopeDsymbol sds = sc.parent.isScopeDsymbol(); + if (!sds.symtab) { - ScopeDsymbol sds = sc.parent.isScopeDsymbol(); - if (!sds.symtab) - { - // Inside template constraint, symtab may not be set yet. - // Initialize it lazily. - assert(sds.isTemplateInstance()); - sds.symtab = new DsymbolTable(); - } - symtab = sds.symtab; + // Inside template constraint, symtab may not be set yet. + // Initialize it lazily. + assert(sds.isTemplateInstance()); + sds.symtab = new DsymbolTable(); } - assert(symtab); - Identifier id = Identifier.generateId(s, symtab.length() + 1); - exp.fd.ident = id; - if (exp.td) - exp.td.ident = id; - symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); + symtab = sds.symtab; } + assert(symtab); + Identifier id = Identifier.generateIdWithLoc(s, exp.loc, cast(string) toDString(sc.parent.toPrettyChars())); + exp.fd.ident = id; + if (exp.td) + exp.td.ident = id; + symtab.insert(exp.td ? cast(Dsymbol)exp.td : cast(Dsymbol)exp.fd); } override void visit(FuncExp exp) @@ -5584,17 +5698,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf(" treq = %s\n", exp.fd.treq.toChars()); } - if (exp.type) - { - result = exp; - return; - } Expression e = exp; - uint olderrors; sc = sc.push(); // just create new scope - sc.flags &= ~SCOPE.ctfe; // temporary stop CTFE + sc.ctfe = false; // temporary stop CTFE sc.visibility = Visibility(Visibility.Kind.public_); // https://issues.dlang.org/show_bug.cgi?id=12506 /* fd.treq might be incomplete type, @@ -5620,6 +5728,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } + void done() + { + sc = sc.pop(); + result = e; + } + //printf("td = %p, treq = %p\n", td, fd.treq); if (exp.td) { @@ -5635,10 +5749,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else e = ErrorExp.get(); } - goto Ldone; + return done(); } - olderrors = global.errors; + const olderrors = global.errors; exp.fd.dsymbolSemantic(sc); if (olderrors == global.errors) { @@ -5651,7 +5765,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.type && exp.fd.type.ty == Tfunction && !exp.fd.type.nextOf()) (cast(TypeFunction)exp.fd.type).next = Type.terror; e = ErrorExp.get(); - goto Ldone; + return done(); } // Type is a "delegate to" or "pointer to" the function literal @@ -5664,7 +5778,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.fd.type.isTypeError()) { e = ErrorExp.get(); - goto Ldone; + return done(); } exp.type = new TypeDelegate(exp.fd.type.isTypeFunction()); exp.type = exp.type.typeSemantic(exp.loc, sc); @@ -5693,10 +5807,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } exp.fd.tookAddressOf++; - - Ldone: - sc = sc.pop(); - result = e; + done(); } /** @@ -5709,70 +5820,69 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expression callExpSemantic(FuncExp exp, Scope* sc, Expressions* arguments) { - if ((!exp.type || exp.type == Type.tvoid) && exp.td && arguments && arguments.length) + if ((exp.type && exp.type != Type.tvoid) || !exp.td ||! arguments || !arguments.length) + return exp.expressionSemantic(sc); + + for (size_t k = 0; k < arguments.length; k++) { - for (size_t k = 0; k < arguments.length; k++) - { - Expression checkarg = (*arguments)[k]; - if (checkarg.op == EXP.error) - return checkarg; - } + Expression checkarg = (*arguments)[k]; + if (checkarg.op == EXP.error) + return checkarg; + } - genIdent(exp, sc); + genIdent(exp, sc); - assert(exp.td.parameters && exp.td.parameters.length); - exp.td.dsymbolSemantic(sc); + assert(exp.td.parameters && exp.td.parameters.length); + exp.td.dsymbolSemantic(sc); - TypeFunction tfl = cast(TypeFunction)exp.fd.type; - size_t dim = tfl.parameterList.length; - if (arguments.length < dim) - { - // Default arguments are always typed, so they don't need inference. - Parameter p = tfl.parameterList[arguments.length]; - if (p.defaultArg) - dim = arguments.length; - } + TypeFunction tfl = cast(TypeFunction)exp.fd.type; + size_t dim = tfl.parameterList.length; + if (arguments.length < dim) + { + // Default arguments are always typed, so they don't need inference. + Parameter p = tfl.parameterList[arguments.length]; + if (p.defaultArg) + dim = arguments.length; + } - if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) || - arguments.length < dim) - { - OutBuffer buf; - foreach (idx, ref arg; *arguments) - buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars()); - error(exp.loc, "function literal `%s%s` is not callable using argument types `(%s)`", - exp.fd.toChars(), parametersTypeToChars(tfl.parameterList), - buf.peekChars()); - errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d", - arguments.length < dim ? "few".ptr : "many".ptr, - cast(int)dim, cast(int)arguments.length); - return ErrorExp.get(); - } + if ((tfl.parameterList.varargs == VarArg.none && arguments.length > dim) || + arguments.length < dim) + { + OutBuffer buf; + foreach (idx, ref arg; *arguments) + buf.printf("%s%s", (idx ? ", ".ptr : "".ptr), arg.type.toChars()); + error(exp.loc, "`%s` is not callable using argument types `(%s)`", + exp.fd.toErrMsg(), // parametersTypeToChars(tfl.parameterList), + buf.peekChars()); + errorSupplemental(exp.loc, "too %s arguments, expected %d, got %d", + arguments.length < dim ? "few".ptr : "many".ptr, + cast(int)dim, cast(int)arguments.length); + return ErrorExp.get(); + } - auto tiargs = new Objects(); - tiargs.reserve(exp.td.parameters.length); + auto tiargs = new Objects(); + tiargs.reserve(exp.td.parameters.length); - for (size_t i = 0; i < exp.td.parameters.length; i++) + for (size_t i = 0; i < exp.td.parameters.length; i++) + { + TemplateParameter tp = (*exp.td.parameters)[i]; + assert(dim <= tfl.parameterList.length); + foreach (u, p; tfl.parameterList) { - TemplateParameter tp = (*exp.td.parameters)[i]; - assert(dim <= tfl.parameterList.length); - foreach (u, p; tfl.parameterList) - { - if (u == dim) - break; + if (u == dim) + break; - if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) - { - Expression e = (*arguments)[u]; - tiargs.push(e.type); - break; - } + if (p.type.ty == Tident && (cast(TypeIdentifier)p.type).ident == tp.ident) + { + Expression e = (*arguments)[u]; + tiargs.push(e.type); + break; } } - - auto ti = new TemplateInstance(exp.loc, exp.td, tiargs); - return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc); } - return exp.expressionSemantic(sc); + + auto ti = new TemplateInstance(exp.loc, exp.td, tiargs); + return (new ScopeExp(exp.loc, ti)).expressionSemantic(sc); } override void visit(CallExp exp) @@ -5781,10 +5891,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CallExp::semantic() %s\n", exp.toChars()); } - if (exp.type) + + scope (exit) { - result = exp; - return; // semantic() already run + if (TypeFunction tf = exp.f ? cast(TypeFunction)exp.f.type : null) + { + result.rvalue = tf.isRvalue; + if (tf.isRvalue && !tf.isRef) + { + error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`"); + setError(); + } + } } Objects* tiargs = null; // initial list of template arguments @@ -5810,7 +5928,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (FuncExp fe = exp.e1.isFuncExp()) { if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) || - preFunctionParameters(sc, exp.argumentList)) + preFunctionParameters(sc, exp.argumentList, global.errorSink)) return setError(); // Run e1 semantic even if arguments have any errors @@ -5821,7 +5939,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -5952,7 +6070,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor __gshared int nest; if (++nest > global.recursionLimit) { - error(exp.loc, "recursive evaluation of `%s`", exp.toChars()); + error(exp.loc, "recursive evaluation of `%s`", exp.toErrMsg()); --nest; return setError(); } @@ -6004,7 +6122,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Rewrite (*fp)(arguments) to fp(arguments) exp.e1 = (cast(PtrExp)exp.e1).e1; } - else if (exp.e1.op == EXP.type && (sc && sc.flags & SCOPE.Cfile)) + else if (exp.e1.op == EXP.type && (sc && sc.inCfile)) { const numArgs = exp.arguments ? exp.arguments.length : 0; @@ -6050,7 +6168,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } if (arrayExpressionSemantic(exp.arguments.peekSlice(), sc) || - preFunctionParameters(sc, exp.argumentList)) + preFunctionParameters(sc, exp.argumentList, global.errorSink)) return setError(); // Check for call operator overload @@ -6075,7 +6193,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (sd.ctor) { auto ctor = sd.ctor.isCtorDeclaration(); - if (ctor && ctor.isCpCtor && ctor.isGenerated()) + if (ctor && (ctor.isCpCtor || ctor.isMoveCtor) && ctor.isGenerated()) sd.ctor = null; } @@ -6131,7 +6249,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } // No constructor, look for overload of opCall - if (search_function(sd, Id.call)) + if (search_function(sd, Id.opCall)) goto L1; // overload of opCall, therefore it's a call if (exp.e1.op != EXP.type) @@ -6172,13 +6290,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { L1: // Rewrite as e1.call(arguments) - Expression e = new DotIdExp(exp.loc, exp.e1, Id.call); + Expression e = new DotIdExp(exp.loc, exp.e1, Id.opCall); e = new CallExp(exp.loc, e, exp.arguments, exp.names); e = e.expressionSemantic(sc); result = e; return; } - else if (exp.e1.op == EXP.type && t1.isscalar()) + else if (exp.e1.op == EXP.type && t1.isScalar()) { Expression e; @@ -6219,46 +6337,45 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (tiargs && s.isFuncDeclaration()) continue; - if (auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet)) + auto f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, argumentList, FuncResolveFlag.quiet); + if (!f2) + continue; + if (f2.errors) + return null; + if (!f) { - if (f2.errors) - return null; - if (f) - { - /* Match in more than one overload set, - * even if one is a 'better' match than the other. - */ - if (f.isCsymbol() && f2.isCsymbol()) - { - /* C has global name space, so just pick one, such as f. - * If f and f2 are not compatible, that's how C rolls. - */ - } - else - ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error - } - else - f = f2; + f = f2; + continue; } - } - if (!f) - { - .error(loc, "no overload matches for `%s`", exp.toChars()); - errorSupplemental(loc, "Candidates are:"); - foreach (s; os.a) + /* Match in more than one overload set, + * even if one is a 'better' match than the other. + */ + if (f.isCsymbol() && f2.isCsymbol()) { - overloadApply(s, (ds){ - if (auto fd = ds.isFuncDeclaration()) - .errorSupplemental(ds.loc, "%s%s", fd.toChars(), - fd.type.toTypeFunction().parameterList.parametersTypeToChars()); - else - .errorSupplemental(ds.loc, "%s", ds.toChars()); - return 0; - }); + /* C has global name space, so just pick one, such as f. + * If f and f2 are not compatible, that's how C rolls. + */ } + else + ScopeDsymbol.multiplyDefined(loc, f, f2); // issue error + } + if (f && f.errors) + return null; + if (f) + return f; + .error(loc, "no overload matches for `%s`", exp.toErrMsg()); + errorSupplemental(loc, "Candidates are:"); + foreach (s; os.a) + { + overloadApply(s, (ds){ + if (auto fd = ds.isFuncDeclaration()) + .errorSupplemental(ds.loc, "%s%s", fd.toChars(), + fd.type.toTypeFunction().parameterList.parametersTypeToChars()); + else + .errorSupplemental(ds.loc, "%s", ds.toChars()); + return 0; + }); } - else if (f.errors) - f = null; return f; } @@ -6316,7 +6433,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tthis = ue.e1.type; if (!(exp.f.type.ty == Tfunction && (cast(TypeFunction)exp.f.type).isScopeQual)) { - if (checkParamArgumentEscape(sc, exp.f, Id.This, exp.f.vthis, STC.undefined_, ethis, false, false)) + if (checkParamArgumentEscape(*sc, exp.f, Id.This, exp.f.vthis, STC.none, ethis, false, false)) return setError(); } } @@ -6506,7 +6623,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (!t1) { - error(exp.loc, "function expected before `()`, not `%s`", exp.e1.toChars()); + error(exp.loc, "function expected before `()`, not `%s`", exp.e1.toErrMsg()); return setError(); } else if (t1.ty == Terror) @@ -6589,7 +6706,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - error(exp.loc, "function expected before `()`, not `%s` of type `%s`", exp.e1.toChars(), exp.e1.type.toChars()); + error(exp.loc, "function expected before `()`, not `%s` of type `%s`", exp.e1.toErrMsg(), exp.e1.type.toChars()); return setError(); } @@ -6603,13 +6720,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor tthis.modToBuffer(buf); //printf("tf = %s, args = %s\n", tf.deco, (*arguments)[0].type.deco); - .error(exp.loc, "%s `%s%s` is not callable using argument types `%s`", - p, exp.e1.toChars(), parametersTypeToChars(tf.parameterList), buf.peekChars()); + .error(exp.loc, "%s `%s` is not callable using argument types `%s`", + p, exp.e1.toErrMsg(), buf.peekChars()); if (failMessage) errorSupplemental(exp.loc, "%s", failMessage); } - if (tf.callMatch(null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch) + if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper, sc) == MATCH.nomatch) return setError(); // Purity and safety check should run after testing arguments matching @@ -6618,29 +6735,29 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.f.checkPurity(exp.loc, sc); exp.f.checkSafety(exp.loc, sc); exp.f.checkNogc(exp.loc, sc); - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); } - else if (sc.func && sc.intypeof != 1 && !(sc.flags & (SCOPE.ctfe | SCOPE.debug_))) + else if (sc.func && sc.intypeof != 1 && !(sc.ctfe || sc.debug_)) { bool err = false; - if (!tf.purity && sc.func.setImpure(exp.loc, "`pure` %s `%s` cannot call impure `%s`", exp.e1)) + if (!tf.purity && sc.func.setImpure(exp.loc, "calling impure `%s`", exp.e1)) { error(exp.loc, "`pure` %s `%s` cannot call impure %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); + sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg()); err = true; } - if (!tf.isnogc && sc.func.setGC(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc `%s`", exp.e1)) + if (!tf.isNogc && sc.func.setGC(exp.loc, "calling non-@nogc `%s`", exp.e1)) { error(exp.loc, "`@nogc` %s `%s` cannot call non-@nogc %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); + sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg()); err = true; } if (tf.trust <= TRUST.system && sc.setUnsafe(true, exp.loc, - "`@safe` function `%s` cannot call `@system` `%s`", sc.func, exp.e1)) + "calling `@system` `%s`", exp.e1)) { error(exp.loc, "`@safe` %s `%s` cannot call `@system` %s `%s`", - sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toChars()); + sc.func.kind(), sc.func.toPrettyChars(), p, exp.e1.toErrMsg()); err = true; } if (err) @@ -6685,14 +6802,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } .error(exp.loc, "%s `%s` is not callable using argument types `%s`", - exp.f.kind(), exp.f.toChars(), buf.peekChars()); + exp.f.kind(), exp.f.toErrMsg(), buf.peekChars()); if (failMessage) errorSupplemental(exp.loc, "%s", failMessage); .errorSupplemental(exp.f.loc, "`%s%s` declared here", exp.f.toPrettyChars(), parametersTypeToChars(tf.parameterList)); exp.f = null; } - if (tf.callMatch(null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch) + if (callMatch(exp.f, tf, null, exp.argumentList, 0, &errorHelper2, sc) == MATCH.nomatch) exp.f = null; } if (!exp.f || exp.f.errors) @@ -6701,7 +6818,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.f.needThis()) { // Change the ancestor lambdas to delegate before hasThis(sc) call. - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); auto memberFunc = hasThis(sc); @@ -6736,7 +6853,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor checkFunctionAttributes(exp, sc, exp.f); checkAccess(exp.loc, sc, null, exp.f); - if (exp.f.checkNestedReference(sc, exp.loc)) + if (exp.f.checkNestedFuncReference(sc, exp.loc)) return setError(); ethis = null; @@ -6761,7 +6878,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { exp.e1 = e1org; // https://issues.dlang.org/show_bug.cgi?id=10922 // avoid recursive expression printing - error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toChars()); + error(exp.loc, "forward reference to inferred return type of function call `%s`", exp.toErrMsg()); return setError(); } @@ -6798,10 +6915,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Loc loc = exp.loc; auto vptr = new DotIdExp(loc, new ThisExp(loc), Id.__vptr); - auto vptrTmpDecl = copyToTemp(0, "__vptrTmp", vptr); + auto vptrTmpDecl = copyToTemp(STC.none, "__vptrTmp", vptr); auto declareVptrTmp = new DeclarationExp(loc, vptrTmpDecl); - auto superTmpDecl = copyToTemp(0, "__superTmp", result); + auto superTmpDecl = copyToTemp(STC.none, "__superTmp", result); auto declareSuperTmp = new DeclarationExp(loc, superTmpDecl); auto declareTmps = new CommaExp(loc, declareVptrTmp, declareSuperTmp); @@ -6845,17 +6962,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DeclarationExp e) { - if (e.type) - { - result = e; - return; - } static if (LOGSEMANTIC) { printf("DeclarationExp::semantic() %s\n", e.toChars()); } - uint olderrors = global.errors; + const olderrors = global.errors; /* This is here to support extern(linkage) declaration, * where the extern(linkage) winds up being an AttribDeclaration @@ -6866,15 +6978,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor while (1) { AttribDeclaration ad = s.isAttribDeclaration(); - if (ad) - { - if (ad.decl && ad.decl.length == 1) - { - s = (*ad.decl)[0]; - continue; - } - } - break; + if (ad && ad.decl && ad.decl.length == 1) + s = (*ad.decl)[0]; + else + break; } //printf("inserting '%s' %p into sc = %p\n", s.toChars(), s, sc); @@ -6885,7 +6992,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor VarDeclaration v = s.isVarDeclaration(); if (v) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* Do semantic() on the type before inserting v into the symbol table */ @@ -6919,7 +7026,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if (v && (sc.flags & SCOPE.Cfile)) + if (v && sc.inCfile) { /* Do semantic() on initializer last so this will be legal: * int a = a; @@ -6957,7 +7064,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // The mangling change only works for D mangling } - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) { /* https://issues.dlang.org/show_bug.cgi?id=21272 * If we are in a foreach body we need to extract the @@ -6970,27 +7077,26 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Disallow shadowing for (Scope* scx = sc.enclosing; scx && (scx.func == sc.func || (fes_enclosing_func && scx.func == fes_enclosing_func)); scx = scx.enclosing) { - Dsymbol s2; - if (scx.scopesym && scx.scopesym.symtab && (s2 = scx.scopesym.symtab.lookup(s.ident)) !is null && s != s2) + if (!scx.scopesym || !scx.scopesym.symtab) + continue; + Dsymbol s2 = scx.scopesym.symtab.lookup(s.ident); + if (s2 is null || s == s2) + continue; + // allow STC.local symbols to be shadowed + // TODO: not really an optimal design + auto decl = s2.isDeclaration(); + if (decl && (decl.storage_class & STC.local)) + continue; + if (sc.func.fes) { - // allow STC.local symbols to be shadowed - // TODO: not really an optimal design - auto decl = s2.isDeclaration(); - if (!decl || !(decl.storage_class & STC.local)) - { - if (sc.func.fes) - { - deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); - deprecationSupplemental(s2.loc, "declared here"); - - } - else - { - error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); - errorSupplemental(s2.loc, "declared here"); - return setError(); - } - } + deprecation(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); + deprecationSupplemental(s2.loc, "declared here"); + } + else + { + error(e.loc, "%s `%s` is shadowing %s `%s`", s.kind(), s.ident.toChars(), s2.kind(), s2.toPrettyChars()); + errorSupplemental(s2.loc, "declared here"); + return setError(); } } } @@ -7052,62 +7158,51 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!ta) { //printf("ta %p ea %p sa %p\n", ta, ea, sa); - error(exp.loc, "no type for `typeid(%s)`", ea ? ea.toChars() : (sa ? sa.toChars() : "")); + error(exp.loc, "no type for `typeid(%s)`", ea ? ea.toErrMsg() : (sa ? sa.toChars() : "")); return setError(); } ta.checkComplexTransition(exp.loc, sc); - Expression e; auto tb = ta.toBasetype(); if (ea && tb.ty == Tclass) { if (tb.toDsymbol(sc).isClassDeclaration().classKind == ClassKind.cpp) { error(exp.loc, "runtime type information is not supported for `extern(C++)` classes"); - e = ErrorExp.get(); + return setError(); } else if (!Type.typeinfoclass) { error(exp.loc, "`object.TypeInfo_Class` could not be found, but is implicitly used"); - e = ErrorExp.get(); + return setError(); } else { /* Get the dynamic type, which is .classinfo */ ea = ea.expressionSemantic(sc); - e = new TypeidExp(ea.loc, ea); + Expression e = new TypeidExp(ea.loc, ea); e.type = Type.typeinfoclass.type; + result = e; + return; } } else if (ta.ty == Terror) { - e = ErrorExp.get(); + return setError(); } - else - { - // Handle this in the glue layer - e = new TypeidExp(exp.loc, ta); - bool genObjCode = true; - - // https://issues.dlang.org/show_bug.cgi?id=23650 - // We generate object code for typeinfo, required - // by typeid, only if in non-speculative context - if (sc.flags & SCOPE.compile) - { - genObjCode = false; - } + // Handle this in the glue layer + Expression e = new TypeidExp(exp.loc, ta); - e.type = getTypeInfoType(exp.loc, ta, sc, genObjCode); - semanticTypeInfo(sc, ta); + e.type = getTypeInfoType(exp.loc, ta, sc); + semanticTypeInfo(sc, ta); - if (ea) - { - e = new CommaExp(exp.loc, ea, e); // execute ea - e = e.expressionSemantic(sc); - } + if (ea) + { + e = new CommaExp(exp.loc, ea, e); // execute ea + e = e.expressionSemantic(sc); } result = e; } @@ -7178,7 +7273,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IsExp::semantic(%s)\n", e.toChars()); } - if (e.id && !(sc.flags & SCOPE.condition)) + if (e.id && !sc.condition) { error(e.loc, "can only declare type aliases within `static if` conditionals or `static assert`s"); return setError(); @@ -7197,7 +7292,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return no(); if (e.tok2 == TOK.package_ && p.isModule()) // Note that isModule() will return null for package modules because they're not actually instances of Module. return no(); - else if(e.tok2 == TOK.module_ && !(p.isModule() || p.isPackageMod())) + if (e.tok2 == TOK.module_ && !(p.isModule() || p.isPackageMod())) return no(); tded = e.targ; return yes(); @@ -7207,7 +7302,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Scope* sc2 = sc.copy(); // keep sc.flags sc2.tinst = null; sc2.minst = null; - sc2.flags |= SCOPE.fullinst; + sc2.fullinst = true; Type t = dmd.typesem.trySemantic(e.targ, e.loc, sc2); sc2.pop(); if (!t) // errors, so condition is false @@ -7387,39 +7482,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor //printf("targ = %s, %s\n", e.targ.toChars(), e.targ.deco); //printf("tspec = %s, %s\n", e.tspec.toChars(), e.tspec.deco); - if (e.tok == TOK.colon) + if (e.tok != TOK.colon) /* == */ { - // current scope is itself deprecated, or deprecations are not errors - const bool deprecationAllowed = sc.isDeprecated - || global.params.useDeprecated != DiagnosticReporting.error; - const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed; + if (e.targ.equals(e.tspec)) + return yes(); + else + return no(); + } - if (preventAliasThis && e.targ.ty == Tstruct) - { - if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec)) - return yes(); - else - return no(); - } - else if (preventAliasThis && e.targ.ty == Tclass) - { - if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec)) - return yes(); - else - return no(); - } - else if (e.targ.implicitConvTo(e.tspec)) + // current scope is itself deprecated, or deprecations are not errors + const bool deprecationAllowed = sc.isDeprecated + || global.params.useDeprecated != DiagnosticReporting.error; + const bool preventAliasThis = e.targ.hasDeprecatedAliasThis && !deprecationAllowed; + + if (preventAliasThis && e.targ.ty == Tstruct) + { + if ((cast(TypeStruct) e.targ).implicitConvToWithoutAliasThis(e.tspec)) return yes(); else return no(); } - else /* == */ + else if (preventAliasThis && e.targ.ty == Tclass) { - if (e.targ.equals(e.tspec)) + if ((cast(TypeClass) e.targ).implicitConvToWithoutAliasThis(e.tspec)) return yes(); else return no(); } + else if (e.targ.implicitConvTo(e.tspec)) + return yes(); + else + return no(); } else if (e.tspec) { @@ -7441,41 +7534,37 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor MATCH m = deduceType(e.targ, sc, e.tspec, *e.parameters, dedtypes, null, 0, e.tok == TOK.equal); if (m == MATCH.nomatch || (m != MATCH.exact && e.tok == TOK.equal)) - { return no(); - } - else - { - tded = cast(Type)dedtypes[0]; - if (!tded) - tded = e.targ; - Objects tiargs = Objects(1); - tiargs[0] = e.targ; - /* Declare trailing parameters - */ - for (size_t i = 1; i < e.parameters.length; i++) - { - TemplateParameter tp = (*e.parameters)[i]; - Declaration s = null; + tded = cast(Type)dedtypes[0]; + if (!tded) + tded = e.targ; + Objects tiargs = Objects(1); + tiargs[0] = e.targ; - m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s); - if (m == MATCH.nomatch) - return no(); - s.dsymbolSemantic(sc); - if (!sc.insert(s)) - { - Dsymbol pscopesym; - auto conflict = sc.search(Loc.initial, s.ident, pscopesym); - error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); - errorSupplemental(conflict.loc, "`%s` `%s` is defined here", - conflict.kind(), conflict.toChars()); - } + /* Declare trailing parameters + */ + for (size_t i = 1; i < e.parameters.length; i++) + { + TemplateParameter tp = (*e.parameters)[i]; + Declaration s = null; - unSpeculative(sc, s); + m = tp.matchArg(e.loc, sc, &tiargs, i, e.parameters, dedtypes, &s); + if (m == MATCH.nomatch) + return no(); + s.dsymbolSemantic(sc); + if (!sc.insert(s)) + { + Dsymbol pscopesym; + auto conflict = sc.search(Loc.initial, s.ident, pscopesym); + error(e.loc, "declaration `%s` is already defined", s.toPrettyChars()); + errorSupplemental(conflict.loc, "`%s` `%s` is defined here", + conflict.kind(), conflict.toChars()); } - return yes(); + + unSpeculative(sc, s); } + return yes(); } else if (e.id) { @@ -7489,19 +7578,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(BinAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } + Expression e; if (exp.e1.op == EXP.arrayLength) { // arr.length op= e2; @@ -7510,7 +7594,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (exp.e1.op == EXP.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray) + if (exp.e1.op == EXP.slice || exp.e1.type.isStaticOrDynamicArray()) { if (checkNonAssignmentArrayOp(exp.e1)) return setError(); @@ -7539,15 +7623,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); exp.type = exp.e1.type; - if (auto ad = isAggregate(exp.e1.type)) - { - if (const s = search_function(ad, Id.opOpAssign)) - { - error(exp.loc, "none of the `opOpAssign` overloads of `%s` are callable for `%s` of type `%s`", ad.toChars(), exp.e1.toChars(), exp.e1.type.toChars()); - return setError(); - } - } - if (exp.e1.checkScalar() || + if (exp.suggestOpOpAssign(sc, parent) || + exp.e1.checkScalar() || exp.e1.checkReadModifyWrite(exp.op, exp.e2) || exp.e1.checkSharedAccess(sc)) return setError(); @@ -7561,7 +7638,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else if (exp.checkNoBool()) return setError(); - if ((exp.op == EXP.addAssign || exp.op == EXP.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isintegral()) + if ((exp.op == EXP.addAssign || exp.op == EXP.minAssign) && exp.e1.type.toBasetype().ty == Tpointer && exp.e2.type.toBasetype().isIntegral()) { result = scaleFactor(exp, sc); return; @@ -7604,19 +7681,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = (cast(BinExp)e).reorderSettingAAElem(sc); } - private Expression compileIt(MixinExp exp, Scope *sc) + private Expression compileIt(MixinExp exp, Scope* sc) { OutBuffer buf; - if (expressionsToString(buf, sc, exp.exps)) + if (expressionsToString(buf, sc, exp.exps, exp.loc, null, true)) return null; - uint errors = global.errors; + const errors = global.errors; const len = buf.length; const str = buf.extractChars()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto loc = adjustLocForMixin(str, exp.loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, exp.loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); @@ -7722,35 +7799,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor const slice = se.peekString(); message("file %.*s\t(%s)", cast(int)slice.length, slice.ptr, resolvedNamez.ptr); } - if (global.params.moduleDeps.buffer !is null) - { - OutBuffer* ob = global.params.moduleDeps.buffer; - Module imod = sc._module; - if (!global.params.moduleDeps.name) - ob.writestring("depsFile "); - ob.writestring(imod.toPrettyChars()); - ob.writestring(" ("); - escapePath(ob, imod.srcfile.toChars()); - ob.writestring(") : "); - if (global.params.moduleDeps.name) - ob.writestring("string : "); - ob.write(se.peekString()); - ob.writestring(" ("); - escapePath(ob, resolvedNamez.ptr); - ob.writestring(")"); - ob.writenl(); - } - if (global.params.makeDeps.doOutput) - { - global.params.makeDeps.files.push(resolvedNamez.ptr); - } + addImportExpDep(global.params.moduleDeps, global.params.makeDeps, resolvedNamez, se.peekString(), sc._module); { auto fileName = FileName(resolvedNamez); if (auto fmResult = global.fileManager.getFileContents(fileName)) { se = new StringExp(e.loc, fmResult); + se.hexString = true; } else { @@ -7773,7 +7830,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // deprecated in 2.107 deprecation(e.loc, "assert condition cannot be a string literal"); deprecationSupplemental(e.loc, "If intentional, use `%s !is null` instead to preserve behaviour", - e.toChars()); + e.toErrMsg()); } const generateMsg = !exp.msg && @@ -7878,7 +7935,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return res.expressionSemantic(sc); } - const stc = op.isLvalue() ? STC.ref_ : 0; + const stc = op.isLvalue() ? STC.ref_ : STC.none; auto tmp = copyToTemp(stc, "__assertOp", op); tmp.dsymbolSemantic(sc); @@ -7913,7 +7970,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { const callExpIdent = callExpFunc.ident; isEqualsCallExpression = callExpIdent == Id.__equals || - callExpIdent == Id.eq; + callExpIdent == Id.opEquals; } } if (op == EXP.equal || op == EXP.notEqual || @@ -8046,7 +8103,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.msg = resolveProperties(sc, exp.msg); exp.msg = exp.msg.implicitCastTo(sc, Type.tchar.constOf().arrayOf()); exp.msg = exp.msg.optimize(WANTvalue); - checkParamArgumentEscape(sc, null, null, null, STC.undefined_, exp.msg, true, false); + checkParamArgumentEscape(*sc, null, null, null, STC.none, exp.msg, true, false); } if (exp.msg && exp.msg.op == EXP.error) @@ -8065,8 +8122,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* This is an `assert(0)` which means halt program execution */ FuncDeclaration fd = sc.parent.isFuncDeclaration(); - if (fd) - fd.hasReturnExp |= 4; sc.ctorflow.orCSX(CSX.halt); if (global.params.useAssert == CHECKENABLE.off) @@ -8094,6 +8149,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { import dmd.statementsem; + te.type = Type.tnoreturn; if (throwSemantic(te.loc, te.e1, sc)) result = te; else @@ -8105,10 +8161,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor static if (LOGSEMANTIC) { printf("DotIdExp::semantic(this = %p, '%s')\n", exp, exp.toChars()); - //printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op)); + printAST(exp); } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -8163,7 +8219,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e && isDotOpDispatch(e)) { auto ode = e; - uint errors = global.startGagging(); + const errors = global.startGagging(); e = resolvePropertiesX(sc, e); // Any error or if 'e' is not resolved, go to UFCS if (global.endGagging(errors) || e is ode) @@ -8188,11 +8244,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DotTemplateExp e) { - if (e.type) - { - result = e; - return; - } if (Expression ex = unaSemantic(e, sc)) { result = ex; @@ -8209,11 +8260,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotVarExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.var = exp.var.toAlias().isDeclaration(); @@ -8381,11 +8427,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTemplateInstanceExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // Indicate we need to resolve by UFCS. Expression e = exp.dotTemplateSemanticProp(sc, DotExpFlag.gag); if (!e) @@ -8401,11 +8442,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DelegateExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } e.e1 = e.e1.expressionSemantic(sc); @@ -8426,7 +8462,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor MODMatchToBuffer(&thisBuf, e.e1.type.mod, tf.mod); MODMatchToBuffer(&funcBuf, tf.mod, e.e1.type.mod); error(e.loc, "%smethod `%s` is not callable using a %s`%s`", - funcBuf.peekChars(), f.toPrettyChars(), thisBuf.peekChars(), e.e1.toChars()); + funcBuf.peekChars(), f.toPrettyChars(), thisBuf.peekChars(), e.e1.toErrMsg()); return setError(); } } @@ -8467,11 +8503,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("DotTypeExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (auto e = unaSemantic(exp, sc)) { @@ -8489,11 +8520,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AddrExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (Expression ex = unaSemantic(exp, sc)) { @@ -8501,7 +8527,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* Special handling for &"string"/&(T[]){0, 1} * since C regards string/array literals as lvalues @@ -8593,8 +8619,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ if (1) { - if (sc.setUnsafe(false, exp.loc, - "cannot take address of lazy parameter `%s` in `@safe` function `%s`", ve, sc.func)) + if (sc.setUnsafe(false, exp.loc, "taking address of lazy parameter `%s`", ve)) { setError(); return; @@ -8621,7 +8646,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.e1.type) { - error(exp.loc, "cannot take address of `%s`", exp.e1.toChars()); + error(exp.loc, "cannot take address of `%s`", exp.e1.toErrMsg()); return setError(); } if (!checkAddressable(exp, sc)) @@ -8645,7 +8670,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor error(exp.loc, "forward reference to %s `%s`", d.kind(), d.toChars()); } else - error(exp.loc, "forward reference to type `%s` of expression `%s`", exp.e1.type.toChars(), exp.e1.toChars()); + error(exp.loc, "forward reference to type `%s` of expression `%s`", exp.e1.type.toChars(), exp.e1.toErrMsg()); return setError(); } } @@ -8741,11 +8766,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (sc.func && !sc.intypeof && !(sc.flags & SCOPE.debug_)) + if (sc.func && !sc.intypeof && !sc.debug_) { - sc.setUnsafe(false, exp.loc, - "`this` reference necessary to take address of member `%s` in `@safe` function `%s`", - f, sc.func); + sc.setUnsafe(false, exp.loc, "taking address of member `%s` without `this` reference", f); } } } @@ -8757,7 +8780,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * &a[i] * check 'a' the same as for a regular variable */ - if (VarDeclaration v = expToVariable(exp.e1)) + int deref; + if (VarDeclaration v = expToVariable(exp.e1, deref)) { v.checkPurity(exp.e1.loc, sc); } @@ -8789,14 +8813,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("PtrExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8815,7 +8833,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor case Tarray: if (isNonAssignmentArrayOp(exp.e1)) goto default; - error(exp.loc, "using `*` on an array is no longer supported; use `*(%s).ptr` instead", exp.e1.toChars()); + error(exp.loc, "using `*` on an array is no longer supported; use `*(%s).ptr` instead", exp.e1.toErrMsg()); exp.type = (cast(TypeArray)tb).next; exp.e1 = exp.e1.castTo(sc, exp.type.pointerTo()); break; @@ -8832,7 +8850,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto case Terror; } - if (sc.flags & SCOPE.Cfile && exp.type && exp.type.toBasetype().ty == Tvoid) + if (sc.inCfile && exp.type && exp.type.toBasetype().ty == Tvoid) { // https://issues.dlang.org/show_bug.cgi?id=23752 // `&*((void*)(0))` is allowed in C @@ -8852,14 +8870,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("NegExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8868,7 +8880,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor fix16997(sc, exp); exp.type = exp.e1.type; Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp.e1)) { @@ -8883,9 +8895,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } - if (exp.e1.checkNoBool()) - return setError(); - if (exp.e1.checkArithmetic(exp.op) || + if (exp.e1.checkNoBool() || + exp.e1.checkArithmetic(exp.op) || exp.e1.checkSharedAccess(sc)) return setError(); @@ -8898,10 +8909,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("UAddExp::semantic('%s')\n", exp.toChars()); } - assert(!exp.type); - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8913,11 +8922,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } - if (exp.e1.checkNoBool()) - return setError(); - if (exp.e1.checkArithmetic(exp.op)) - return setError(); - if (exp.e1.checkSharedAccess(sc)) + + if (exp.e1.checkNoBool() || + exp.e1.checkArithmetic(exp.op) || + exp.e1.checkSharedAccess(sc)) return setError(); result = exp.e1; @@ -8925,14 +8933,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ComExp exp) { - if (exp.type) - { - result = exp; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; @@ -8941,7 +8943,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor fix16997(sc, exp); exp.type = exp.e1.type; Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp.e1)) { @@ -8956,9 +8958,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } - if (exp.e1.checkNoBool()) - return setError(); - if (exp.e1.checkIntegral() || + if (exp.e1.checkNoBool() || + exp.e1.checkIntegral() || exp.e1.checkSharedAccess(sc)) return setError(); @@ -8967,11 +8968,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(NotExp e) { - if (e.type) - { - result = e; - return; - } e.setNoderefOperand(); @@ -9003,23 +8999,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (checkNonAssignmentArrayOp(e.e1)) return setError(); - e.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + e.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; result = e; } override void visit(DeleteExp exp) { - // @@@DEPRECATED_2.109@@@ - // 1. Deprecated since 2.079 - // 2. Error since 2.099 - // 3. Removal of keyword, "delete" can be used for other identities - if (!exp.isRAII) - { - error(exp.loc, "the `delete` keyword is obsolete"); - errorSupplemental(exp.loc, "use `object.destroy()` (and `core.memory.GC.free()` if applicable) instead"); - return setError(); - } - Expression e = exp; if (Expression ex = unaSemantic(exp, sc)) @@ -9076,13 +9061,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("CastExp::semantic('%s')\n", exp.toChars()); } //static int x; assert(++x < 10); - if (exp.type) - { - result = exp; - return; - } - if ((sc && sc.flags & SCOPE.Cfile) && + if ((sc && sc.inCfile) && exp.to && (exp.to.ty == Tident || exp.to.ty == Tsarray) && (exp.e1.op == EXP.address || exp.e1.op == EXP.star || exp.e1.op == EXP.uadd || exp.e1.op == EXP.negate)) @@ -9136,6 +9116,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + // https://issues.dlang.org/show_bug.cgi?id=24701 + auto r = checkNoreturnVarAccess(exp.e1); + if (r != exp.e1 && exp.to && !exp.to.isTypeNoreturn()) + { + result = r; + return; + } + if (exp.to && !exp.to.isTypeSArray() && !exp.to.isTypeFunction()) exp.e1 = exp.e1.arrayFuncConv(sc); @@ -9155,7 +9143,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.e1.type) { - error(exp.loc, "cannot cast `%s`", exp.e1.toChars()); + error(exp.loc, "cannot cast `%s`", exp.e1.toErrMsg()); return setError(); } @@ -9193,7 +9181,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.to.ty == Ttuple) { - error(exp.loc, "cannot cast `%s` of type `%s` to type sequence `%s`", exp.e1.toChars(), exp.e1.type.toChars(), exp.to.toChars()); + error(exp.loc, "cannot cast `%s` of type `%s` to type sequence `%s`", exp.e1.toErrMsg(), exp.e1.type.toChars(), exp.to.toChars()); return setError(); } @@ -9207,7 +9195,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!exp.to.equals(exp.e1.type) && exp.mod == cast(ubyte)~0) { - if (Expression e = exp.op_overload(sc)) + if (Expression e = exp.opOverloadCast(sc)) { result = e.implicitCastTo(sc, exp.to); return; @@ -9236,7 +9224,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if (!t1b.equals(tob) && (t1b.ty == Tarray || t1b.ty == Tsarray)) + if (!t1b.equals(tob) && t1b.isStaticOrDynamicArray()) { if (checkNonAssignmentArrayOp(exp.e1)) return setError(); @@ -9250,13 +9238,27 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // Check for unsafe casts - if (!isSafeCast(ex, t1b, tob)) + string msg; + if (!isSafeCast(ex, t1b, tob, msg)) { - if (sc.setUnsafe(false, exp.loc, "cast from `%s` to `%s` not allowed in safe code", exp.e1.type, exp.to)) + if (sc.setUnsafe(false, exp.loc, + "cast from `%s` to `%s`", exp.e1.type, exp.to)) { + if (msg.length) + errorSupplemental(exp.loc, "%.*s", msg.fTuple.expand); return setError(); } } + else if (msg.length) // deprecated unsafe + { + const err = sc.setUnsafePreview(FeatureState.default_, false, exp.loc, + "cast from `%s` to `%s`", exp.e1.type, exp.to); + // if message was printed + if (sc.func && sc.func.isSafeBypassingInference() && !sc.isDeprecated()) + deprecationSupplemental(exp.loc, "%.*s", msg.fTuple.expand); + if (err) + return setError(); + } // `object.__ArrayCast` is a rewrite of an old runtime hook `_d_arraycast`. `_d_arraycast` was not built // to handle certain casts. Those casts which `object.__ArrayCast` does not support are filtered out. @@ -9322,7 +9324,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.4-5: A cast does not yield an lvalue. * So ensure that castTo does not strip away the cast so that this @@ -9343,11 +9345,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("VectorExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.e1 = exp.e1.expressionSemantic(sc); exp.type = exp.to.typeSemantic(exp.loc, sc); @@ -9368,7 +9365,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (elem.isConst() == 1) return false; - error(exp.loc, "constant expression expected, not `%s`", elem.toChars()); + error(exp.loc, "constant expression expected, not `%s`", elem.toErrMsg()); return true; } @@ -9416,11 +9413,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("SliceExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // operator overloading should be handled in ArrayExp already. if (Expression ex = unaSemantic(exp, sc)) @@ -9433,7 +9425,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (exp.lwr || exp.upr) { - error(exp.loc, "cannot slice type `%s`", exp.e1.toChars()); + error(exp.loc, "cannot slice type `%s`", exp.e1.toErrMsg()); return setError(); } Expression e = new TypeExp(exp.loc, exp.e1.type.arrayOf()); @@ -9485,7 +9477,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { if (t1b.isPtrToFunction()) { - error(exp.loc, "cannot slice function pointer `%s`", exp.e1.toChars()); + error(exp.loc, "cannot slice function pointer `%s`", exp.e1.toErrMsg()); return setError(); } if (!exp.lwr || !exp.upr) @@ -9493,8 +9485,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor error(exp.loc, "upper and lower bounds are needed to slice a pointer"); if (auto ad = isAggregate(tp.next.toBasetype())) { - auto s = search_function(ad, Id.index); - if (!s) s = search_function(ad, Id.slice); + auto s = search_function(ad, Id.opIndex); + if (!s) s = search_function(ad, Id.opSlice); if (s) { auto fd = s.isFuncDeclaration(); @@ -9502,9 +9494,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { errorSupplemental(exp.loc, "pointer `%s` points to an aggregate that defines an `%s`, perhaps you meant `(*%s)[]`", - exp.e1.toChars(), + exp.e1.toErrMsg(), s.ident.toChars(), - exp.e1.toChars() + exp.e1.toErrMsg() ); } @@ -9513,7 +9505,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if (sc.setUnsafe(false, exp.loc, "pointer slicing not allowed in safe functions")) + if (sc.setUnsafe(false, exp.loc, "pointer slicing")) return setError(); } else if (t1b.ty == Tarray) @@ -9545,14 +9537,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - error(exp.loc, "`%s` cannot be sliced with `[]`", t1b.ty == Tvoid ? exp.e1.toChars() : t1b.toChars()); + error(exp.loc, "`%s` cannot be sliced with `[]`", t1b.ty == Tvoid ? exp.e1.toErrMsg() : t1b.toChars()); return setError(); } /* Run semantic on lwr and upr. */ Scope* scx = sc; - if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple) + if (t1b.isStaticOrDynamicArray() || t1b.ty == Ttuple) { // Create scope for 'length' variable ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp); @@ -9659,7 +9651,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor IntRange lwrRange = getIntRange(exp.lwr); IntRange uprRange = getIntRange(exp.upr); - if (t1b.ty == Tsarray || t1b.ty == Tarray) + if (t1b.isStaticOrDynamicArray()) { Expression el = new ArrayLengthExp(exp.loc, exp.e1); el = el.expressionSemantic(sc); @@ -9703,11 +9695,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayLengthExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } if (Expression ex = unaSemantic(e, sc)) { @@ -9726,9 +9713,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("ArrayExp::semantic('%s')\n", exp.toChars()); } - assert(!exp.type); - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -9743,15 +9729,48 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (result) return; - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadArray(sc)) { result = e; return; } - if (isAggregate(exp.e1.type)) - error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + if (auto ad = isAggregate(exp.e1.type)) + { + if (exp.arguments.length == 0) + { + error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + errorSupplemental(ad.loc, "perhaps define `auto opIndex() {}` for `%s`", ad.toPrettyChars()); + } + else + { + const(char)* typeString(Expression exp) + { + if (auto e = exp.trySemantic(sc)) + return e.type.toChars(); + else + return "__error__"; + } + + if (auto ie = (*exp.arguments)[0].isIntervalExp()) + { + error(exp.loc, "no `[%s]` operator overload for type `%s`", ie.toChars(), exp.e1.type.toChars()); + errorSupplemental(ad.loc, "perhaps define `auto opSlice(%s lower, %s upper) {}` for `%s`", + typeString(ie.lwr), typeString(ie.upr), ad.toPrettyChars()); + } + else + { + OutBuffer buf; + buf.printf("%s", typeString((*exp.arguments)[0])); + foreach (e; (*exp.arguments)[1 .. $]) + buf.printf(", %s", typeString(e)); + + error(exp.loc, "no `[]` operator overload for type `%s`", exp.e1.type.toChars()); + errorSupplemental(ad.loc, "perhaps define `auto opIndex(%s) {}` for `%s`", + buf.extractChars, ad.toPrettyChars()); + } + } + } else if (exp.e1.op == EXP.type && exp.e1.type.ty != Ttuple) error(exp.loc, "static array of `%s` with multiple lengths not allowed", exp.e1.type.toChars()); else if (isIndexableNonAggregate(exp.e1.type)) @@ -9797,11 +9816,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(CommaExp e) { //printf("Semantic.CommaExp() %s\n", e.toChars()); - if (e.type) - { - result = e; - return; - } // Allow `((a,b),(x,y))` if (e.allowCommaExp) @@ -9826,16 +9840,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = e.e2.type; result = e; - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return; - if (e.type is Type.tvoid) + if (!e.isGenerated) { - checkMustUse(e.e1, sc); - discardValue(e.e1); + if (e.allowCommaExp) + { + checkMustUse(e.e1, sc); + discardValue(e.e1); + } + else + error(e.loc, "using the result of a comma expression is not allowed"); } - else if (!e.allowCommaExp && !e.isGenerated) - error(e.loc, "using the result of a comma expression is not allowed"); } override void visit(IntervalExp e) @@ -9844,11 +9861,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IntervalExp::semantic('%s')\n", e.toChars()); } - if (e.type) - { - result = e; - return; - } Expression le = e.lwr; le = le.expressionSemantic(sc); @@ -9923,11 +9935,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("IndexExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } // operator overloading should be handled in ArrayExp already. if (!exp.e1.type) @@ -9965,7 +9972,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor t1b = t1b.castMod(tv1.mod); exp.e1 = exp.e1.castTo(sc, t1b); } - if (t1b.ty == Tsarray || t1b.ty == Tarray) + if (t1b.isStaticOrDynamicArray()) { if (!checkAddressable(exp, sc)) return setError(); @@ -9974,7 +9981,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* Run semantic on e2 */ Scope* scx = sc; - if (t1b.ty == Tsarray || t1b.ty == Tarray || t1b.ty == Ttuple) + if (t1b.isStaticOrDynamicArray() || t1b.ty == Ttuple) { // Create scope for 'length' variable ScopeDsymbol sym = new ArrayScopeSymbol(sc, exp); @@ -10006,7 +10013,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor case Tpointer: if (t1b.isPtrToFunction()) { - error(exp.loc, "cannot index function pointer `%s`", exp.e1.toChars()); + error(exp.loc, "cannot index function pointer `%s`", exp.e1.toErrMsg()); return setError(); } exp.e2 = exp.e2.implicitCastTo(sc, Type.tsize_t); @@ -10016,7 +10023,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.e2.op == EXP.int64 && exp.e2.toInteger() == 0) { } - else if (sc.setUnsafe(false, exp.loc, "`@safe` function `%s` cannot index pointer `%s`", sc.func, exp.e1)) + else if (sc.setUnsafe(false, exp.loc, "indexing pointer `%s`", exp.e1)) { return setError(); } @@ -10054,7 +10061,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } semanticTypeInfo(sc, taa); - checkNewEscape(sc, exp.e2, false); + checkNewEscape(*sc, exp.e2, false); exp.type = taa.next; break; @@ -10088,7 +10095,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (length <= index) { - error(exp.loc, "array index `[%llu]` is outside array bounds `[0 .. %llu]`", index, cast(ulong)length); + error(exp.loc, "sequence index `[%llu]` is outside bounds `[0 .. %llu]`", index, cast(ulong)length); return setError(); } Expression e; @@ -10103,14 +10110,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } default: - error(exp.loc, "`%s` must be an array or pointer type, not `%s`", exp.e1.toChars(), exp.e1.type.toChars()); + error(exp.loc, "`%s` must be an array or pointer type, not `%s`", exp.e1.toErrMsg(), exp.e1.type.toChars()); return setError(); } // We might know $ now setLengthVarIfKnown(exp.lengthVar, t1b); - if (t1b.ty == Tsarray || t1b.ty == Tarray) + if (t1b.isStaticOrDynamicArray()) { Expression el = new ArrayLengthExp(exp.loc, exp.e1); el = el.expressionSemantic(sc); @@ -10125,7 +10132,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // OR it in, because it might already be set for C array indexing exp.indexIsInBounds |= bounds.contains(getIntRange(exp.e2)); } - else if (sc.flags & SCOPE.Cfile && t1b.ty == Tsarray) + else if (sc.inCfile && t1b.ty == Tsarray) { if (auto ve = exp.e1.isVarExp()) { @@ -10150,13 +10157,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("PostExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* See if need to rewrite the AST because of cast/call ambiguity */ @@ -10180,20 +10182,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.e1 = e1x; - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - if (exp.e1.checkReadModifyWrite(exp.op)) return setError(); if (exp.e1.op == EXP.slice) { const(char)* s = exp.op == EXP.plusPlus ? "increment" : "decrement"; - error(exp.loc, "cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toChars(), s); + error(exp.loc, "cannot post-%s array slice `%s`, use pre-%s instead", s, exp.e1.toErrMsg(), s); return setError(); } @@ -10218,7 +10213,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* Rewrite as: * auto tmp = e1; ++e1; tmp */ - auto tmp = copyToTemp(0, "__pitmp", exp.e1); + auto tmp = copyToTemp(STC.none, "__pitmp", exp.e1); Expression ea = new DeclarationExp(exp.loc, tmp); Expression eb = exp.e1.syntaxCopy(); @@ -10229,7 +10224,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Combine de,ea,eb,ec if (de) ea = new CommaExp(exp.loc, de, ea); - e = new CommaExp(exp.loc, ea, eb); + Expression e = new CommaExp(exp.loc, ea, eb); e = new CommaExp(exp.loc, e, ec); e = e.expressionSemantic(sc); result = e; @@ -10239,7 +10234,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.modifiableLvalue(sc); exp.e1 = exp.e1.optimize(WANTvalue, /*keepLvalue*/ true); - e = exp; + Expression e = exp; if (exp.e1.checkScalar() || exp.e1.checkSharedAccess(sc)) return setError(); @@ -10256,20 +10251,20 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PreExp exp) { - Expression e = exp.op_overload(sc); // printf("PreExp::semantic('%s')\n", toChars()); - if (e) + if (Expression e = exp.opOverloadUnary(sc)) { result = e; return; } // Rewrite as e1+=1 or e1-=1 + Expression e; if (exp.op == EXP.prePlusPlus) e = new AddAssignExp(exp.loc, exp.e1, IntegerExp.literal!1); else e = new MinAssignExp(exp.loc, exp.e1, IntegerExp.literal!1); - result = e.expressionSemantic(sc); + result = e.expressionSemanticWithParent(sc, exp); } /* @@ -10312,9 +10307,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { static if (LOGSEMANTIC) { - if (exp.op == EXP.blit) printf("BlitExp.toElem('%s')\n", exp.toChars()); - if (exp.op == EXP.assign) printf("AssignExp.toElem('%s')\n", exp.toChars()); - if (exp.op == EXP.construct) printf("ConstructExp.toElem('%s')\n", exp.toChars()); + if (exp.op == EXP.blit) printf("BlitExp.semantic('%s')\n", exp.toChars()); + if (exp.op == EXP.assign) printf("AssignExp.semantic('%s')\n", exp.toChars()); + if (exp.op == EXP.construct) printf("ConstructExp.semantic('%s')\n", exp.toChars()); } void setResult(Expression e, int line = __LINE__) @@ -10323,16 +10318,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; } - if (exp.type) - { - return setResult(exp); - } - Expression e1old = exp.e1; if (auto e2comma = exp.e2.isCommaExp()) { - if (!e2comma.isGenerated && !(sc.flags & SCOPE.Cfile)) + if (!e2comma.isGenerated && !sc.inCfile) error(exp.loc, "using the result of a comma expression is not allowed"); /* Rewrite to get rid of the comma from rvalue @@ -10380,10 +10370,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor AggregateDeclaration ad = isAggregate(t1b); if (!ad) break; - if (search_function(ad, Id.indexass)) + if (search_function(ad, Id.opIndexAssign)) { // Deal with $ - res = resolveOpDollar(sc, ae, &e0); + res = resolveOpDollar(sc, ae, e0); if (!res) // a[i..j] = e2 might be: a.opSliceAssign(e2, i, j) goto Lfallback; if (res.op == EXP.error) @@ -10399,7 +10389,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ Expressions* a = ae.arguments.copy(); a.insert(0, exp.e2); - res = new DotIdExp(exp.loc, ae.e1, Id.indexass); + res = new DotIdExp(exp.loc, ae.e1, Id.opIndexAssign); res = new CallExp(exp.loc, res, a); if (maybeSlice) // a[] = e2 might be: a.opSliceAssign(e2) res = res.trySemantic(sc); @@ -10410,10 +10400,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Lfallback: - if (maybeSlice && search_function(ad, Id.sliceass)) + if (maybeSlice && search_function(ad, Id.opSliceAssign)) { // Deal with $ - res = resolveOpDollar(sc, ae, ie, &e0); + res = resolveOpDollar(sc, ae, ie, e0); if (res.op == EXP.error) return setResult(res); @@ -10433,7 +10423,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor a.push(ie.lwr); a.push(ie.upr); } - res = new DotIdExp(exp.loc, ae.e1, Id.sliceass); + res = new DotIdExp(exp.loc, ae.e1, Id.opSliceAssign); res = new CallExp(exp.loc, res, a); res = res.expressionSemantic(sc); return setResult(Expression.combine(e0, res)); @@ -10477,7 +10467,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e1x = e; } - else if (sc.flags & SCOPE.Cfile && e1x.isDotIdExp()) + else if (sc.inCfile && e1x.isDotIdExp()) { auto die = e1x.isDotIdExp(); e1x = fieldLookup(die.e1, sc, die.ident, die.arrow); @@ -10497,7 +10487,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ auto ode = e; exp.e2 = exp.e2.expressionSemantic(sc); - uint errors = global.startGagging(); + const errors = global.startGagging(); e = resolvePropertiesX(sc, e, exp.e2); // Any error or if 'e' is not resolved, go to UFCS if (global.endGagging(errors) || e is ode) @@ -10523,7 +10513,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * or: * f() = value */ - if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, exp)) + if (Expression e = resolvePropertiesX(sc, e1x, exp.e2, &aliasThisStop)) return setResult(e); if (e1x.checkRightThis(sc)) @@ -10656,6 +10646,35 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.op == EXP.assign && exp.e1.checkModifiable(sc) == Modifiable.initialization) { + // Check common mistake of misspelled parameters in constructors, + // e.g. `this(int feild) { this.field = field; }` + if (auto dve1 = exp.e1.isDotVarExp) + if (auto dve2 = exp.e2.isDotVarExp) + if (sc.func && sc.func.parameters && dve1.e1.isThisExp && dve2.e1.isThisExp() + && dve1.var.ident.equals(dve2.var.ident)) + { + // @@@DEPRECATED_2.121@@@ + // Deprecated in 2.111, make it an error in 2.121 + deprecation(exp.e1.loc, "cannot initialize field `%s` with itself", dve1.var.toChars()); + auto findParameter(const(char)[] s, ref int cost) + { + foreach (p; *sc.func.parameters) + { + if (p.ident.toString == s) + { + cost = 1; + return p.ident.toString; + } + } + return null; + } + import dmd.root.speller : speller; + if (auto s = speller!findParameter(dve1.var.ident.toString)) + { + deprecationSupplemental(sc.func.loc, "did you mean to use parameter `%.*s`?\n", s.fTuple.expand); + } + } + //printf("[%s] change to init - %s\n", exp.loc.toChars(), exp.toChars()); auto t = exp.type; exp = new ConstructExp(exp.loc, exp.e1, exp.e2); @@ -10750,7 +10769,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor dvx.e1 = e1x; auto cx = cast(CallExp)ce.copy(); cx.e1 = dvx; - if (checkConstructorEscape(sc, cx, false)) + if (checkConstructorEscape(*sc, cx, false)) return setError(); Expression e0; @@ -10784,6 +10803,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* We have a copy constructor for this */ + //printf("exp: %s\n", toChars(exp)); + //printf("e2x: %s\n", toChars(e2x)); if (e2x.isLvalue()) { if (sd.hasCopyCtor) @@ -10830,6 +10851,38 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } + else if (sd.hasMoveCtor && (!e2x.isCallExp() || e2x.rvalue) && !e2x.isStructLiteralExp()) + { + // #move + /* The !e2x.isCallExp() is because it is already an rvalue + and the move constructor is unnecessary: + struct S { + alias TT this; + long TT(); + this(T)(int x) {} + this(S); + this(ref S); + ~this(); + } + S fun(ref S arg); + void test() { S st; fun(st); } + */ + /* Rewrite as: + * e1 = init, e1.moveCtor(e2); + */ + Expression einit = new BlitExp(exp.loc, exp.e1, getInitExp(sd, exp.loc, sc, t1)); + einit.type = e1x.type; + + Expression e; + e = new DotIdExp(exp.loc, e1x, Id.ctor); + e = new CallExp(exp.loc, e, e2x); + e = new CommaExp(exp.loc, einit, e); + + //printf("e: %s\n", e.toChars()); + + result = e.expressionSemantic(sc); + return; + } else { /* The struct value returned from the function is transferred @@ -10844,13 +10897,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!e2x.implicitConvTo(t1)) { AggregateDeclaration ad2 = isAggregate(e2x.type); - if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type)) + if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(aliasThisStop[1], exp.e2.type)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident); - result = exp.expressionSemantic(sc); + result = exp.expressionSemantic(sc, aliasThisStop); return; } } @@ -10888,7 +10941,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (newExp.newtype && newExp.newtype == t1) { error(exp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", - newExp.toChars(), newExp.type.toChars(), t1.toChars()); + newExp.toErrMsg(), newExp.type.toChars(), t1.toChars()); errorSupplemental(exp.loc, "Perhaps remove the `new` keyword?"); return setError(); } @@ -10905,14 +10958,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = e; return; } - if (search_function(sd, Id.call)) + if (search_function(sd, Id.opCall)) { /* Look for static opCall * https://issues.dlang.org/show_bug.cgi?id=2702 * Rewrite as: * e1 = typeof(e1).opCall(arguments) */ - e2x = typeDotIdExp(e2x.loc, e1x.type, Id.call); + e2x = typeDotIdExp(e2x.loc, e1x.type, Id.opCall); e2x = new CallExp(exp.loc, e2x, exp.e2); e2x = e2x.expressionSemantic(sc); @@ -10929,13 +10982,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else // https://issues.dlang.org/show_bug.cgi?id=11355 { AggregateDeclaration ad2 = isAggregate(e2x.type); - if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(exp.att2, exp.e2.type)) + if (ad2 && ad2.aliasthis && !isRecursiveAliasThis(aliasThisStop[1], exp.e2.type)) { /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ exp.e2 = new DotIdExp(exp.e2.loc, exp.e2, ad2.aliasthis.ident); - result = exp.expressionSemantic(sc); + result = exp.expressionSemantic(sc, aliasThisStop); return; } } @@ -10976,8 +11029,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor ae.e1 = ae.e1.expressionSemantic(sc); ae.e1 = ae.e1.optimize(WANTvalue); ae.e2 = ev; - Expression e = ae.op_overload(sc); - if (e) + if (Expression e = ae.opOverloadAssign(sc, aliasThisStop)) { Expression ey = null; if (t2.ty == Tstruct && sd == t2.toDsymbol(sc)) @@ -11027,14 +11079,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } } - else + else if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } + result = e; + return; } } else @@ -11051,8 +11099,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Disallow assignment operator overloads for same type if (exp.op == EXP.assign && !exp.e2.implicitConvTo(exp.e1.type)) { - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.isAssignExp().opOverloadAssign(sc, aliasThisStop)) { result = e; return; @@ -11072,7 +11119,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * string to match the size of e1. */ Type t2 = e2x.type.toBasetype(); - if (sc.flags & SCOPE.Cfile && e2x.isStringExp() && t2.isTypeSArray()) + if (sc.inCfile && e2x.isStringExp() && t2.isTypeSArray()) { uinteger_t dim1 = t1.isTypeSArray().dim.toInteger(); uinteger_t dim2 = t2.isTypeSArray().dim.toInteger(); @@ -11185,6 +11232,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e2 = e2x; t1 = e1x.type.toBasetype(); } + else if (t1.ty == Taarray) + { + // when assigning a constant, the need for TypeInfo might change + semanticTypeInfo(sc, t1); + } /* Check the mutability of e1. */ if (auto ale = exp.e1.isArrayLengthExp()) @@ -11233,14 +11285,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = id.expressionSemantic(sc); auto arguments = new Expressions(); - arguments.reserve(5); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } arguments.push(ale.e1); arguments.push(exp.e2); @@ -11267,7 +11311,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.op == EXP.assign && !tn.baseElemOf().isAssignable()) { error(exp.loc, "slice `%s` is not mutable, struct `%s` has immutable members", - exp.e1.toChars(), tn.baseElemOf().toChars()); + exp.e1.toErrMsg(), tn.baseElemOf().toChars()); result = ErrorExp.get(); return; } @@ -11290,7 +11334,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tn && !tn.baseElemOf().isAssignable()) { error(exp.loc, "array `%s` is not mutable, struct `%s` has immutable members", - exp.e1.toChars(), tn.baseElemOf().toChars()); + exp.e1.toErrMsg(), tn.baseElemOf().toChars()); result = ErrorExp.get(); return; } @@ -11336,7 +11380,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } else if (exp.e1.op == EXP.slice && - (t2.ty == Tarray || t2.ty == Tsarray) && + t2.isStaticOrDynamicArray() && t2.nextOf().implicitConvTo(t1.nextOf())) { // Check element-wise assignment. @@ -11360,7 +11404,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor uinteger_t dim2 = tsa2.dim.toInteger(); if (dim1 != dim2) { - error(exp.loc, "mismatched array lengths %d and %d for assignment `%s`", cast(int)dim1, cast(int)dim2, exp.toChars()); + error(exp.loc, "mismatched array lengths %d and %d for assignment `%s`", cast(int)dim1, cast(int)dim2, exp.toErrMsg()); return setError(); } } @@ -11412,11 +11456,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else e2x = e2x.implicitCastTo(sc, exp.e1.type); } - if (t1n.toBasetype.ty == Tvoid && t2n.toBasetype.ty == Tvoid) - { - if (sc.setUnsafe(false, exp.loc, "cannot copy `void[]` to `void[]` in `@safe` code")) - return setError(); - } } else { @@ -11433,7 +11472,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e2x.op == EXP.error && exp.op == EXP.construct && t1.ty == Tstruct) { scope sd = (cast(TypeStruct)t1).sym; - Dsymbol opAssign = search_function(sd, Id.assign); + Dsymbol opAssign = search_function(sd, Id.opAssign); // and the struct defines an opAssign if (opAssign) @@ -11441,13 +11480,40 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // offer more information about the cause of the problem errorSupplemental(exp.loc, "`%s` is the first assignment of `%s` therefore it represents its initialization", - exp.toChars(), exp.e1.toChars()); + exp.toErrMsg(), exp.e1.toErrMsg()); errorSupplemental(exp.loc, "`opAssign` methods are not used for initialization, but for subsequent assignments"); } } } } + + if (exp.e1.op == EXP.slice && + t1.isStaticOrDynamicArray() && + t1.nextOf().toBasetype().ty == Tvoid) + { + if (t2.nextOf().implicitConvTo(t1.nextOf())) + { + if (sc.setUnsafe(false, exp.loc, "copying `%s` to `%s`", t2, t1)) + return setError(); + } + else + { + // copying from non-void to void was overlooked, deprecate + if (sc.setUnsafePreview(FeatureState.default_, false, exp.loc, + "copying `%s` to `%s`", t2, t1)) + return setError(); + } + if (sc.previews.fixImmutableConv && !t2.implicitConvTo(t1)) + { + error(exp.loc, "cannot copy `%s` to `%s`", + t2.toChars(), t1.toChars()); + errorSupplemental(exp.loc, + "Source data has incompatible type qualifier(s)"); + errorSupplemental(exp.loc, "Use `cast(%s)` to force copy", t1.toChars()); + return setError(); + } + } if (e2x.op == EXP.error) { result = e2x; @@ -11458,7 +11524,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor /* Look for array operations */ - if ((t2.ty == Tarray || t2.ty == Tsarray) && isArrayOpValid(exp.e2)) + if (t2.isStaticOrDynamicArray() && isArrayOpValid(exp.e2)) { // Look for valid array operations if (exp.memset != MemorySet.blockAssign && @@ -11511,9 +11577,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * `checkAssignExp` expects only AssignExps. */ if (res == exp) // no `AA[k] = v` rewrite was performed - checkAssignEscape(sc, res, false, false); + checkAssignEscape(*sc, res, false, false); else - checkNewEscape(sc, assignElem, false); // assigning to AA puts it on heap + checkNewEscape(*sc, assignElem, false); // assigning to AA puts it on heap if (auto ae = res.isConstructExp()) { @@ -11635,7 +11701,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return ae; const isArrayAssign = (ae.e1.isSliceExp() || ae.e1.type.ty == Tsarray) && - (ae.e2.type.ty == Tsarray || ae.e2.type.ty == Tarray) && + (ae.e2.type.isStaticOrDynamicArray()) && (ae.e1.type.nextOf() && ae.e2.type.nextOf() && ae.e1.type.nextOf.mutableOf.equals(ae.e2.type.nextOf.mutableOf())); const isArraySetAssign = (ae.e1.isSliceExp() || ae.e1.type.ty == Tsarray) && @@ -11675,7 +11741,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // `__assigntmp` will be destroyed together with the array `ae.e1`. // When `ae.e2` is a variadic arg array, it is also `scope`, so // `__assigntmp` may also be scope. - StorageClass stc = STC.nodtor; + STC stc = STC.nodtor; if (isArrayAssign) stc |= STC.rvalue | STC.scope_; @@ -11701,24 +11767,18 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PowAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } - - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } - if (exp.e1.checkReadModifyWrite(exp.op, exp.e2)) + if (exp.suggestOpOpAssign(sc, parent) || + exp.e1.checkReadModifyWrite(exp.op, exp.e2)) return setError(); assert(exp.e1.type && exp.e2.type); - if (exp.e1.op == EXP.slice || exp.e1.type.ty == Tarray || exp.e1.type.ty == Tsarray) + if (exp.e1.op == EXP.slice || exp.e1.type.isStaticOrDynamicArray()) { if (checkNonAssignmentArrayOp(exp.e1)) return setError(); @@ -11738,9 +11798,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // Check element types are arithmetic Type tb1 = exp.e1.type.nextOf().toBasetype(); Type tb2 = exp.e2.type.toBasetype(); - if (tb2.ty == Tarray || tb2.ty == Tsarray) + if (tb2.isStaticOrDynamicArray()) tb2 = tb2.nextOf().toBasetype(); - if ((tb1.isintegral() || tb1.isfloating()) && (tb2.isintegral() || tb2.isfloating())) + if ((tb1.isIntegral() || tb1.isFloating()) && (tb2.isIntegral() || tb2.isFloating())) { exp.type = exp.e1.type; result = arrayOp(exp, sc); @@ -11752,10 +11812,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.modifiableLvalue(sc); } - if ((exp.e1.type.isintegral() || exp.e1.type.isfloating()) && (exp.e2.type.isintegral() || exp.e2.type.isfloating())) + if ((exp.e1.type.isIntegral() || exp.e1.type.isFloating()) && (exp.e2.type.isIntegral() || exp.e2.type.isFloating())) { Expression e0 = null; - e = exp.reorderSettingAAElem(sc); + Expression e = exp.reorderSettingAAElem(sc); e = Expression.extractLast(e, e0); assert(e == exp); @@ -11785,20 +11845,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(CatAssignExp exp) { - if (exp.type) - { - result = exp; - return; - } //printf("CatAssignExp::semantic() %s\n", exp.toChars()); - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinaryAssign(sc, aliasThisStop)) { result = e; return; } + if (exp.suggestOpOpAssign(sc, parent)) + return setError(); + if (SliceExp se = exp.e1.isSliceExp()) { if (se.e1.type.toBasetype().ty == Tsarray) @@ -11833,9 +11890,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor * EXP.concatenateDcharAssign: appending dchar to T[] */ if ((tb1.ty == Tarray) && - (tb2.ty == Tarray || tb2.ty == Tsarray) && + tb2.isStaticOrDynamicArray() && (exp.e2.implicitConvTo(exp.e1.type) || (tb2.nextOf().implicitConvTo(tb1next) && + // Do not strip const(void)[] + (!sc.previews.fixImmutableConv || tb1next.ty != Tvoid) && (tb2.nextOf().size(Loc.initial) == tb1next.size(Loc.initial))))) { // EXP.concatenateAssign @@ -11861,11 +11920,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (tb2.checkPostblit(exp.e2.loc, sc)) return setError(); - if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(*sc, exp.e2, false)) return setError(); - exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, exp.e2.castTo(sc, tb1next)); - exp.e2 = doCopyOrMove(sc, exp.e2); + auto ecast = exp.e2.castTo(sc, tb1next); + if (auto ce = ecast.isCastExp()) + ce.trusted = true; + + exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast); + exp.e2 = doCopyOrMove(sc, exp.e2, null, false); } else if (tb1.ty == Tarray && (tb1next.ty == Tchar || tb1next.ty == Twchar) && @@ -11881,49 +11944,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else { - // Try alias this on first operand - static Expression tryAliasThisForLhs(BinAssignExp exp, Scope* sc) - { - AggregateDeclaration ad1 = isAggregate(exp.e1.type); - if (!ad1 || !ad1.aliasthis) - return null; - - /* Rewrite (e1 op e2) as: - * (e1.aliasthis op e2) - */ - if (isRecursiveAliasThis(exp.att1, exp.e1.type)) - return null; - //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); - Expression e1 = new DotIdExp(exp.loc, exp.e1, ad1.aliasthis.ident); - BinExp be = cast(BinExp)exp.copy(); - be.e1 = e1; - return be.trySemantic(sc); - } - - // Try alias this on second operand - static Expression tryAliasThisForRhs(BinAssignExp exp, Scope* sc) - { - AggregateDeclaration ad2 = isAggregate(exp.e2.type); - if (!ad2 || !ad2.aliasthis) - return null; - /* Rewrite (e1 op e2) as: - * (e1 op e2.aliasthis) - */ - if (isRecursiveAliasThis(exp.att2, exp.e2.type)) - return null; - //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); - Expression e2 = new DotIdExp(exp.loc, exp.e2, ad2.aliasthis.ident); - BinExp be = cast(BinExp)exp.copy(); - be.e2 = e2; - return be.trySemantic(sc); - } - Laliasthis: - result = tryAliasThisForLhs(exp, sc); + result = checkAliasThisForLhs(isAggregate(exp.e1.type), sc, exp, aliasThisStop); if (result) return; - result = tryAliasThisForRhs(exp, sc); + result = checkAliasThisForRhs(isAggregate(exp.e2.type), sc, exp, aliasThisStop); if (result) return; @@ -11938,9 +11964,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto assignElem = exp.e2; auto res = exp.reorderSettingAAElem(sc); if (res != exp) // `AA[k] = v` rewrite was performed - checkNewEscape(sc, assignElem, false); + checkNewEscape(*sc, assignElem, false); else if (exp.op == EXP.concatenateElemAssign || exp.op == EXP.concatenateDcharAssign) - checkAssignEscape(sc, res, false, false); + checkAssignEscape(*sc, res, false, false); result = res; @@ -11976,15 +12002,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotIdExp(exp.loc, id, hook); auto arguments = new Expressions(); - arguments.reserve(5); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } - arguments.push(exp.e1); arguments.push(exp.e2); Expression ce = new CallExp(exp.loc, id, arguments); @@ -12019,15 +12036,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor id = new DotIdExp(exp.loc, id, hook); auto arguments = new Expressions(); - arguments.reserve(5); - if (global.params.tracegc) - { - auto funcname = (sc.callsc && sc.callsc.func) ? sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } - Expression eValue1; Expression value1 = extractSideEffect(sc, "__appendtmp", eValue1, exp.e1); @@ -12075,19 +12083,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("AddExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12113,13 +12110,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (err) return setError(); - if (tb1.ty == Tpointer && exp.e2.type.isintegral() || tb2.ty == Tpointer && exp.e1.type.isintegral()) + if (tb1.ty == Tpointer && exp.e2.type.isIntegral() || tb2.ty == Tpointer && exp.e1.type.isIntegral()) { result = scaleFactor(exp, sc); return; } - if (tb1.ty == Tpointer && tb2.ty == Tpointer) + if (tb1.ty == Tpointer && tb2.ty == Tpointer || + tb1.ty == Tnull && tb2.ty == Tnull) { result = exp.incompatibleTypes(); return; @@ -12132,7 +12130,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { @@ -12143,13 +12141,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + if (exp.suggestBinaryOverloads(sc) || exp.checkArithmeticBin()) + return setError(); + tb1 = exp.e1.type.toBasetype(); if (!target.isVectorOpSupported(tb1, exp.op, tb2)) { result = exp.incompatibleTypes(); return; } - if ((tb1.isreal() && exp.e2.type.isimaginary()) || (tb1.isimaginary() && exp.e2.type.isreal())) + if ((tb1.isReal() && exp.e2.type.isImaginary()) || (tb1.isImaginary() && exp.e2.type.isReal())) { switch (exp.type.toBasetype().ty) { @@ -12181,19 +12182,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("MinExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12216,11 +12206,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { err |= exp.e2.checkArithmetic(exp.op) || exp.e2.checkSharedAccess(sc); } + if (t1.ty == Tnull && t2.ty == Tnull) + { + exp.incompatibleTypes(); + return setError(); + } if (err) return setError(); if (t1.ty == Tpointer) { + Expression e; if (t2.ty == Tpointer) { // https://dlang.org/spec/expression.html#add_expressions @@ -12232,12 +12228,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (!p1.equivalent(p2)) { - // Deprecation to remain for at least a year, after which this should be - // changed to an error // See https://github.com/dlang/dmd/pull/7332 - deprecation(exp.loc, - "cannot subtract pointers to different types: `%s` and `%s`.", + error(exp.loc, "cannot subtract pointers to different types: `%s` and `%s`.", t1.toChars(), t2.toChars()); + return setError(); } // Need to divide the result by the stride @@ -12265,7 +12259,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e.type = Type.tptrdiff_t; } } - else if (t2.isintegral()) + else if (t2.isIntegral()) e = scaleFactor(exp, sc); else { @@ -12289,7 +12283,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { @@ -12300,6 +12294,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + if (exp.suggestBinaryOverloads(sc) || exp.checkArithmeticBin()) + return setError(); + t1 = exp.e1.type.toBasetype(); t2 = exp.e2.type.toBasetype(); if (!target.isVectorOpSupported(t1, exp.op, t2)) @@ -12307,7 +12304,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } - if ((t1.isreal() && t2.isimaginary()) || (t1.isimaginary() && t2.isreal())) + if ((t1.isReal() && t2.isImaginary()) || (t1.isImaginary() && t2.isReal())) { switch (exp.type.ty) { @@ -12360,7 +12357,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return result; } - void handleCatArgument(Expressions *arguments, Expression e, Type catType, bool isRightArg) + void handleCatArgument(Expressions* arguments, Expression e, Type catType, bool isRightArg) { auto tb = e.type.toBasetype(); @@ -12382,7 +12379,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (hook == Id._d_arraycatnTX) arguments.pushSlice((*callExp.arguments)[]); else - arguments.pushSlice((*callExp.arguments)[3 .. $]); + arguments.pushSlice((*callExp.arguments)[0 .. $ - 3]); } } else @@ -12390,15 +12387,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } auto arguments = new Expressions(); - if (useTraceGCHook) - { - auto funcname = (sc.callsc && sc.callsc.func) ? - sc.callsc.func.toPrettyChars() : sc.func.toPrettyChars(); - arguments.push(new StringExp(exp.loc, exp.loc.filename.toDString())); - arguments.push(new IntegerExp(exp.loc, exp.loc.linnum, Type.tint32)); - arguments.push(new StringExp(exp.loc, funcname.toDString())); - } - handleCatArgument(arguments, exp.e1, exp.type.toBasetype(), false); handleCatArgument(arguments, exp.e2, exp.type.toBasetype(), true); @@ -12428,19 +12416,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { // https://dlang.org/spec/expression.html#cat_expressions //printf("CatExp.semantic() %s\n", toChars()); - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -12480,11 +12456,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } // Check for: array ~ element - if ((tb1.ty == Tsarray || tb1.ty == Tarray) && tb2.ty != Tvoid) + if (tb1.isStaticOrDynamicArray() && tb2.ty != Tvoid) { if (exp.e1.op == EXP.arrayLiteral) { - exp.e2 = doCopyOrMove(sc, exp.e2); + exp.e2 = doCopyOrMove(sc, exp.e2, null, false); // https://issues.dlang.org/show_bug.cgi?id=14686 // Postblit call appears in AST, and this is // finally translated to an ArrayLiteralExp in below optimize(). @@ -12511,7 +12487,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e2 = exp.e2.implicitCastTo(sc, tb1next); exp.type = tb1next.arrayOf(); L2elem: - if (checkNewEscape(sc, exp.e2, false)) + if (checkNewEscape(*sc, exp.e2, false)) return setError(); result = exp.optimize(WANTvalue); trySetCatExpLowering(result); @@ -12519,11 +12495,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } } // Check for: element ~ array - if ((tb2.ty == Tsarray || tb2.ty == Tarray) && tb1.ty != Tvoid) + if (tb2.isStaticOrDynamicArray() && tb1.ty != Tvoid) { if (exp.e2.op == EXP.arrayLiteral) { - exp.e1 = doCopyOrMove(sc, exp.e1); + exp.e1 = doCopyOrMove(sc, exp.e1, null, false); } else if (exp.e2.op == EXP.string_) { @@ -12545,7 +12521,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.implicitCastTo(sc, tb2next); exp.type = tb2next.arrayOf(); L1elem: - if (checkNewEscape(sc, exp.e1, false)) + if (checkNewEscape(*sc, exp.e1, false)) return setError(); result = exp.optimize(WANTvalue); trySetCatExpLowering(result); @@ -12554,7 +12530,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Lpeer: - if ((tb1.ty == Tsarray || tb1.ty == Tarray) && (tb2.ty == Tsarray || tb2.ty == Tarray) && (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod)) + if (tb1.isStaticOrDynamicArray() && tb2.isStaticOrDynamicArray() && + (tb1next.mod || tb2next.mod) && (tb1next.mod != tb2next.mod)) { Type t1 = tb1next.mutableOf().constOf().arrayOf(); Type t2 = tb2next.mutableOf().constOf().arrayOf(); @@ -12581,7 +12558,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = tb.nextOf().arrayOf(); if (exp.type.ty == Tarray && tb1next && tb2next && tb1next.mod != tb2next.mod) { - exp.type = exp.type.nextOf().toHeadMutable().arrayOf(); + // Do not strip const(void)[] + if (!sc.previews.fixImmutableConv || tb.nextOf().ty != Tvoid) + exp.type = exp.type.nextOf().toHeadMutable().arrayOf(); } if (Type tbn = tb.nextOf()) { @@ -12590,8 +12569,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type t1 = exp.e1.type.toBasetype(); Type t2 = exp.e2.type.toBasetype(); - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) + Expression e; + if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) { // Normalize to ArrayLiteralExp or StringExp as far as possible e = exp.optimize(WANTvalue); @@ -12607,67 +12586,64 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor trySetCatExpLowering(result); } - override void visit(MulExp exp) + bool commonArithBinOpSemantic(BinExp exp) { - version (none) - { - printf("MulExp::semantic() %s\n", exp.toChars()); - } - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; - return; + return true; } if (Expression ex = typeCombine(exp, sc)) { result = ex; - return; + return true; } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { result = arrayOpInvalidError(exp); - return; + return true; } result = exp; - return; + return true; } - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); + if (exp.suggestBinaryOverloads(sc) || exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) + { + setError(); + return true; + } + return false; + } + override void visit(MulExp exp) + { + version (none) + { + printf("MulExp::semantic() %s\n", exp.toChars()); + } - if (exp.type.isfloating()) + if (commonArithBinOpSemantic(exp)) + return; + if (exp.type.isFloating()) { Type t1 = exp.e1.type; Type t2 = exp.e2.type; - if (t1.isreal()) + if (t1.isReal()) { exp.type = t2; } - else if (t2.isreal()) + else if (t2.isReal()) { exp.type = t1; } - else if (t1.isimaginary()) + else if (t1.isImaginary()) { - if (t2.isimaginary()) + if (t2.isImaginary()) { switch (t1.toBasetype().ty) { @@ -12690,7 +12666,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // iy * iv = -yv exp.e1.type = exp.type; exp.e2.type = exp.type; - e = new NegExp(exp.loc, exp); + Expression e = new NegExp(exp.loc, exp); e = e.expressionSemantic(sc); result = e; return; @@ -12698,12 +12674,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else exp.type = t2; // t2 is complex } - else if (t2.isimaginary()) + else if (t2.isImaginary()) { exp.type = t1; // t1 is complex } } - else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + else if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; @@ -12713,70 +12689,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(DivExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; + if (commonArithBinOpSemantic(exp)) return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - if (exp.type.isfloating()) + if (exp.type.isFloating()) { Type t1 = exp.e1.type; Type t2 = exp.e2.type; - if (t1.isreal()) + if (t1.isReal()) { exp.type = t2; - if (t2.isimaginary()) + if (t2.isImaginary()) { // x/iv = i(-x/v) exp.e2.type = t1; - e = new NegExp(exp.loc, exp); + Expression e = new NegExp(exp.loc, exp); e = e.expressionSemantic(sc); result = e; return; } } - else if (t2.isreal()) + else if (t2.isReal()) { exp.type = t1; } - else if (t1.isimaginary()) + else if (t1.isImaginary()) { - if (t2.isimaginary()) + if (t2.isImaginary()) { switch (t1.toBasetype().ty) { @@ -12799,12 +12739,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor else exp.type = t2; // t2 is complex } - else if (t2.isimaginary()) + else if (t2.isImaginary()) { exp.type = t1; // t1 is complex } } - else if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + else if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; @@ -12814,54 +12754,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(ModExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; + if (commonArithBinOpSemantic(exp)) return; - } - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; } - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (exp.type.isfloating()) + if (exp.type.isFloating()) { exp.type = exp.e1.type; - if (exp.e2.type.iscomplex()) + if (exp.e2.type.isComplex()) { error(exp.loc, "cannot perform modulo complex arithmetic"); return setError(); @@ -12872,54 +12777,17 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(PowExp exp) { - if (exp.type) - { - result = exp; + if (commonArithBinOpSemantic(exp)) return; - } - //printf("PowExp::semantic() %s\n", toChars()); - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - - if (exp.checkArithmeticBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) + if (!target.isVectorOpSupported(exp.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) { result = exp.incompatibleTypes(); return; } // First, attempt to fold the expression. - e = exp.optimize(WANTvalue); + Expression e = exp.optimize(WANTvalue); if (e.op != EXP.pow) { e = e.expressionSemantic(sc); @@ -12930,7 +12798,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Module mmath = Module.loadStdMath(); if (!mmath) { - error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toChars()); + error(e.loc, "`%s` requires `std.math` for `^^` operators", e.toErrMsg()); return setError(); } e = new ScopeExp(exp.loc, mmath); @@ -12950,28 +12818,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } - override void visit(ShlExp exp) + private void visitShift(BinExp exp) { - //printf("ShlExp::semantic(), type = %p\n", type); - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) + if (exp.suggestBinaryOverloads(sc) || exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) return setError(); if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) @@ -12979,101 +12834,34 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } + exp.e1 = integralPromotions(exp.e1, sc); if (exp.e2.type.toBasetype().ty != Tvector) - exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt); + { + Type tb1 = exp.e1.type.toBasetype(); + exp.e2 = exp.e2.castTo(sc, tb1.ty == Tvector ? tb1 : Type.tshiftcnt); + } exp.type = exp.e1.type; result = exp; } + override void visit(ShlExp exp) + { + visitShift(exp); + } override void visit(ShrExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - exp.e1 = integralPromotions(exp.e1, sc); - if (exp.e2.type.toBasetype().ty != Tvector) - exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt); - - exp.type = exp.e1.type; - result = exp; + visitShift(exp); } - override void visit(UshrExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - if (!target.isVectorOpSupported(exp.e1.type.toBasetype(), exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - exp.e1 = integralPromotions(exp.e1, sc); - if (exp.e2.type.toBasetype().ty != Tvector) - exp.e2 = exp.e2.castTo(sc, Type.tshiftcnt); - - exp.type = exp.e1.type; - result = exp; + visitShift(exp); } - override void visit(AndExp exp) + private void visitBinaryBitOp(BinExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -13093,7 +12881,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) + if (tb.isStaticOrDynamicArray()) { if (!isArrayOpValid(exp)) { @@ -13108,120 +12896,24 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor result = exp.incompatibleTypes(); return; } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) + + if (exp.suggestBinaryOverloads(sc) || exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) return setError(); result = exp; } + override void visit(AndExp exp) + { + visitBinaryBitOp(exp); + } override void visit(OrExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool) - { - exp.type = exp.e1.type; - result = exp; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - result = exp; + visitBinaryBitOp(exp); } - override void visit(XorExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) - { - result = e; - return; - } - - if (exp.e1.type.toBasetype().ty == Tbool && exp.e2.type.toBasetype().ty == Tbool) - { - exp.type = exp.e1.type; - result = exp; - return; - } - - if (Expression ex = typeCombine(exp, sc)) - { - result = ex; - return; - } - - Type tb = exp.type.toBasetype(); - if (tb.ty == Tarray || tb.ty == Tsarray) - { - if (!isArrayOpValid(exp)) - { - result = arrayOpInvalidError(exp); - return; - } - result = exp; - return; - } - if (!target.isVectorOpSupported(tb, exp.op, exp.e2.type.toBasetype())) - { - result = exp.incompatibleTypes(); - return; - } - if (exp.checkIntegralBin() || exp.checkSharedAccessBin(sc)) - return setError(); - - result = exp; + visitBinaryBitOp(exp); } override void visit(LogicalExp exp) @@ -13231,11 +12923,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("LogicalExp::semantic() %s\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13248,14 +12935,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor e1x = resolveProperties(sc, e1x); e1x = e1x.toBoolean(sc); - if (sc.flags & SCOPE.condition) + if (sc.condition) { /* If in static if, don't evaluate e2 if we don't have to. */ e1x = e1x.optimize(WANTvalue); if (e1x.toBool().hasValue(exp.op == EXP.orOr)) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) result = new IntegerExp(exp.op == EXP.orOr); else result = IntegerExp.createBool(exp.op == EXP.orOr); @@ -13285,7 +12972,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e2x.op == EXP.type || e2x.op == EXP.scope_) { - error(exp.loc, "`%s` is not an expression", exp.e2.toChars()); + error(exp.loc, "`%s` is not an expression", exp.e2.toErrMsg()); return setError(); } if (e1x.op == EXP.error || e1x.type.ty == Tnoreturn) @@ -13303,7 +12990,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (e2x.type.ty == Tvoid) exp.type = Type.tvoid; else - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; exp.e1 = e1x; exp.e2 = e2x; @@ -13317,11 +13004,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CmpExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13338,57 +13020,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - - EXP cmpop = exp.op; - if (auto e = exp.op_overload(sc, &cmpop)) + if (auto e = exp.opOverloadCmp(sc, aliasThisStop)) { - if (!e.type.isscalar() && e.type.equals(exp.e1.type)) - { - error(exp.loc, "recursive `opCmp` expansion"); - return setError(); - } - if (e.op == EXP.call) - { - - if (t1.ty == Tclass && t2.ty == Tclass) - { - // Lower to object.__cmp(e1, e2) - Expression cl = new IdentifierExp(exp.loc, Id.empty); - cl = new DotIdExp(exp.loc, cl, Id.object); - cl = new DotIdExp(exp.loc, cl, Id.__cmp); - cl = cl.expressionSemantic(sc); - - auto arguments = new Expressions(); - // Check if op_overload found a better match by calling e2.opCmp(e1) - // If the operands were swapped, then the result must be reversed - // e1.opCmp(e2) == -e2.opCmp(e1) - // cmpop takes care of this - if (exp.op == cmpop) - { - arguments.push(exp.e1); - arguments.push(exp.e2); - } - else - { - // Use better match found by op_overload - arguments.push(exp.e2); - arguments.push(exp.e1); - } - - cl = new CallExp(exp.loc, cl, arguments); - cl = new CmpExp(cmpop, exp.loc, cl, new IntegerExp(0)); - result = cl.expressionSemantic(sc); - return; - } - - e = new CmpExp(cmpop, exp.loc, e, IntegerExp.literal!0); - e = e.expressionSemantic(sc); - } result = e; return; } - if (Expression ex = typeCombine(exp, sc)) { result = ex; @@ -13400,13 +13037,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (f1 || f2) return setError(); - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; // Special handling for array comparisons Expression arrayLowering = null; t1 = exp.e1.type.toBasetype(); t2 = exp.e2.type.toBasetype(); - if ((t1.ty == Tarray || t1.ty == Tsarray || t1.ty == Tpointer) && (t2.ty == Tarray || t2.ty == Tsarray || t2.ty == Tpointer)) + if ((t1.isStaticOrDynamicArray() || t1.ty == Tpointer) && (t2.isStaticOrDynamicArray() || t2.ty == Tpointer)) { Type t1next = t1.nextOf(); Type t2next = t2.nextOf(); @@ -13416,8 +13053,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) + if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) { if (!verifyHookExist(exp.loc, *sc, Id.__cmp, "comparing arrays")) return setError(); @@ -13438,19 +13074,21 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor arrayLowering = al; } } - else if (t1.ty == Tstruct || t2.ty == Tstruct || (t1.ty == Tclass && t2.ty == Tclass)) + else if (t1.isTypeClass() && t2.isTypeClass()) { - if (t2.ty == Tstruct) - error(exp.loc, "need member function `opCmp()` for %s `%s` to compare", t2.toDsymbol(sc).kind(), t2.toChars()); - else - error(exp.loc, "need member function `opCmp()` for %s `%s` to compare", t1.toDsymbol(sc).kind(), t1.toChars()); + error(exp.loc, "need member function `opCmp()` for %s `%s` to compare", t1.toDsymbol(sc).kind(), t1.toChars()); return setError(); } - else if (t1.iscomplex() || t2.iscomplex()) + else if (t1.isComplex() || t2.isComplex()) { error(exp.loc, "compare not defined for complex operands"); return setError(); } + else if (t1.isTypeFunction() || t2.isTypeFunction()) + { + error(exp.loc, "comparison is not defined for function types"); + return setError(); + } else if (t1.ty == Taarray || t2.ty == Taarray) { error(exp.loc, "`%s` is not defined for associative arrays", EXPtoString(exp.op).ptr); @@ -13486,19 +13124,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(InExp exp) { - if (exp.type) - { - result = exp; - return; - } - - if (Expression ex = binSemanticProp(exp, sc)) - { - result = ex; - return; - } - Expression e = exp.op_overload(sc); - if (e) + if (Expression e = exp.opOverloadBinary(sc, aliasThisStop)) { result = e; return; @@ -13518,7 +13144,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.e1 = exp.e1.implicitCastTo(sc, ta.index); } - semanticTypeInfo(sc, ta.index); + // even though the glue layer only needs the type info of the index, + // this might be the first time an AA literal is accessed, so check + // the full type info + semanticTypeInfo(sc, ta); // Return type is pointer to value exp.type = ta.nextOf().pointerTo(); @@ -13529,15 +13158,15 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); case Tarray, Tsarray: - result = exp.incompatibleTypes(); + result = exp.incompatibleTypes(sc); errorSupplemental(exp.loc, "`in` is only allowed on associative arrays"); const(char)* slice = (t2b.ty == Tsarray) ? "[]" : ""; errorSupplemental(exp.loc, "perhaps use `std.algorithm.find(%s, %s%s)` instead", - exp.e1.toChars(), exp.e2.toChars(), slice); + exp.e1.toErrMsg(), exp.e2.toErrMsg(), slice); return; default: - result = exp.incompatibleTypes(); + result = exp.incompatibleTypes(sc); return; } result = exp; @@ -13556,11 +13185,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(EqualExp exp) { //printf("EqualExp::semantic('%s')\n", exp.toChars()); - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13667,15 +13291,13 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return false; } - if (auto e = exp.op_overload(sc)) + if (auto e = exp.opOverloadEqual(sc, aliasThisStop)) { result = e; return; } - - const isArrayComparison = (t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray); + const isArrayComparison = t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray(); const needsArrayLowering = isArrayComparison && needsDirectEq(t1, t2, sc); if (!needsArrayLowering) @@ -13695,11 +13317,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (f1 || f2) return setError(); - exp.type = (sc && sc.flags & SCOPE.Cfile) ? Type.tint32 : Type.tbool; + exp.type = (sc && sc.inCfile) ? Type.tint32 : Type.tbool; if (!isArrayComparison) { - if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating()) + if (exp.e1.type != exp.e2.type && exp.e1.type.isFloating() && exp.e2.type.isFloating()) { // Cast both to complex exp.e1 = exp.e1.castTo(sc, Type.tcomplex80); @@ -13762,8 +13384,9 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } if (exp.e1.type.toBasetype().ty == Taarray) + { semanticTypeInfo(sc, exp.e1.type.toBasetype()); - + } if (!target.isVectorOpSupported(t1, exp.op, t2)) { @@ -13771,6 +13394,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + if (t1.isTypeFunction() || t2.isTypeFunction()) + { + error(exp.loc, "operator `==` is not defined for function types"); + return setError(); + } + if (auto tv = t1.isTypeVector()) exp.type = tv.toBooleanVector(); @@ -13779,11 +13408,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor override void visit(IdentityExp exp) { - if (exp.type) - { - result = exp; - return; - } exp.setNoderefOperands(); @@ -13812,7 +13436,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor exp.type = Type.tbool; - if (exp.e1.type != exp.e2.type && exp.e1.type.isfloating() && exp.e2.type.isfloating()) + if (exp.e1.type != exp.e2.type && exp.e1.type.isFloating() && exp.e2.type.isFloating()) { // Cast both to complex exp.e1 = exp.e1.castTo(sc, Type.tcomplex80); @@ -13832,6 +13456,12 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor if (exp.e2.op == EXP.call) exp.e2 = (cast(CallExp)exp.e2).addDtorHook(sc); + if (exp.e1.type.isTypeFunction() || exp.e2.type.isTypeFunction()) + { + error(exp.loc, "operator `is` is not defined for function types"); + return setError(); + } + if (exp.e1.type.toBasetype().ty == Tsarray || exp.e2.type.toBasetype().ty == Tsarray) deprecation(exp.loc, "identity comparison of static arrays " @@ -13847,11 +13477,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("CondExp::semantic('%s')\n", exp.toChars()); } - if (exp.type) - { - result = exp; - return; - } if (auto die = exp.econd.isDotIdExp()) die.noderef = true; @@ -13911,7 +13536,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=23767 // `cast(void*) 0` should be treated as `null` so the ternary expression // gets the pointer type of the other branch - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { static void rewriteCNull(ref Expression e, ref Type t) { @@ -14143,7 +13768,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor Expression trySemantic(Expression exp, Scope* sc) { //printf("+trySemantic(%s)\n", exp.toChars()); - uint errors = global.startGagging(); + const errors = global.startGagging(); Expression e = expressionSemantic(exp, sc); if (global.endGagging(errors)) { @@ -14153,6 +13778,32 @@ Expression trySemantic(Expression exp, Scope* sc) return e; } +/********************************** + * Try expression semantic on `exp`, gagging semantic errors, + * but don't resolve alias this on a BinExp when the lhs or rhs + * has the corresponding type in `aliasThisStop` (See `isRecursiveAliasThis`). + * + * Params: + * exp = expression to try semantic on + * sc = scope + * aliasThisStop = pair of recursive alias this types to stop endless recursion + * Returns: + * exp after expression semantic, or `null` on error + */ +Expression trySemanticAliasThis(Expression exp, Scope* sc, Type[2] aliasThisStop) +{ + if (exp.expressionSemanticDone) + return exp; + + const errors = global.startGagging(); + Expression e = expressionSemantic(exp, sc, aliasThisStop); + + if (global.endGagging(errors)) + return null; + + return e; +} + /************************** * Helper function for easy error propagation. * If error occurs, returns ErrorExp. Otherwise returns NULL. @@ -14213,26 +13864,68 @@ Expression binSemanticProp(BinExp e, Scope* sc) return null; } +/// Returns: whether expressionSemantic() has been run on expression `e` +private bool expressionSemanticDone(Expression e) +{ + // Usually, Expression.type gets set by expressionSemantic and is `null` beforehand + // There are some exceptions however: + return e.type !is null && !( + e.isRealExp() // type sometimes gets set already before semantic + || e.isTypeExp() // stores its type in the Expression.type field + || e.isCompoundLiteralExp() // stores its `(type) {}` in type field, gets rewritten to struct literal + || e.isVarExp() // type sometimes gets set already before semantic + ); +} + // entrypoint for semantic ExpressionSemanticVisitor Expression expressionSemantic(Expression e, Scope* sc) { + if (e.expressionSemanticDone) + return e; + scope v = new ExpressionSemanticVisitor(sc); e.accept(v); return v.result; } +// ditto, but passes alias this stop types, see trySemanticAliasThis +private Expression expressionSemantic(Expression e, Scope* sc, Type[2] aliasThisStop) +{ + if (e.expressionSemanticDone) + return e; + + scope v = new ExpressionSemanticVisitor(sc); + v.aliasThisStop = aliasThisStop; + e.accept(v); + return v.result; +} + +// ditto, but with `parent` parameter that represents the expression before rewriting. +// This way, when lowering an expression (e.g. i++ to i+=1), error messages can still +// refer to the original expression. +private Expression expressionSemanticWithParent(Expression e, Scope* sc, Expression parent) +{ + if (e.expressionSemanticDone) + return e; + + scope v = new ExpressionSemanticVisitor(sc); + v.parent = parent; + e.accept(v); + return v.result; +} + private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) { - //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars()); + //printf("dotIdSemanticPropX() %s\n", toChars(exp)); if (Expression ex = unaSemantic(exp, sc)) return ex; - if (!(sc.flags & SCOPE.Cfile) && exp.ident == Id._mangleof) + if (!sc.inCfile && exp.ident == Id._mangleof) { // symbol.mangleof // return mangleof as an Expression - static Expression dotMangleof(const ref Loc loc, Scope* sc, Dsymbol ds, bool hasOverloads) + static Expression dotMangleof(Loc loc, Scope* sc, Dsymbol ds, bool hasOverloads) { Expression e; @@ -14301,14 +13994,16 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) if (auto te = exp.e1.isTupleExp()) { - if (exp.ident == Id.offsetof) + if (exp.ident == Id.offsetof || + exp.ident == Id.bitoffsetof || + exp.ident == Id.bitwidth) { /* 'distribute' the .offsetof to each of the tuple elements. */ auto exps = new Expressions(te.exps.length); foreach (i, e; (*te.exps)[]) { - (*exps)[i] = new DotIdExp(e.loc, e, Id.offsetof); + (*exps)[i] = new DotIdExp(e.loc, e, exp.ident); } // Don't evaluate te.e0 in runtime Expression e = new TupleExp(exp.loc, null, exps); @@ -14326,26 +14021,18 @@ private Expression dotIdSemanticPropX(DotIdExp exp, Scope* sc) // Template has no built-in properties except for 'stringof'. if ((exp.e1.isDotTemplateExp() || exp.e1.isTemplateExp()) && exp.ident != Id.stringof) { - error(exp.loc, "template `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars()); + error(exp.loc, "template `%s` does not have property `%s`", exp.e1.toErrMsg(), exp.ident.toChars()); return ErrorExp.get(); } if (!exp.e1.type) { - error(exp.loc, "expression `%s` does not have property `%s`", exp.e1.toChars(), exp.ident.toChars()); + error(exp.loc, "expression `%s` does not have property `%s`", exp.e1.toErrMsg(), exp.ident.toChars()); return ErrorExp.get(); } return exp; } -private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc) -{ - if (auto d = s.isDeclaration()) - return d.checkDisabled(loc, sc); - - return false; -} - /****************************** * Resolve properties, i.e. `e1.ident`, without seeing UFCS. * Params: @@ -14357,11 +14044,11 @@ private bool checkDisabled(Dsymbol s, ref Loc loc, Scope* sc) */ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) { - //printf("DotIdExp::semanticY(this = %p, '%s')\n", exp, exp.toChars()); + //printf("dotIdSemanticProp('%s')\n", exp.toChars()); //{ static int z; fflush(stdout); if (++z == 10) *(char*)0=0; } - const cfile = (sc.flags & SCOPE.Cfile) != 0; + const cfile = sc.inCfile; /* Special case: rewrite this.id and super.id * to be classtype.id and baseclasstype.id @@ -14416,13 +14103,13 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) */ if (ie.sds.isModule() && ie.sds != sc._module) flags |= SearchOpt.ignorePrivateImports; - if (sc.flags & SCOPE.ignoresymbolvisibility) + if (sc.ignoresymbolvisibility) flags |= SearchOpt.ignoreVisibility; Dsymbol s = ie.sds.search(exp.loc, exp.ident, flags); /* Check for visibility before resolving aliases because public * aliases to private symbols are public. */ - if (s && !(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc._module, s)) + if (s && !sc.ignoresymbolvisibility && !symbolIsVisible(sc._module, s)) { s = null; } @@ -14440,7 +14127,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) s = s.toAlias(); s.checkDeprecated(exp.loc, sc); - s.checkDisabled(exp.loc, sc); + if (auto d = s.isDeclaration()) + d.checkDisabled(exp.loc, sc); if (auto em = s.isEnumMember()) { @@ -14631,6 +14319,8 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) !cfile && (exp.ident == Id._mangleof || exp.ident == Id.offsetof || + exp.ident == Id.bitoffsetof || + exp.ident == Id.bitwidth || exp.ident == Id._init || exp.ident == Id.stringof) )) @@ -14692,7 +14382,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag) const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag); - Expression e = exp.e1.type.dotExp(sc, exp.e1, exp.ident, flag); + Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag); if (e) { e = e.expressionSemantic(sc); @@ -14735,11 +14425,18 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g auto die = new DotIdExp(exp.loc, e1, exp.ti.name); Expression e = die.dotIdSemanticPropX(sc); + + Expression notTemplate() + { + error(exp.loc, "`%s` isn't a template", e.toErrMsg()); + return errorExp(); + } + if (e == die) { exp.e1 = die.e1; // take back Type t1b = exp.e1.type.toBasetype(); - if (t1b.ty == Tarray || t1b.ty == Tsarray || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid)) + if (t1b.isStaticOrDynamicArray() || t1b.ty == Taarray || t1b.ty == Tnull || (t1b.isTypeBasic() && t1b.ty != Tvoid)) { /* No built-in type has templatized properties, so do shortcut. * It is necessary in: 1024.max!"a < b" @@ -14779,7 +14476,7 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g exp.e1 = dve.e1; // pull semantic() result if (!exp.findTempDecl(sc)) - goto Lerr; + return notTemplate(); if (exp.ti.needsTypeInference(sc)) return exp; exp.ti.dsymbolSemantic(sc); @@ -14875,9 +14572,7 @@ Expression dotTemplateSemanticProp(DotTemplateInstanceExp exp, Scope* sc, bool g .expressionSemantic(sc); } -Lerr: - error(exp.loc, "`%s` isn't a template", e.toChars()); - return errorExp(); + return notTemplate(); } MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink eSink) @@ -14997,14 +14692,14 @@ MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink convertMatch = true; auto tfy = new TypeFunction(tfx.parameterList, tof.next, - tfx.linkage, STC.undefined_); + tfx.linkage, STC.none); tfy.mod = tfx.mod; tfy.trust = tfx.trust; - tfy.isnothrow = tfx.isnothrow; - tfy.isnogc = tfx.isnogc; + tfy.isNothrow = tfx.isNothrow; + tfy.isNogc = tfx.isNogc; tfy.purity = tfx.purity; - tfy.isproperty = tfx.isproperty; - tfy.isref = tfx.isref; + tfy.isProperty = tfx.isProperty; + tfy.isRef = tfx.isRef; tfy.isInOutParam = tfx.isInOutParam; tfy.isInOutQual = tfx.isInOutQual; tfy.deco = tfy.merge().deco; @@ -15048,11 +14743,115 @@ MATCH matchType(FuncExp funcExp, Type to, Scope* sc, FuncExp* presult, ErrorSink { auto ts = toAutoQualChars(tx, to); eSink.error(loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", - funcExp.toChars(), ts[0], ts[1]); + funcExp.toErrMsg(), ts[0], ts[1]); } return m; } +private bool checkScalar(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isScalar()) + { + error(e.loc, "`%s` is not a scalar, it is a `%s`", e.toErrMsg(), e.type.toChars()); + return true; + } + return e.checkValue(); +} + +private bool checkNoBool(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (e.type.toBasetype().ty == Tbool) + { + error(e.loc, "operation not allowed on `bool` `%s`", e.toErrMsg()); + return true; + } + return false; +} + +private bool checkIntegral(Expression e) +{ + if (e.op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isIntegral()) + { + error(e.loc, "`%s` is not of integral type, it is a `%s`", e.toErrMsg(), e.type.toChars()); + return true; + } + return e.checkValue(); +} + +private bool checkArithmetic(Expression e, EXP op) +{ + if (op == EXP.error) + return true; + if (e.type.toBasetype().ty == Terror) + return true; + if (!e.type.isIntegral() && !e.type.isFloating()) + { + // unary aggregate ops error here + const char* msg = e.type.isAggregate() ? + "operator `%s` is not defined for `%s` of type `%s`" : + "illegal operator `%s` for `%s` of type `%s`"; + error(e.loc, msg, EXPtoString(op).ptr, e.toErrMsg(), e.type.toChars()); + return true; + } + + if ((op == EXP.add || op == EXP.min) && e.isTypeExp()) + { + // @@@DEPRECATED_2.121@@@ + // Deprecated in 2.111 + // In 2.121, remove this branch to let `checkValue` raise the error + deprecation(e.loc, "type `%s` has no value", e.toChars); + if (!e.type.isOpaqueType) + deprecationSupplemental(e.loc, "perhaps use `%s.init`", e.toChars); + return false; + } + + return e.checkValue(); +} + +/******************************* + * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. + * ex is the RHS expression, or NULL if ++/-- is used (for diagnostics) + * Returns true if error occurs. + */ +private bool checkReadModifyWrite(Expression e, EXP rmwOp, Expression ex = null) +{ + //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex.toChars() : ""); + if (!e.type || !e.type.isShared() || e.type.isTypeStruct() || e.type.isTypeClass()) + return false; + + // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. + switch (rmwOp) + { + case EXP.plusPlus: + case EXP.prePlusPlus: + rmwOp = EXP.addAssign; + break; + case EXP.minusMinus: + case EXP.preMinusMinus: + rmwOp = EXP.minAssign; + break; + default: + break; + } + + error(e.loc, "read-modify-write operations are not allowed for `shared` variables"); + errorSupplemental(e.loc, "Use `core.atomic.atomicOp!\"%s\"(%s, %s)` instead", + EXPtoString(rmwOp).ptr, e.toChars(), ex ? ex.toChars() : "1"); + return true; +} + private bool checkSharedAccessBin(BinExp binExp, Scope* sc) { const r1 = binExp.e1.checkSharedAccess(sc); @@ -15060,6 +14859,89 @@ private bool checkSharedAccessBin(BinExp binExp, Scope* sc) return (r1 || r2); } +private bool checkIntegralBin(BinExp e) +{ + bool r1 = e.e1.checkIntegral(); + bool r2 = e.e2.checkIntegral(); + return (r1 || r2); +} + +private bool checkArithmeticBin(BinExp e) +{ + return (e.e1.checkArithmetic(e.op) || e.e2.checkArithmetic(e.op)); +} + +/**************************************** + * Check that the expression has a valid value. + * If not, generates an error "... has no value".` + * + * Params: + * e = expression to check + * + * Returns: + * `true` if the expression is not valid or has `void` type. + */ +bool checkValue(Expression e) +{ + if (auto te = e.isTypeExp()) + { + error(e.loc, "type `%s` has no value", e.toErrMsg()); + if (!e.type.isOpaqueType) + errorSupplemental(e.loc, "perhaps use `%s.init`", e.toChars()); + return true; + } + + if (auto dtie = e.isDotTemplateInstanceExp()) + { + if (dtie.ti.tempdecl && + dtie.ti.semantictiargsdone && + dtie.ti.semanticRun == PASS.initial) + + error(e.loc, "partial %s `%s` has no value", dtie.ti.kind(), e.toErrMsg()); + else + error(e.loc, "%s `%s` has no value", dtie.ti.kind(), dtie.ti.toChars()); + return true; + } + + if (auto se = e.isScopeExp()) + { + error(e.loc, "%s `%s` has no value", se.sds.kind(), se.sds.toChars()); + return true; + } + + if (auto te = e.isTemplateExp()) + { + error(e.loc, "%s `%s` has no value", te.td.kind(), te.toChars()); + return true; + } + + if (auto fe = e.isFuncExp()) + { + if (fe.td) + { + error(e.loc, "template lambda has no value"); + return true; + } + return false; + } + + if (auto dte = e.isDotTemplateExp()) + { + error(e.loc, "%s `%s` has no value", dte.td.kind(), e.toErrMsg()); + return true; + } + + if (e.type && e.type.toBasetype().ty == Tvoid) + { + error(e.loc, "expression `%s` is `void` and has no value", e.toErrMsg()); + //print(); assert(0); + if (!global.gag) + e.type = Type.terror; + return true; + } + return false; +} + /*************************************** * If expression is shared, check that we can access it. * Give error message if not. @@ -15075,13 +14957,34 @@ private bool checkSharedAccessBin(BinExp binExp, Scope* sc) */ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) { - if (global.params.noSharedAccess != FeatureState.enabled || - !sc || + if (!sc || + !sc.previews.noSharedAccess || sc.intypeof || - sc.flags & SCOPE.ctfe) + sc.ctfe) { return false; } + else if (sc._module.ident == Id.atomic && sc._module.parent !is null) + { + // Allow core.internal.atomic, it is an compiler implementation for a given platform module. + // It is then exposed by other modules such as core.atomic and core.stdc.atomic. + // This is available as long as druntime is on the import path and the platform supports that operation. + + // https://issues.dlang.org/show_bug.cgi?id=24846 + + Package parent = sc._module.parent.isPackage(); + if (parent !is null) + { + // This can be easily converted over to apply to core.atomic and core.internal.atomic + if (parent.ident == Id.internal) + { + parent = parent.parent.isPackage(); + + if (parent !is null && parent.ident == Id.core && parent.parent is null) + return false; + } + } + } //printf("checkSharedAccess() `%s` returnRef: %d\n", e.toChars(), returnRef); @@ -15090,7 +14993,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) bool sharedError(Expression e) { // https://dlang.org/phobos/core_atomic.html - error(e.loc, "direct access to shared `%s` is not allowed, see `core.atomic`", e.toChars()); + error(e.loc, "direct access to shared `%s` is not allowed, see `core.atomic`", e.toErrMsg()); return true; } @@ -15106,6 +15009,8 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) bool visitNew(NewExp e) { + if (e.placement) + check(e.placement, false); if (e.thisexp) check(e.thisexp, false); return false; @@ -15126,10 +15031,8 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) { if (e.var.isThisDeclaration()) return false; - else - return sharedError(e); } - else if (!allowRef && e.var.type.isShared()) + if (!allowRef && e.var.type.isShared()) return sharedError(e); return false; @@ -15233,7 +15136,7 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) /**************************************** * Resolve __FILE__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __FILE_FULL_PATH__ to loc. */ -Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) +Expression resolveLoc(Expression exp, Loc loc, Scope* sc) { // Don't replace the special keywords, while we are inside a default // argument. They are replaced later when copied to the call site. @@ -15277,6 +15180,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitStructLiteral(StructLiteralExp exp) { + if (!exp.elements) + return exp; + foreach (ref element; *exp.elements) { if (element) @@ -15288,6 +15194,8 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitNew(NewExp exp) { + if (exp.placement) + exp.placement = exp.placement.resolveLoc(loc, sc); if (exp.thisexp) exp.thisexp = exp.thisexp.resolveLoc(loc, sc); if (exp.argprefix) @@ -15295,6 +15203,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) if (exp.lowering) exp.lowering = exp.lowering.resolveLoc(loc, sc); + if (!exp.arguments) + return exp; + foreach (ref element; *exp.arguments) { if (element) @@ -15306,6 +15217,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitCall(CallExp exp) { + if (!exp.arguments) + return exp; + foreach (ref element; *exp.arguments) { if (element) @@ -15319,6 +15233,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) { exp.e1 = exp.e1.resolveLoc(loc, sc); + if (!exp.arguments) + return exp; + foreach (ref element; *exp.arguments) { if (element) @@ -15331,8 +15248,10 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitSlice(SliceExp exp) { exp.e1 = exp.e1.resolveLoc(loc, sc); - exp.lwr = exp.lwr.resolveLoc(loc, sc); - exp.upr = exp.upr.resolveLoc(loc, sc); + if (exp.lwr) + exp.lwr = exp.lwr.resolveLoc(loc, sc); + if (exp.upr) + exp.upr = exp.upr.resolveLoc(loc, sc); return exp; } @@ -15350,6 +15269,9 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) if (exp.basis) exp.basis = exp.basis.resolveLoc(loc, sc); + if (!exp.elements) + return exp; + foreach (ref element; *exp.elements) { if (element) @@ -15469,6 +15391,7 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) */ Expression addDtorHook(Expression e, Scope* sc) { + //printf("addDtorHook() %s\n", toChars(e)); Expression visit(Expression exp) { return exp; @@ -15494,7 +15417,7 @@ Expression addDtorHook(Expression e, Scope* sc) buf[0 .. prefix.length] = prefix; buf[prefix.length .. len] = ident[0 .. len - prefix.length]; - auto tmp = copyToTemp(0, buf[0 .. len], exp); + auto tmp = copyToTemp(STC.none, buf[0 .. len], exp); Expression ae = new DeclarationExp(exp.loc, tmp); Expression e = new CommaExp(exp.loc, ae, new VarExp(exp.loc, tmp)); e = e.expressionSemantic(sc); @@ -15514,7 +15437,7 @@ Expression addDtorHook(Expression e, Scope* sc) if (auto tf = e1.type.isTypeFunction()) { - if (tf.isref) + if (tf.isRef) return exp; } @@ -15527,7 +15450,7 @@ Expression addDtorHook(Expression e, Scope* sc) /* Type needs destruction, so declare a tmp * which the back end will recognize and call dtor on */ - auto tmp = copyToTemp(0, Id.__tmpfordtor.toString(), exp); + auto tmp = copyToTemp(STC.none, Id.__tmpfordtor.toString(), exp); auto de = new DeclarationExp(exp.loc, tmp); auto ve = new VarExp(exp.loc, tmp); Expression e = new CommaExp(exp.loc, de, ve); @@ -15598,9 +15521,9 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action if (e.op == EXP.type) error(_this.loc, "cannot %s type `%s`", action, e.type.toChars()); else if (e.op == EXP.template_) - error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toChars()); + error(_this.loc, "cannot %s template `%s`, perhaps instantiate it first", action, e.toErrMsg()); else - error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toChars()); + error(_this.loc, "cannot %s expression `%s` because it is not an lvalue", action, e.toErrMsg()); return ErrorExp.get(); } @@ -15609,7 +15532,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action { if (!_this.loc.isValid()) _this.loc = e.loc; - error(e.loc, "cannot %s constant `%s`", action, e.toChars()); + error(e.loc, "cannot %s constant `%s`", action, e.toErrMsg()); return ErrorExp.get(); } @@ -15632,7 +15555,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitStructLiteral(StructLiteralExp _this) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return _this; // C struct literals are lvalues else return visit(_this); @@ -15679,7 +15602,7 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action auto e1 = _this.e1; auto var = _this.var; //printf("DotVarExp::toLvalue(%s)\n", toChars()); - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.2.3-3: A postfix expression followed by the '.' or '->' operator * is an lvalue if the first expression is an lvalue. @@ -15725,14 +15648,22 @@ private Expression toLvalueImpl(Expression _this, Scope* sc, const(char)* action Expression visitCast(CastExp _this) { - if (sc && sc.flags & SCOPE.Cfile) + if (sc && sc.inCfile) { /* C11 6.5.4-5: A cast does not yield an lvalue. */ return visit(_this); } if (_this.isLvalue()) + { + with (_this) + if (!trusted && !e1.type.pointerTo().implicitConvTo(to.pointerTo())) + sc.setUnsafePreview(FeatureState.default_, false, loc, + "using the result of a cast from `%s` to `%s` as an lvalue", + e1.type, to); + return _this; + } return visit(_this); } @@ -16026,12 +15957,12 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression break; if (!ff.type.isMutable) { - error(exp.loc, "cannot modify `%s` in `%s` function", exp.toChars(), MODtoChars(type.mod)); + error(exp.loc, "cannot modify `%s` in `%s` function", exp.toErrMsg(), MODtoChars(type.mod)); return ErrorExp.get(); } } } - error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toChars()); + error(exp.loc, "cannot modify `%s` expression `%s`", MODtoChars(type.mod), exp.toErrMsg()); return ErrorExp.get(); } else if (!type.isAssignable()) @@ -16046,7 +15977,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitString(StringExp exp) { - error(exp.loc, "cannot modify string literal `%s`", exp.toChars()); + error(exp.loc, "cannot modify string literal `%s`", exp.toErrMsg()); return ErrorExp.get(); } @@ -16055,7 +15986,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression //printf("VarExp::modifiableLvalue('%s')\n", exp.var.toChars()); if (exp.var.storage_class & STC.manifest) { - error(exp.loc, "cannot modify manifest constant `%s`", exp.toChars()); + error(exp.loc, "cannot modify manifest constant `%s`", exp.toErrMsg()); return ErrorExp.get(); } // See if this expression is a modifiable lvalue (i.e. not const) @@ -16084,7 +16015,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitSlice(SliceExp exp) { - error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toChars()); + error(exp.loc, "slice expression `%s` is not a modifiable lvalue", exp.toErrMsg()); return exp; } @@ -16096,7 +16027,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitDelegatePtr(DelegatePtrExp exp) { - if (sc.setUnsafe(false, exp.loc, "cannot modify delegate pointer in `@safe` code `%s`", exp)) + if (sc.setUnsafe(false, exp.loc, "modifying delegate pointer `%s`", exp)) { return ErrorExp.get(); } @@ -16105,7 +16036,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression Expression visitDelegateFuncptr(DelegateFuncptrExp exp) { - if (sc.setUnsafe(false, exp.loc, "cannot modify delegate function pointer in `@safe` code `%s`", exp)) + if (sc.setUnsafe(false, exp.loc, "modifying delegate function pointer `%s`", exp)) { return ErrorExp.get(); } @@ -16126,7 +16057,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression { if (!exp.e1.isLvalue() && !exp.e2.isLvalue()) { - error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toChars()); + error(exp.loc, "conditional expression `%s` is not a modifiable lvalue", exp.toErrMsg()); return ErrorExp.get(); } exp.e1 = exp.e1.modifiableLvalue(sc); @@ -16159,7 +16090,7 @@ private Expression modifiableLvalueImpl(Expression _this, Scope* sc, Expression * Returns: * `true` if ok, `false` for error */ -bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) +private bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) { //printf("checkAddressVar(exp: %s, v: %s)\n", exp.toChars(), v.toChars()); if (v is null) @@ -16167,14 +16098,14 @@ bool checkAddressVar(Scope* sc, Expression exp, VarDeclaration v) if (!v.canTakeAddressOf()) { - error(exp.loc, "cannot take address of `%s`", exp.toChars()); + error(exp.loc, "cannot take address of `%s`", exp.toErrMsg()); return false; } if (sc.func && !sc.intypeof && !v.isDataseg()) { if (sc.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)) + sc.setUnsafe(false, exp.loc, "taking the address of stack-allocated local variable `%s`", v)) { return false; } @@ -16220,7 +16151,7 @@ bool checkAddressable(Expression e, Scope* sc) continue; case EXP.variable: - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { // C11 6.5.3.2: A variable that has its address taken cannot be // stored in a register. @@ -16229,9 +16160,9 @@ bool checkAddressable(Expression e, Scope* sc) if (ex.isVarExp().var.storage_class & STC.register) { if (e.isIndexExp()) - error(e.loc, "cannot index through register variable `%s`", ex.toChars()); + error(e.loc, "cannot index through register variable `%s`", ex.toErrMsg()); else - error(e.loc, "cannot take address of register variable `%s`", ex.toChars()); + error(e.loc, "cannot take address of register variable `%s`", ex.toErrMsg()); return false; } } @@ -16282,7 +16213,7 @@ private bool checkFunctionAttributes(Expression exp, Scope* sc, FuncDeclaration * Returns: * Expression representing the `this` for the var */ -Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, AggregateDeclaration ad, Expression e1, Type t, Dsymbol var, bool flag = false) +Expression getThisSkipNestedFuncs(Loc loc, Scope* sc, Dsymbol s, AggregateDeclaration ad, Expression e1, Type t, Dsymbol var, bool flag = false) { int n = 0; while (s && s.isFuncDeclaration()) @@ -16343,7 +16274,7 @@ Expression getThisSkipNestedFuncs(const ref Loc loc, Scope* sc, Dsymbol s, Aggre * newly created variable such that a closure is made for the variable when * the address of `fd` is taken. */ -VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration fd) +private VarDeclaration makeThis2Argument(Loc loc, Scope* sc, FuncDeclaration fd) { Type tthis2 = Type.tvoidptr.sarrayOf(2); VarDeclaration vthis2 = new VarDeclaration(loc, tthis2, Identifier.generateId("__this"), null); @@ -16369,7 +16300,7 @@ VarDeclaration makeThis2Argument(const ref Loc loc, Scope* sc, FuncDeclaration f * Returns: * a `bool` indicating if the hook is present. */ -bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object) +bool verifyHookExist(Loc loc, ref Scope sc, Identifier id, string description, Identifier module_ = Id.object) { Dsymbol pscopesym; auto rootSymbol = sc.search(loc, Id.empty, pscopesym); @@ -16393,7 +16324,7 @@ bool verifyHookExist(const ref Loc loc, ref Scope sc, Identifier id, string desc * false if any errors occur, * otherwise true and elements[] are rewritten for the output. */ -private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions* elements, Type stype) +private bool fit(StructDeclaration sd, Loc loc, Scope* sc, Expressions* elements, Type stype) { if (!elements) return true; @@ -16447,7 +16378,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions if ((!stype.alignment.isDefault() && stype.alignment.get() < target.ptrsize || (v.offset & (target.ptrsize - 1))) && (sc.setUnsafe(false, loc, - "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", sd, v))) + "field `%s.%s` assigning to misaligned pointers", sd, v))) { return false; } @@ -16465,7 +16396,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions Type typeb = se.type.toBasetype(); TY tynto = tb.nextOf().ty; if (!se.committed && - (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && + typeb.isStaticOrDynamicArray() && tynto.isSomeChar && se.numberOfCodeUnits(tynto) < (cast(TypeSArray)tb).dim.toInteger()) { e = se.castTo(sc, t); @@ -16489,7 +16420,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions if (e.op == EXP.error) return false; - (*elements)[i] = doCopyOrMove(sc, e); + (*elements)[i] = doCopyOrMove(sc, e, null, false); } return true; } @@ -16504,7 +16435,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions * Returns: * VarExp referenceing `em` or ErrorExp if `em` if disabled/deprecated */ -Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc) +Expression getVarExp(EnumMember em, Loc loc, Scope* sc) { dsymbolSemantic(em, sc); if (em.errors) @@ -16522,7 +16453,7 @@ Expression getVarExp(EnumMember em, const ref Loc loc, Scope* sc) return ErrorExp.get(); Expression e = new VarExp(loc, em); e = e.expressionSemantic(sc); - if (!(sc.flags & SCOPE.Cfile) && em.isCsymbol()) + if (!sc.inCfile && em.isCsymbol()) { /* C11 types them as int. But if in D file, * type qualified names as the enum @@ -16563,7 +16494,7 @@ Expression toBoolean(Expression exp, Scope* sc) case EXP.construct: case EXP.blit: case EXP.loweredAssignExp: - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return exp; // Things like: // if (a = b) ... @@ -16610,7 +16541,7 @@ Expression toBoolean(Expression exp, Scope* sc) /* Don't really need to check for opCast first, but by doing so we * get better error messages if it isn't there. */ - if (Dsymbol fd = search_function(ad, Id._cast)) + if (Dsymbol fd = search_function(ad, Id.opCast)) { e = new CastExp(exp.loc, e, Type.tbool); e = e.expressionSemantic(sc); @@ -16702,7 +16633,7 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool const uint nerrors = global.errors; sc = sc.startCTFE(); - sc.flags |= SCOPE.condition; + sc.condition = true; e = e.expressionSemantic(sc); e = resolveProperties(sc, e); @@ -16725,7 +16656,7 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool if (opt.isEmpty()) { if (!e.type.isTypeError()) - error(e.loc, "expression `%s` is not constant", e.toChars()); + error(e.loc, "expression `%s` is not constant", e.toErrMsg()); errors = true; return false; } @@ -16736,3 +16667,1007 @@ bool evalStaticCondition(Scope* sc, Expression original, Expression e, out bool } return impl(e); } + +/************************************ + * Check to see the aggregate type is nested and its context pointer is + * accessible from the current scope. + * Returns true if error occurs. + */ +bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, size_t iStart = 0) +{ + Dsymbol sparent = ad.toParentLocal(); + Dsymbol sparent2 = ad.toParent2(); + Dsymbol s = sc.func; + if (ad.isNested() && s) + { + //printf("ad = %p %s [%s], parent:%p\n", ad, ad.toChars(), ad.loc.toChars(), ad.parent); + //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent.toChars(), sparent.loc.toChars(), sparent.parent,toChars()); + //printf("sparent2 = %p %s [%s], parent: %s\n", sparent2, sparent2.toChars(), sparent2.loc.toChars(), sparent2.parent,toChars()); + if (!ensureStaticLinkTo(s, sparent) || sparent != sparent2 && !ensureStaticLinkTo(s, sparent2)) + { + error(loc, "cannot access frame pointer of `%s`", ad.toPrettyChars()); + return true; + } + } + + bool result = false; + for (size_t i = iStart; i < ad.fields.length; i++) + { + VarDeclaration vd = ad.fields[i]; + Type tb = vd.type.baseElemOf(); + if (tb.ty == Tstruct) + { + result |= checkFrameAccess(loc, sc, (cast(TypeStruct)tb).sym); + } + } + return result; +} + +/// Return value for `checkModifiable` +enum Modifiable +{ + /// Not modifiable + no, + /// Modifiable (the type is mutable) + yes, + /// Modifiable because it is initialization + initialization, +} + +/** + * Specifies how the checkModify deals with certain situations + */ +enum ModifyFlags +{ + /// Issue error messages on invalid modifications of the variable + none, + /// No errors are emitted for invalid modifications + noError = 0x1, + /// The modification occurs for a subfield of the current variable + fieldAssign = 0x2, +} + +/************************************* + * Check to see if declaration can be modified in this context (sc). + * Issue error if not. + * Params: + * loc = location for error messages + * e1 = `null` or `this` expression when this declaration is a field + * sc = context + * flag = if the first bit is set it means do not issue error message for + * invalid modification; if the second bit is set, it means that + this declaration is a field and a subfield of it is modified. + * Returns: + * Modifiable.yes or Modifiable.initialization + */ +private Modifiable checkModify(Declaration d, Loc loc, Scope* sc, Expression e1, ModifyFlags flag) +{ + VarDeclaration v = d.isVarDeclaration(); + if (v && v.canassign) + return Modifiable.initialization; + + if (d.isParameter() || d.isResult()) + { + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (scx.func == d.parent && scx.contract != Contract.none) + { + const(char)* s = d.isParameter() && d.parent.ident != Id.ensure ? "parameter" : "result"; + if (!(flag & ModifyFlags.noError)) + error(loc, "%s `%s` cannot modify %s `%s` in contract", d.kind, d.toPrettyChars, s, d.toChars()); + return Modifiable.initialization; // do not report type related errors + } + } + } + + if (e1 && e1.op == EXP.this_ && d.isField()) + { + VarDeclaration vthis = e1.isThisExp().var; + for (Scope* scx = sc; scx; scx = scx.enclosing) + { + if (scx.func == vthis.parent && scx.contract != Contract.none) + { + if (!(flag & ModifyFlags.noError)) + error(loc, "cannot modify member variable `%s` in contract", d.toPrettyChars()); + return Modifiable.initialization; // do not report type related errors + } + } + } + + if (v && (v.isCtorinit() || d.isField())) + { + // It's only modifiable if inside the right constructor + if ((d.storage_class & (STC.foreach_ | STC.ref_)) == (STC.foreach_ | STC.ref_)) + return Modifiable.initialization; + if (flag & ModifyFlags.fieldAssign) + return Modifiable.yes; + return modifyFieldVar(loc, sc, v, e1) ? Modifiable.initialization : Modifiable.yes; + } + return Modifiable.yes; +} + +/*********************************************** + * Mark variable v as modified if it is inside a constructor that var + * is a field in. + * Also used to allow immutable globals to be initialized inside a static constructor. + * Returns: + * true if it's an initialization of v + */ +private bool modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) +{ + //printf("modifyFieldVar(var = %s)\n", var.toChars()); + Dsymbol s = sc.func; + while (1) + { + FuncDeclaration fd = null; + if (s) + fd = s.isFuncDeclaration(); + if (fd && + ((fd.isCtorDeclaration() && var.isField()) || + ((fd.isStaticCtorDeclaration() || fd.isCrtCtor) && !var.isField())) && + fd.toParentDecl() == var.toParent2() && + (!e1 || e1.op == EXP.this_)) + { + bool result = true; + + var.ctorinit = true; + //printf("setting ctorinit\n"); + + if (var.isField() && sc.ctorflow.fieldinit.length && !sc.intypeof) + { + assert(e1); + auto mustInit = ((var.storage_class & STC.nodefaultctor) != 0 || + var.type.needsNested()); + + const dim = sc.ctorflow.fieldinit.length; + auto ad = fd.isMemberDecl(); + assert(ad); + size_t i; + for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? + { + if (ad.fields[i] == var) + break; + } + assert(i < dim); + auto fieldInit = &sc.ctorflow.fieldinit[i]; + const fi = fieldInit.csx; + + if (fi & CSX.this_ctor) + { + if (var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field `%s` initialized multiple times", modStr, var.toChars()); + .errorSupplemental(fieldInit.loc, "Previous initialization is here."); + } + } + else if (sc.inLoop || (fi & CSX.label)) + { + if (!mustInit && var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field `%s` initialization is not allowed in loops or after labels", modStr, var.toChars()); + } + } + + fieldInit.csx |= CSX.this_ctor; + fieldInit.loc = e1.loc; + if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 + { + foreach (j, v; ad.fields) + { + if (v is var || !var.isOverlappedWith(v)) + continue; + v.ctorinit = true; + sc.ctorflow.fieldinit[j].csx = CSX.this_ctor; + } + } + } + else if (fd != sc.func) + { + if (var.type.isMutable()) + result = false; + else if (sc.func.fes) + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s `%s` initialization is not allowed in foreach loop", + MODtoChars(var.type.mod), p, var.toChars()); + } + else + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s `%s` initialization is not allowed in nested function `%s`", + MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); + } + } + else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && + var.type.isImmutable()) + { + .error(loc, "%s %s `%s` initialization is not allowed in `static this`", + MODtoChars(var.type.mod), var.kind(), var.toChars()); + errorSupplemental(loc, "Use `shared static this` instead."); + } + else if (fd.isStaticCtorDeclaration() && !fd.isSharedStaticCtorDeclaration() && + var.type.isConst()) + { + // @@@DEPRECATED_2.116@@@ + // Turn this into an error, merging with the branch above + .deprecation(loc, "%s %s `%s` initialization is not allowed in `static this`", + MODtoChars(var.type.mod), var.kind(), var.toChars()); + deprecationSupplemental(loc, "Use `shared static this` instead."); + } + return result; + } + else + { + if (s) + { + s = s.toParentP(var.toParent2()); + continue; + } + } + break; + } + return false; +} + +/*************************************** + * Request additional semantic analysis for TypeInfo generation. + * Params: + * sc = context + * t = type that TypeInfo is being generated for + */ +void semanticTypeInfo(Scope* sc, Type t) +{ + if (sc) + { + if (sc.intypeof) + return; + if (!sc.needsCodegen()) + return; + } + + if (!t) + return; + + void visitVector(TypeVector t) + { + semanticTypeInfo(sc, t.basetype); + } + + void visitAArray(TypeAArray t) + { + semanticTypeInfo(sc, t.index); + semanticTypeInfo(sc, t.next); + + if (global.params.useTypeInfo) + getTypeInfoType(t.loc, t, sc); + } + + void visitStruct(TypeStruct t) + { + //printf("semanticTypeInfo.visit(TypeStruct = %s)\n", t.toChars()); + StructDeclaration sd = t.sym; + + /* Step 1: create TypeInfoDeclaration + */ + if (!sc) // inline may request TypeInfo. + { + Scope scx; + scx.eSink = global.errorSink; + scx._module = sd.getModule(); + if (global.params.useTypeInfo) + { + getTypeInfoType(sd.loc, t, &scx); + sd.requestTypeInfo = true; + } + } + else if (!sc.minst) + { + // don't yet have to generate TypeInfo instance if + // the typeid(T) expression exists in speculative scope. + } + else + { + getTypeInfoType(sd.loc, t, sc); + sd.requestTypeInfo = true; + + // https://issues.dlang.org/show_bug.cgi?id=15149 + // if the typeid operand type comes from a + // result of auto function, it may be yet speculative. + // unSpeculative(sc, sd); + } + + /* Step 2: If the TypeInfo generation requires sd.semantic3, run it later. + * This should be done even if typeid(T) exists in speculative scope. + * Because it may appear later in non-speculative scope. + */ + if (!sd.members) + return; // opaque struct + if (!sd.xeq && !sd.xcmp && !sd.postblit && !sd.tidtor && !sd.xhash && !search_toString(sd)) + return; // none of TypeInfo-specific members + + // If the struct is in a non-root module, run semantic3 to get + // correct symbols for the member function. + if (sd.semanticRun >= PASS.semantic3) + { + // semantic3 is already done + } + else if (TemplateInstance ti = sd.isInstantiated()) + { + if (ti.minst && !ti.minst.isRoot()) + Module.addDeferredSemantic3(sd); + } + else + { + if (sd.inNonRoot()) + { + //printf("deferred sem3 for TypeInfo - sd = %s, inNonRoot = %d\n", sd.toChars(), sd.inNonRoot()); + Module.addDeferredSemantic3(sd); + } + } + } + + void visitTuple(TypeTuple t) + { + if (t.arguments) + { + foreach (arg; *t.arguments) + { + semanticTypeInfo(sc, arg.type); + } + } + } + + /* Note structural similarity of this Type walker to that in isSpeculativeType() + */ + + Type tb = t.toBasetype(); + switch (tb.ty) + { + case Tvector: visitVector(tb.isTypeVector()); break; + case Taarray: visitAArray(tb.isTypeAArray()); break; + case Tstruct: visitStruct(tb.isTypeStruct()); break; + case Ttuple: visitTuple (tb.isTypeTuple()); break; + + case Tclass: + case Tenum: break; + + default: semanticTypeInfo(sc, tb.nextOf()); break; + } +} + +/** + * Issue an error if an attempt to call a disabled method is made + * + * If the declaration is disabled but inside a disabled function, + * returns `true` but do not issue an error message. + * + * Params: + * d = Declaration to check + * loc = Location information of the call + * sc = Scope in which the call occurs + * isAliasedDeclaration = if `true` searches overload set + * + * Returns: + * `true` if this `Declaration` is `@disable`d, `false` otherwise. + */ +bool checkDisabled(Declaration d, Loc loc, Scope* sc, bool isAliasedDeclaration = false) +{ + if (!(d.storage_class & STC.disable)) + return false; + + if (sc.func && sc.func.storage_class & STC.disable) + return true; + + if (auto p = d.toParent()) + { + if (auto postblit = d.isPostBlitDeclaration()) + { + /* https://issues.dlang.org/show_bug.cgi?id=21885 + * + * If the generated postblit is disabled, it + * means that one of the fields has a disabled + * postblit. Print the first field that has + * a disabled postblit. + */ + if (postblit.isGenerated()) + { + auto sd = p.isStructDeclaration(); + assert(sd); + for (size_t i = 0; i < sd.fields.length; i++) + { + auto structField = sd.fields[i]; + if (structField.overlapped) + continue; + Type tv = structField.type.baseElemOf(); + if (tv.ty != Tstruct) + continue; + auto sdv = (cast(TypeStruct)tv).sym; + if (!sdv.postblit) + continue; + if (sdv.postblit.isDisabled()) + { + .error(loc, "%s `%s` is not copyable because field `%s` is not copyable", p.kind, p.toPrettyChars, structField.toChars()); + return true; + } + } + } + .error(loc, "%s `%s` is not copyable because it has a disabled postblit", p.kind, p.toPrettyChars); + return true; + } + } + + // if the function is @disabled, maybe there + // is an overload in the overload set that isn't + if (isAliasedDeclaration) + { + if (FuncDeclaration fd = d.isFuncDeclaration()) + { + for (FuncDeclaration ovl = fd; ovl; ovl = cast(FuncDeclaration)ovl.overnext) + if (!(ovl.storage_class & STC.disable)) + return false; + } + } + + if (auto ctor = d.isCtorDeclaration()) + { + //printf("checkDisabled() %s %s\n", ctor.toPrettyChars(), toChars(ctor.type)); + if (ctor.isCpCtor && ctor.isGenerated()) + { + .error(loc, "generating an `inout` copy constructor for `struct %s` failed, therefore instances of it are uncopyable", d.parent.toPrettyChars()); + return true; + } + } + .error(loc, "%s `%s` cannot be used because it is annotated with `@disable`", d.kind, d.toPrettyChars); + return true; +} + +/******************************************* + * Helper function for the expansion of manifest constant. + */ +private Expression expandInitializer(VarDeclaration vd, Loc loc) +{ + assert((vd.storage_class & STC.manifest) && vd._init); + + auto e = vd.getConstInitializer(); + if (!e) + { + .error(loc, "cannot make expression out of initializer for `%s`", vd.toChars()); + return ErrorExp.get(); + } + + e = e.copy(); + e.loc = loc; // for better error message + return e; +} + +/***************************************************** + * Determine if template instance is really a template function, + * and that template function needs to infer types from the function + * arguments. + * + * Like findBestMatch, iterate possible template candidates, + * but just looks only the necessity of type inference. + */ +private bool needsTypeInference(TemplateInstance ti, Scope* sc, int flag = 0) +{ + //printf("TemplateInstance.needsTypeInference() %s\n", toChars()); + if (ti.semanticRun != PASS.initial) + return false; + + const olderrs = global.errors; + Objects dedtypes; + size_t count = 0; + + auto tovers = ti.tempdecl.isOverloadSet(); + foreach (size_t oi; 0 .. tovers ? tovers.a.length : 1) + { + Dsymbol dstart = tovers ? tovers.a[oi] : ti.tempdecl; + int r = overloadApply(dstart, (Dsymbol s) + { + auto td = s.isTemplateDeclaration(); + if (!td) + return 0; + + /* If any of the overloaded template declarations need inference, + * then return true + */ + if (!td.onemember) + return 0; + if (auto td2 = td.onemember.isTemplateDeclaration()) + { + if (!td2.onemember || !td2.onemember.isFuncDeclaration()) + return 0; + if (ti.tiargs.length >= td.parameters.length - (td.isVariadic() ? 1 : 0)) + return 0; + return 1; + } + auto fd = td.onemember.isFuncDeclaration(); + if (!fd || fd.type.ty != Tfunction) + return 0; + + foreach (tp; *td.parameters) + { + if (tp.isTemplateThisParameter()) + return 1; + } + + /* Determine if the instance arguments, tiargs, are all that is necessary + * to instantiate the template. + */ + //printf("tp = %p, td.parameters.length = %d, tiargs.length = %d\n", tp, td.parameters.length, tiargs.length); + auto tf = fd.type.isTypeFunction(); + if (tf.parameterList.length) + { + auto tp = td.isVariadic(); + if (tp && td.parameters.length > 1) + return 1; + + if (!tp && ti.tiargs.length < td.parameters.length) + { + // Can remain tiargs be filled by default arguments? + foreach (size_t i; ti.tiargs.length .. td.parameters.length) + { + if (!(*td.parameters)[i].hasDefaultArg()) + return 1; + } + } + + foreach (i, fparam; tf.parameterList) + { + // 'auto ref' needs inference. + if (fparam.storageClass & STC.auto_) + return 1; + } + } + + if (!flag) + { + /* Calculate the need for overload resolution. + * When only one template can match with tiargs, inference is not necessary. + */ + dedtypes.setDim(td.parameters.length); + dedtypes.zero(); + if (td.semanticRun == PASS.initial) + { + if (td._scope) + { + // Try to fix forward reference. Ungag errors while doing so. + Ungag ungag = td.ungagSpeculative(); + td.dsymbolSemantic(td._scope); + } + if (td.semanticRun == PASS.initial) + { + .error(ti.loc, "%s `%s` `%s` forward references template declaration `%s`", + ti.kind, ti.toPrettyChars, ti.toChars(), td.toChars()); + return 1; + } + } + MATCH m = matchWithInstance(sc, td, ti, dedtypes, ArgumentList(), 0); + if (m == MATCH.nomatch) + return 0; + } + + /* If there is more than one function template which matches, we may + * need type inference (see https://issues.dlang.org/show_bug.cgi?id=4430) + */ + return ++count > 1 ? 1 : 0; + }); + if (r) + return true; + } + + if (olderrs != global.errors) + { + if (!global.gag) + { + errorSupplemental(ti.loc, "while looking for match for `%s`", ti.toChars()); + ti.semanticRun = PASS.semanticdone; + ti.inst = ti; + } + ti.errors = true; + } + //printf("false\n"); + return false; +} + +/*************************************** + * Fill out remainder of elements[] with default initializers for fields[]. + * Params: + * sd = struct + * loc = location + * elements = explicit arguments which given to construct object. + * ctorinit = true if the elements will be used for default initialization. + * Returns: + * false if any errors occur. + * Otherwise, returns true and the missing arguments will be pushed in elements[]. + */ +bool fill(StructDeclaration sd, Loc loc, ref Expressions elements, bool ctorinit) +{ + //printf("AggregateDeclaration::fill() %s\n", toChars()); + assert(sd.sizeok == Sizeok.done); + const nfields = sd.nonHiddenFields(); + bool errors = false; + + size_t dim = elements.length; + elements.setDim(nfields); + foreach (size_t i; dim .. nfields) + elements[i] = null; + + // Fill in missing any elements with default initializers + foreach (i; 0 .. nfields) + { + if (elements[i]) + continue; + + auto vd = sd.fields[i]; + auto vx = vd; + if (vd._init && vd._init.isVoidInitializer()) + vx = null; + + // Find overlapped fields with the hole [vd.offset .. vd.offset.size()]. + size_t fieldi = i; + foreach (j; 0 .. nfields) + { + if (i == j) + continue; + auto v2 = sd.fields[j]; + if (!vd.isOverlappedWith(v2)) + continue; + + if (elements[j]) + { + vx = null; + break; + } + if (v2._init && v2._init.isVoidInitializer()) + continue; + + version (all) + { + /* Prefer first found non-void-initialized field + * union U { int a; int b = 2; } + * U u; // Error: overlapping initialization for field a and b + */ + if (!vx) + { + vx = v2; + fieldi = j; + } + else if (v2._init) + { + .error(loc, "overlapping initialization for field `%s` and `%s`", v2.toChars(), vd.toChars()); + errors = true; + } + } + else + { + // fixes https://issues.dlang.org/show_bug.cgi?id=1432 by enabling this path always + + /* Prefer explicitly initialized field + * union U { int a; int b = 2; } + * U u; // OK (u.b == 2) + */ + if (!vx || !vx._init && v2._init) + { + vx = v2; + fieldi = j; + } + else if (vx != vd && !vx.isOverlappedWith(v2)) + { + // Both vx and v2 fills vd, but vx and v2 does not overlap + } + else if (vx._init && v2._init) + { + .error(loc, "overlapping default initialization for field `%s` and `%s`", + v2.toChars(), vd.toChars()); + errors = true; + } + else + assert(vx._init || !vx._init && !v2._init); + } + } + if (!vx) + continue; + + Expression e; + if (vx.type.size() == 0) + { + e = null; + } + else if (vx._init) + { + assert(!vx._init.isVoidInitializer()); + if (vx.inuse) // https://issues.dlang.org/show_bug.cgi?id=18057 + { + .error(loc, "%s `%s` recursive initialization of field", vx.kind(), vx.toPrettyChars()); + errors = true; + } + else + e = vx.getConstInitializer(false); + } + else + { + if ((vx.storage_class & STC.nodefaultctor) && !ctorinit) + { + .error(loc, "field `%s.%s` must be initialized because it has no default constructor", + sd.type.toChars(), vx.toChars()); + errors = true; + } + /* https://issues.dlang.org/show_bug.cgi?id=12509 + * Get the element of static array type. + */ + Type telem = vx.type; + if (telem.ty == Tsarray) + { + /* We cannot use Type::baseElemOf() here. + * If the bottom of the Tsarray is an enum type, baseElemOf() + * will return the base of the enum, and its default initializer + * would be different from the enum's. + */ + TypeSArray tsa; + while ((tsa = telem.toBasetype().isTypeSArray()) !is null) + telem = tsa.next; + if (telem.ty == Tvoid) + telem = Type.tuns8.addMod(telem.mod); + } + if (telem.needsNested() && ctorinit) + e = telem.defaultInit(loc); + else + e = telem.defaultInitLiteral(loc); + } + elements[fieldi] = e; + } + foreach (e; elements) + { + if (e && e.op == EXP.error) + return false; + } + + return !errors; +} + +/***************************************** +* Lower any aggregate that is not an array to an array using a +* regular foreach loop within CTFE. If there are multiple +* `static foreach` loop variables, an array of tuples is +* generated. In thise case, the field `needExpansion` is set to +* true to indicate that the static foreach loop expansion will +* need to expand the tuples into multiple variables. +* +* For example, `static foreach (x; range) { ... }` is lowered to: +* +* static foreach (x; { +* typeof({ +* foreach (x; range) return x; +* }())[] __res; +* foreach (x; range) __res ~= x; +* return __res; +* }()) { ... } +* +* Finally, call `lowerArrayAggregate` to turn the produced +* array into an expression tuple. +* +* Params: +* sfe = The 'static foreach'. +* sc = The current scope. +*/ +extern (C++) void lowerNonArrayAggregate(StaticForeach sfe, Scope* sc) +{ + import dmd.statement; + + auto nvars = sfe.aggrfe ? sfe.aggrfe.parameters.length : 1; + auto aloc = sfe.aggrfe ? sfe.aggrfe.aggr.loc : sfe.rangefe.lwr.loc; + // We need three sets of foreach loop variables because the + // lowering contains three foreach loops. + Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; + foreach (i; 0 .. nvars) + { + foreach (params; pparams) + { + auto p = sfe.aggrfe ? (*sfe.aggrfe.parameters)[i] : sfe.rangefe.param; + params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null)); + } + } + Expression[2] res; + TypeStruct tplty = null; + if (nvars == 1) // only one `static foreach` variable, generate identifiers. + { + foreach (i; 0 .. 2) + { + res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident); + } + } + else // multiple `static foreach` variables, generate tuples. + { + foreach (i; 0 .. 2) + { + auto e = new Expressions(pparams[0].length); + foreach (j, ref elem; *e) + { + auto p = (*pparams[i])[j]; + elem = new IdentifierExp(aloc, p.ident); + } + if (!tplty) + { + tplty = sfe.createTupleType(aloc, e, sc); + } + res[i] = sfe.createTuple(aloc, tplty, e); + } + sfe.needExpansion = true; // need to expand the tuples later + } + // generate remaining code for the new aggregate which is an + // array (see documentation comment). + if (sfe.rangefe) + { + sc = sc.startCTFE(); + sfe.rangefe.lwr = sfe.rangefe.lwr.expressionSemantic(sc); + sfe.rangefe.lwr = resolveProperties(sc, sfe.rangefe.lwr); + sfe.rangefe.upr = sfe.rangefe.upr.expressionSemantic(sc); + sfe.rangefe.upr = resolveProperties(sc, sfe.rangefe.upr); + sc = sc.endCTFE(); + sfe.rangefe.lwr = sfe.rangefe.lwr.optimize(WANTvalue); + sfe.rangefe.lwr = sfe.rangefe.lwr.ctfeInterpret(); + sfe.rangefe.upr = sfe.rangefe.upr.optimize(WANTvalue); + sfe.rangefe.upr = sfe.rangefe.upr.ctfeInterpret(); + } + auto s1 = new Statements(); + auto stmts = new Statements(); + if (tplty) stmts.push(new ExpStatement(sfe.loc, tplty.sym)); + stmts.push(new ReturnStatement(aloc, res[0])); + s1.push(sfe.createForeach(aloc, pparams[0], new CompoundStatement(aloc, stmts))); + s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); + Type ety = new TypeTypeof(aloc, sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s1))); + auto aty = ety.arrayOf(); + auto idres = Identifier.generateId("__res"); + auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp); + auto s2 = new Statements(); + + // Run 'typeof' gagged to avoid duplicate errors and if it fails just create + // an empty foreach to expose them. + const olderrors = global.startGagging(); + ety = ety.typeSemantic(aloc, sc); + if (global.endGagging(olderrors)) + s2.push(sfe.createForeach(aloc, pparams[1], null)); + else + { + s2.push(new ExpStatement(aloc, vard)); + auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); + s2.push(sfe.createForeach(aloc, pparams[1], new ExpStatement(aloc, catass))); + s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); + } + + Expression aggr = void; + Type indexty = void; + + if (sfe.rangefe && (indexty = ety).isIntegral()) + { + sfe.rangefe.lwr.type = indexty; + sfe.rangefe.upr.type = indexty; + auto lwrRange = getIntRange(sfe.rangefe.lwr); + auto uprRange = getIntRange(sfe.rangefe.upr); + + const lwr = sfe.rangefe.lwr.toInteger(); + auto upr = sfe.rangefe.upr.toInteger(); + size_t length = 0; + + if (lwrRange.imin <= uprRange.imax) + length = cast(size_t) (upr - lwr); + + auto exps = new Expressions(length); + + if (sfe.rangefe.op == TOK.foreach_) + { + foreach (i; 0 .. length) + (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty); + } + else + { + --upr; + foreach (i; 0 .. length) + (*exps)[i] = new IntegerExp(aloc, upr - i, indexty); + } + aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps); + } + else + { + aggr = sfe.wrapAndCall(aloc, new CompoundStatement(aloc, s2)); + sc = sc.startCTFE(); + aggr = aggr.expressionSemantic(sc); + aggr = resolveProperties(sc, aggr); + sc = sc.endCTFE(); + aggr = aggr.optimize(WANTvalue); + aggr = aggr.ctfeInterpret(); + } + + assert(!!sfe.aggrfe ^ !!sfe.rangefe); + sfe.aggrfe = new ForeachStatement(sfe.loc, TOK.foreach_, pparams[2], aggr, + sfe.aggrfe ? sfe.aggrfe._body : sfe.rangefe._body, + sfe.aggrfe ? sfe.aggrfe.endloc : sfe.rangefe.endloc); + sfe.rangefe = null; + sfe.lowerArrayAggregate(sc); // finally, turn generated array into expression tuple +} + +/***************************************** +* Perform `static foreach` lowerings that are necessary in order +* to finally expand the `static foreach` using +* `dmd.statementsem.makeTupleForeach`. +*/ +extern(D) void prepare(StaticForeach sfe, Scope* sc) +{ + assert(sc); + + if (sfe.aggrfe) + { + sc = sc.startCTFE(); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.expressionSemantic(sc); + sc = sc.endCTFE(); + } + + if (sfe.aggrfe && sfe.aggrfe.aggr.type.toBasetype().ty == Terror) + { + return; + } + + if (!sfe.ready()) + { + if (sfe.aggrfe && sfe.aggrfe.aggr.type.toBasetype().ty == Tarray) + { + sfe.lowerArrayAggregate(sc); + } + else + { + sfe.lowerNonArrayAggregate(sc); + } + } +} + +/***************************************** + * Turn an aggregate which is an array into an expression tuple + * of its elements. I.e., lower + * static foreach (x; [1, 2, 3, 4]) { ... } + * to + * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } + */ +extern(D) void lowerArrayAggregate(StaticForeach sfe, Scope* sc) +{ + auto aggr = sfe.aggrfe.aggr; + Expression el = new ArrayLengthExp(aggr.loc, aggr); + sc = sc.startCTFE(); + el = el.expressionSemantic(sc); + sc = sc.endCTFE(); + el = el.optimize(WANTvalue); + el = el.ctfeInterpret(); + if (el.op != EXP.int64) + { + sfe.aggrfe.aggr = ErrorExp.get(); + return; + } + + Expressions* es; + if (auto ale = aggr.isArrayLiteralExp()) + { + // Directly use the elements of the array for the TupleExp creation + es = ale.elements; + } + else + { + const length = cast(size_t)el.toInteger(); + es = new Expressions(length); + foreach (i; 0 .. length) + { + auto index = new IntegerExp(sfe.loc, i, Type.tsize_t); + auto value = new IndexExp(aggr.loc, aggr, index); + (*es)[i] = value; + } + } + sfe.aggrfe.aggr = new TupleExp(aggr.loc, es); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.expressionSemantic(sc); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.optimize(WANTvalue); + sfe.aggrfe.aggr = sfe.aggrfe.aggr.ctfeInterpret(); +} diff --git a/gcc/d/dmd/file_manager.d b/gcc/d/dmd/file_manager.d index c696a5c..2ccb1d2 100644 --- a/gcc/d/dmd/file_manager.d +++ b/gcc/d/dmd/file_manager.d @@ -1,16 +1,17 @@ /** * Read a file from disk and store it in memory. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/file_manager.d, _file_manager.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/file_manager.d, _file_manager.d) * Documentation: https://dlang.org/phobos/dmd_file_manager.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/file_manager.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/file_manager.d */ module dmd.file_manager; import core.stdc.stdio; +import dmd.common.outbuffer; import dmd.root.stringtable : StringTable; import dmd.root.file : File, Buffer; import dmd.root.filename : FileName, isDirSeparator; @@ -70,44 +71,87 @@ private struct PathStack } } -final class FileManager +/*************************** + * Cache path lookups so the operating system + * is only consulted once for each path. + */ +private struct PathCache { - private StringTable!(const(ubyte)[]) files; // contents of files indexed by file name - private StringTable!(bool) packageStatus; + /* for filespec "a/b/c/d.ext" + * a b and c are directories, a, a/b, a/b/c are paths. + */ + + StringTable!(bool) pathStatus; // cached value of does a path exist or not + + nothrow: - // check if the package path of the given path exists. The input path is - // expected to contain the full path to the module, so the parent - // directories of that path are checked. - private bool packageExists(const(char)[] p) nothrow + /** + * Determine if the path part of path/filename exists. + * Cache the results for the path and each left-justified subpath of the path. + * Params: + * filespec = path/filename + * Returns: + * true if path exists, false if it does not + */ + bool pathExists(const(char)[] filespec) nothrow { - // step 1, look for the closest parent path that is cached + /* look for the longest leftmost parent path that is cached + * by starting at the right and working to the left + */ bool exists = true; - auto st = PathStack(p); - while (st.up) { - if (auto cached = packageStatus.lookup(st.cur)) { + auto st = PathStack(filespec); + while (st.up) + { + if (auto cached = pathStatus.lookup(st.cur)) + { exists = cached.value; break; } } - // found a parent that is cached (or reached the end of the stack). - // step 2, traverse back up the stack storing either false if the - // parent doesn't exist, or the result of the `exists` call if it does. - while (st.down) { + /* found a parent path that is cached (or reached the left end of the path). + * Now move right caching the results of those directories. + * Once a directory is found to not exist, all the directories + * to the right of it do not exist + */ + while (st.down) + { if (!exists) - packageStatus.insert(st.cur, false); + pathStatus.insert(st.cur, false); else - exists = packageStatus.insert(st.cur, FileName.exists(st.cur) == 2).value; + exists = pathStatus.insert(st.cur, FileName.exists(st.cur) == 2).value; } - // at this point, exists should be the answer. return exists; } + /** + * Ask if path ends in a directory. + * Cache result for speed. + * Params: + * path = a path + * Returns: + * true if it's a path, false if not + */ + bool isExistingPath(const char[] path) + { + auto cached = pathStatus.lookup(path); + if (!cached) + cached = pathStatus.insert(path, FileName.exists(path) == 2); + return cached.value; + } +} + +final class FileManager +{ + private StringTable!(const(ubyte)[]) files; // contents of files indexed by file name + + private PathCache pathCache; + /// public this () nothrow { this.files._init(); - this.packageStatus._init(); + this.pathCache.pathStatus._init(); } nothrow: @@ -117,39 +161,37 @@ nothrow: * Does not open the file. * Params: * filename = as supplied by the user - * path = path to look for filename + * pathsInfo = pathsInfo to look for filename with metadata + * whichPathFoundThis = Which path from `path` was used in determining the output path, or -1 if unknown. * Returns: * the found file name or * `null` if it is not different from filename. */ - const(char)[] lookForSourceFile(const char[] filename, const char*[] path) + const(char)[] lookForSourceFile(const char[] filename, const ImportPathInfo[] pathsInfo, out ptrdiff_t whichPathFoundThis) { //printf("lookForSourceFile(`%.*s`)\n", cast(int)filename.length, filename.ptr); - /* Search along path[] for .di file, then .d file. + /* Search along pathsInfo[] for .di file, then .d file. */ + + whichPathFoundThis = -1; + // see if we should check for the module locally. - bool checkLocal = packageExists(filename); + bool checkLocal = pathCache.pathExists(filename); const sdi = FileName.forceExt(filename, hdr_ext); if (checkLocal && FileName.exists(sdi) == 1) return sdi; scope(exit) FileName.free(sdi.ptr); const sd = FileName.forceExt(filename, mars_ext); - // Special file name representing `stdin`, always assume its presence - if (sd == "__stdin.d") - return sd; if (checkLocal && FileName.exists(sd) == 1) return sd; scope(exit) FileName.free(sd.ptr); if (checkLocal) { - auto cached = packageStatus.lookup(filename); - if (!cached) - cached = packageStatus.insert(filename, FileName.exists(filename) == 2); - if (cached.value) + if (pathCache.isExistingPath(filename)) { - /* The filename exists and it's a directory. + /* The filename exists but it's a directory. * Therefore, the result should be: filename/package.d * iff filename/package.d is a file */ @@ -167,56 +209,57 @@ nothrow: if (FileName.absolute(filename)) return null; - if (!path.length) + if (!pathsInfo.length) return null; - foreach (entry; path) + foreach (pathIndex, entry; pathsInfo) { - const p = entry.toDString(); + const p = entry.path.toDString(); const(char)[] n = FileName.combine(p, sdi); - if (!packageExists(n)) { + if (!pathCache.pathExists(n)) + { FileName.free(n.ptr); continue; // no need to check for anything else. } - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) return n; - } + FileName.free(n.ptr); n = FileName.combine(p, sd); - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) + { + whichPathFoundThis = pathIndex; return n; } FileName.free(n.ptr); - const b = FileName.removeExt(filename); - n = FileName.combine(p, b); - FileName.free(b.ptr); - + n = FileName.combine(p, FileName.sansExt(filename)); scope(exit) FileName.free(n.ptr); // also cache this if we are looking for package.d[i] - auto cached = packageStatus.lookup(n); - if (!cached) { - cached = packageStatus.insert(n, FileName.exists(n) == 2); - } - - if (cached.value) + if (pathCache.isExistingPath(n)) { const n2i = FileName.combine(n, package_di); if (FileName.exists(n2i) == 1) + { + whichPathFoundThis = pathIndex; return n2i; + } + FileName.free(n2i.ptr); const n2 = FileName.combine(n, package_d); - if (FileName.exists(n2) == 1) { + if (FileName.exists(n2) == 1) + { + whichPathFoundThis = pathIndex; return n2; } FileName.free(n2.ptr); } } - /* ImportC: No D modules found, now search along path[] for .i file, then .c file. + /* ImportC: No D modules found, now search along paths[] for .i file, then .c file. */ const si = FileName.forceExt(filename, i_ext); if (FileName.exists(si) == 1) @@ -227,18 +270,22 @@ nothrow: if (FileName.exists(sc) == 1) return sc; scope(exit) FileName.free(sc.ptr); - foreach (entry; path) + foreach (pathIndex, entry; pathsInfo) { - const p = entry.toDString(); + const p = entry.path.toDString(); const(char)[] n = FileName.combine(p, si); - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) + { + whichPathFoundThis = pathIndex; return n; } FileName.free(n.ptr); n = FileName.combine(p, sc); - if (FileName.exists(n) == 1) { + if (FileName.exists(n) == 1) + { + whichPathFoundThis = pathIndex; return n; } FileName.free(n.ptr); @@ -261,128 +308,23 @@ nothrow: if (auto val = files.lookup(name)) // if `name` is cached return val.value; // return its contents - if (name == "__stdin.d") // special name for reading from stdin - { - const ubyte[] buffer = readFromStdin().extractSlice(); - if (this.files.insert(name, buffer) is null) - // this.files already contains the name - assert(0, "stdin: Insert after lookup failure should never return `null`"); - return buffer; - } - if (FileName.exists(name) != 1) // if not an ordinary file return null; - auto readResult = File.read(name); - if (!readResult.success) - return null; + OutBuffer buf; + if (File.read(name, buf)) + return null; // failed - const ubyte[] fb = readResult.extractSlice(); + buf.write32(0); // terminating dchar 0 + + const length = buf.length; + const ubyte[] fb = cast(ubyte[])(buf.extractSlice()[0 .. length - 4]); if (files.insert(name, fb) is null) assert(0, "Insert after lookup failure should never return `null`"); return fb; } - /********************************** - * Take `text` and turn it into an InputRange that emits - * slices into `text` for each line. - * Params: - * text = array of characters - * Returns: - * InputRange accessing `text` as a sequence of lines - * Reference: - * `std.string.splitLines()` - */ - auto splitLines(const char[] text) - { - struct Range - { - @safe: - @nogc: - nothrow: - pure: - private: - - const char[] text; - size_t index; // index of start of line - size_t eolIndex; // index of end of line before newline characters - size_t nextIndex; // index past end of line - - public this(const char[] text) - { - this.text = text; - } - - public bool empty() { return index == text.length; } - - public void popFront() { advance(); index = nextIndex; } - - public const(char)[] front() { advance(); return text[index .. eolIndex]; } - - private void advance() - { - if (index != nextIndex) // if already advanced - return; - - for (size_t i = index; i < text.length; ++i) - { - switch (text[i]) - { - case '\v', '\f', '\n': - eolIndex = i; - nextIndex = i + 1; - return; - - case '\r': - if (i + 1 < text.length && text[i + 1] == '\n') // decode "\r\n" - { - eolIndex = i; - nextIndex = i + 2; - return; - } - eolIndex = i; - nextIndex = i + 1; - return; - - /* Manually decode: - * NEL is C2 85 - */ - case 0xC2: - if (i + 1 < text.length && text[i + 1] == 0x85) - { - eolIndex = i; - nextIndex = i + 2; - return; - } - break; - - /* Manually decode: - * lineSep is E2 80 A8 - * paraSep is E2 80 A9 - */ - case 0xE2: - if (i + 2 < text.length && - text[i + 1] == 0x80 && - (text[i + 2] == 0xA8 || text[i + 2] == 0xA9) - ) - { - eolIndex = i; - nextIndex = i + 3; - return; - } - break; - - default: - break; - } - } - } - } - - return Range(text); - } - /** * Adds the contents of a file to the table. * Params: @@ -397,46 +339,3 @@ nothrow: return val == null ? null : val.value; } } - -private Buffer readFromStdin() nothrow -{ - import core.stdc.stdio; - import dmd.errors; - import dmd.root.rmem; - - enum bufIncrement = 128 * 1024; - size_t pos = 0; - size_t sz = bufIncrement; - - ubyte* buffer = null; - for (;;) - { - buffer = cast(ubyte*)mem.xrealloc(buffer, sz + 4); // +2 for sentinel and +2 for lexer - - // Fill up buffer - do - { - assert(sz > pos); - size_t rlen = fread(buffer + pos, 1, sz - pos, stdin); - pos += rlen; - if (ferror(stdin)) - { - import core.stdc.errno; - error(Loc.initial, "cannot read from stdin, errno = %d", errno); - fatal(); - } - if (feof(stdin)) - { - // We're done - assert(pos < sz + 2); - buffer[pos .. pos + 4] = '\0'; - return Buffer(buffer[0 .. pos]); - } - } while (pos < sz); - - // Buffer full, expand - sz += bufIncrement; - } - - assert(0); -} diff --git a/gcc/d/dmd/func.d b/gcc/d/dmd/func.d index 7003c2b..064b67f 100644 --- a/gcc/d/dmd/func.d +++ b/gcc/d/dmd/func.d @@ -8,12 +8,12 @@ * - `invariant` * - `unittest` * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/func.d, _func.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/func.d, _func.d) * Documentation: https://dlang.org/phobos/dmd_func.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/func.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/func.d */ module dmd.func; @@ -29,15 +29,14 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.delegatize; -import dmd.dinterpret; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; import dmd.dsymbol; import dmd.dtemplate; -import dmd.errors; import dmd.escape; import dmd.expression; +import dmd.funcsem : overloadApply; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -46,17 +45,12 @@ import dmd.init; import dmd.location; import dmd.mtype; import dmd.objc; -import dmd.root.aav; import dmd.common.outbuffer; import dmd.rootobject; import dmd.root.string; import dmd.root.stringtable; -import dmd.semantic2; -import dmd.semantic3; -import dmd.statement_rewrite_walker; import dmd.statement; import dmd.tokens; -import dmd.typesem; import dmd.visitor; version (IN_GCC) {} @@ -109,7 +103,8 @@ enum BUILTIN : ubyte yl2xp1, toPrecFloat, toPrecDouble, - toPrecReal + toPrecReal, + ctfeWrite, } private struct FUNCFLAG @@ -118,9 +113,9 @@ private struct FUNCFLAG bool safetyInprocess; /// working on determining safety bool nothrowInprocess; /// working on determining nothrow bool nogcInprocess; /// working on determining @nogc - bool returnInprocess; /// working on inferring 'return' for parameters + bool saferD; /// do -preview=safer checks if this function has default safety + bool scopeInprocess; /// infer `return` and `scope` for parameters bool inlineScanned; /// function has been scanned for inline possibilities - bool inferScope; /// infer 'scope' for parameters bool hasCatches; /// function has try-catch statements bool skipCodegen; /// do not generate code for this function. bool printf; /// is a printf-like function @@ -143,6 +138,10 @@ private struct FUNCFLAG bool computedEscapingSiblings; /// `hasEscapingSiblings` has been computed bool dllImport; /// __declspec(dllimport) bool dllExport; /// __declspec(dllexport) + + bool hasReturnExp; /// Has return exp; statement + bool hasInlineAsm; /// Has asm{} statement + bool hasMultipleReturnExp; /// Has multiple return exp; statements } /*********************************************************** @@ -164,15 +163,12 @@ extern (C++) struct Ensure */ static Ensures* arraySyntaxCopy(Ensures* a) { - Ensures* b = null; - if (a) - { - b = a.copy(); - foreach (i, e; *a) - { - (*b)[i] = e.syntaxCopy(); - } - } + if (!a) + return null; + + Ensures* b = a.copy(); + foreach (i, e; *a) + (*b)[i] = e.syntaxCopy(); return b; } @@ -239,17 +235,10 @@ extern (C++) class FuncDeclaration : Declaration */ Type tintro; - StorageClass storage_class2; /// storage class for template onemember's + STC storage_class2; /// storage class for template onemember's // Things that should really go into Scope - /// 1 if there's a return exp; statement - /// 2 if there's a throw statement - /// 4 if there's an assert(0) - /// 8 if there's inline asm - /// 16 if there are multiple return statements - int hasReturnExp; - VarDeclaration nrvo_var; /// variable to replace with shidden Symbol* shidden; /// hidden pointer passed to function @@ -281,10 +270,14 @@ extern (C++) class FuncDeclaration : Declaration */ VarDeclarations outerVars; + // Most recent encountered `main` (`WinMain` or `DllMain`) function. + // Track it to give error messages for multiple entrypoints + __gshared FuncDeclaration lastMain; + /// Sibling nested functions which called this one FuncDeclarations siblingCallers; - FuncDeclarations *inlinedNestedCallees; + FuncDeclarations* inlinedNestedCallees; /// In case of failed `@safe` inference, store the error that made the function `@system` for /// better diagnostics @@ -303,9 +296,9 @@ extern (C++) class FuncDeclaration : Declaration */ ObjcFuncDeclaration objc; - extern (D) this(const ref Loc loc, const ref Loc endloc, Identifier ident, StorageClass storage_class, Type type, bool noreturn = false) + extern (D) this(Loc loc, Loc endloc, Identifier ident, STC storage_class, Type type, bool noreturn = false) { - super(loc, ident); + super(DSYM.funcDeclaration, loc, ident); //.printf("FuncDeclaration(id = '%s', type = %s)\n", ident.toChars(), type.toChars()); //.printf("storage_class = x%llx\n", storage_class); this.storage_class = storage_class; @@ -327,9 +320,9 @@ extern (C++) class FuncDeclaration : Declaration this.inferRetType = true; } - static FuncDeclaration create(const ref Loc loc, const ref Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false) + static FuncDeclaration create(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type, bool noreturn = false) { - return new FuncDeclaration(loc, endloc, id, storage_class, type, noreturn); + return new FuncDeclaration(loc, endloc, id, cast(STC) storage_class, type, noreturn); } final nothrow pure @safe @@ -386,61 +379,38 @@ extern (C++) class FuncDeclaration : Declaration if (this == o) return true; - if (auto s = isDsymbol(o)) - { - auto fd1 = this; - auto fd2 = s.isFuncDeclaration(); - if (!fd2) - return false; + auto s = isDsymbol(o); + if (!s) + return false; - auto fa1 = fd1.isFuncAliasDeclaration(); - auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; + auto fd1 = this; + auto fd2 = s.isFuncDeclaration(); + if (!fd2) + return false; - auto fa2 = fd2.isFuncAliasDeclaration(); - auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; + auto fa1 = fd1.isFuncAliasDeclaration(); + auto faf1 = fa1 ? fa1.toAliasFunc() : fd1; - if (fa1 && fa2) - { - return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; - } + auto fa2 = fd2.isFuncAliasDeclaration(); + auto faf2 = fa2 ? fa2.toAliasFunc() : fd2; - bool b1 = fa1 !is null; - if (b1 && faf1.isUnique() && !fa1.hasOverloads) - b1 = false; + if (fa1 && fa2) + return faf1.equals(faf2) && fa1.hasOverloads == fa2.hasOverloads; - bool b2 = fa2 !is null; - if (b2 && faf2.isUnique() && !fa2.hasOverloads) - b2 = false; + bool b1 = fa1 !is null; + if (b1 && faf1.isUnique() && !fa1.hasOverloads) + b1 = false; - if (b1 != b2) - return false; + bool b2 = fa2 !is null; + if (b2 && faf2.isUnique() && !fa2.hasOverloads) + b2 = false; - return faf1.toParent().equals(faf2.toParent()) && - faf1.ident.equals(faf2.ident) && - faf1.type.equals(faf2.type); - } - return false; - } + if (b1 != b2) + return false; - /**************************************************** - * Determine if 'this' overrides fd. - * Return !=0 if it does. - */ - extern (D) final int overrides(FuncDeclaration fd) - { - int result = 0; - if (fd.ident == ident) - { - const cov = type.covariant(fd.type); - if (cov != Covariant.distinct) - { - ClassDeclaration cd1 = toParent().isClassDeclaration(); - ClassDeclaration cd2 = fd.toParent().isClassDeclaration(); - if (cd1 && cd2 && cd2.isBaseOf(cd1, null)) - result = 1; - } - } - return result; + return faf1.toParent().equals(faf2.toParent()) && + faf1.ident.equals(faf2.ident) && + faf1.type.equals(faf2.type); } /**************************************************** @@ -451,8 +421,7 @@ extern (C++) class FuncDeclaration : Declaration { //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s.toChars(), toChars()); assert(s != this); - AliasDeclaration ad = s.isAliasDeclaration(); - if (ad) + if (AliasDeclaration ad = s.isAliasDeclaration()) { if (overnext) return overnext.overloadInsert(ad); @@ -479,26 +448,6 @@ extern (C++) class FuncDeclaration : Declaration if (!fd) return false; - version (none) - { - /* Disable this check because: - * const void foo(); - * semantic() isn't run yet on foo(), so the const hasn't been - * applied yet. - */ - if (type) - { - printf("type = %s\n", type.toChars()); - printf("fd.type = %s\n", fd.type.toChars()); - } - // fd.type can be NULL for overloaded constructors - if (type && fd.type && fd.type.covariant(type) && fd.type.mod == type.mod && !isFuncAliasDeclaration()) - { - //printf("\tfalse: conflict %s\n", kind()); - return false; - } - } - if (overnext) { td = overnext.isTemplateDeclaration(); @@ -513,149 +462,6 @@ extern (C++) class FuncDeclaration : Declaration } /******************************************** - * Find function in overload list that exactly matches t. - */ - extern (D) final FuncDeclaration overloadExactMatch(Type t) - { - FuncDeclaration fd; - overloadApply(this, (Dsymbol s) - { - auto f = s.isFuncDeclaration(); - if (!f) - return 0; - if (f.storage_class & STC.disable) - return 0; - if (t.equals(f.type)) - { - fd = f; - return 1; - } - - /* Allow covariant matches, as long as the return type - * is just a const conversion. - * This allows things like pure functions to match with an impure function type. - */ - if (t.ty == Tfunction) - { - auto tf = cast(TypeFunction)f.type; - if (tf.covariant(t) == Covariant.yes && - tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) - { - fd = f; - return 1; - } - } - return 0; - }); - return fd; - } - - /******************************************** - * Find function in overload list that matches to the 'this' modifier. - * There's four result types. - * - * 1. If the 'tthis' matches only one candidate, it's an "exact match". - * Returns the function and 'hasOverloads' is set to false. - * eg. If 'tthis" is mutable and there's only one mutable method. - * 2. If there's two or more match candidates, but a candidate function will be - * a "better match". - * Returns the better match function but 'hasOverloads' is set to true. - * eg. If 'tthis' is mutable, and there's both mutable and const methods, - * the mutable method will be a better match. - * 3. If there's two or more match candidates, but there's no better match, - * Returns null and 'hasOverloads' is set to true to represent "ambiguous match". - * eg. If 'tthis' is mutable, and there's two or more mutable methods. - * 4. If there's no candidates, it's "no match" and returns null with error report. - * e.g. If 'tthis' is const but there's no const methods. - */ - extern (D) final FuncDeclaration overloadModMatch(const ref Loc loc, Type tthis, ref bool hasOverloads) - { - //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars()); - MatchAccumulator m; - overloadApply(this, (Dsymbol s) - { - auto f = s.isFuncDeclaration(); - if (!f || f == m.lastf) // skip duplicates - return 0; - - auto tf = f.type.toTypeFunction(); - //printf("tf = %s\n", tf.toChars()); - - MATCH match; - if (tthis) // non-static functions are preferred than static ones - { - if (f.needThis()) - match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod); - else - match = MATCH.constant; // keep static function in overload candidates - } - else // static functions are preferred than non-static ones - { - if (f.needThis()) - match = MATCH.convert; - else - match = MATCH.exact; - } - if (match == MATCH.nomatch) - return 0; - - if (match > m.last) goto LcurrIsBetter; - if (match < m.last) goto LlastIsBetter; - - // See if one of the matches overrides the other. - if (m.lastf.overrides(f)) goto LlastIsBetter; - if (f.overrides(m.lastf)) goto LcurrIsBetter; - - //printf("\tambiguous\n"); - m.nextf = f; - m.count++; - return 0; - - LlastIsBetter: - //printf("\tlastbetter\n"); - m.count++; // count up - return 0; - - LcurrIsBetter: - //printf("\tisbetter\n"); - if (m.last <= MATCH.convert) - { - // clear last secondary matching - m.nextf = null; - m.count = 0; - } - m.last = match; - m.lastf = f; - m.count++; // count up - return 0; - }); - - if (m.count == 1) // exact match - { - hasOverloads = false; - } - else if (m.count > 1) // better or ambiguous match - { - hasOverloads = true; - } - else // no match - { - hasOverloads = true; - auto tf = this.type.toTypeFunction(); - assert(tthis); - assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch - { - OutBuffer thisBuf, funcBuf; - MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); - MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); - .error(loc, "%smethod %s is not callable using a %sobject", kind, toPrettyChars, - funcBuf.peekChars(), this.toPrettyChars(), thisBuf.peekChars()); - } - } - return m.lastf; - } - - /******************************************** * find function template root in overload list */ extern (D) final TemplateDeclaration findTemplateDeclRoot() @@ -664,8 +470,7 @@ extern (C++) class FuncDeclaration : Declaration while (f && f.overnext) { //printf("f.overnext = %p %s\n", f.overnext, f.overnext.toChars()); - TemplateDeclaration td = f.overnext.isTemplateDeclaration(); - if (td) + if (TemplateDeclaration td = f.overnext.isTemplateDeclaration()) return td; f = f.overnext.isFuncDeclaration(); } @@ -689,91 +494,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /************************************* - * Determine partial specialization order of functions `f` vs `g`. - * This is very similar to TemplateDeclaration::leastAsSpecialized(). - * Params: - * f = first function - * g = second function - * names = names of parameters - * Returns: - * match 'this' is at least as specialized as g - * 0 g is more specialized than 'this' - */ - static MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* names) - { - enum LOG_LEASTAS = 0; - static if (LOG_LEASTAS) - { - import core.stdc.stdio : printf; - printf("leastAsSpecialized(%s, %s, %s)\n", f.toChars(), g.toChars(), names ? names.toChars() : "null"); - printf("%s, %s\n", f.type.toChars(), g.type.toChars()); - } - - /* This works by calling g() with f()'s parameters, and - * if that is possible, then f() is at least as specialized - * as g() is. - */ - - TypeFunction tf = f.type.toTypeFunction(); - TypeFunction tg = g.type.toTypeFunction(); - - /* If both functions have a 'this' pointer, and the mods are not - * the same and g's is not const, then this is less specialized. - */ - if (f.needThis() && g.needThis() && tf.mod != tg.mod) - { - if (f.isCtorDeclaration()) - { - if (!MODimplicitConv(tg.mod, tf.mod)) - return MATCH.nomatch; - } - else - { - if (!MODimplicitConv(tf.mod, tg.mod)) - return MATCH.nomatch; - } - } - - /* Create a dummy array of arguments out of the parameters to f() - */ - Expressions args; - foreach (u, p; tf.parameterList) - { - Expression e; - if (p.isReference()) - { - e = new IdentifierExp(Loc.initial, p.ident); - e.type = p.type; - } - else - e = p.type.defaultInitLiteral(Loc.initial); - args.push(e); - } - - MATCH m = tg.callMatch(null, ArgumentList(&args, names), 1); - if (m > MATCH.nomatch) - { - /* A variadic parameter list is less specialized than a - * non-variadic one. - */ - if (tf.parameterList.varargs && !tg.parameterList.varargs) - goto L1; // less specialized - - static if (LOG_LEASTAS) - { - printf(" matches %d, so is least as specialized\n", m); - } - return m; - } - L1: - static if (LOG_LEASTAS) - { - printf(" doesn't match, so is not as specialized\n"); - } - return MATCH.nomatch; - } - /******************************** * Searches for a label with the given identifier. This function will insert a new * `LabelDsymbol` into `labtab` if it does not contain a mapping for `ident`. @@ -784,7 +504,7 @@ extern (C++) class FuncDeclaration : Declaration * * Returns: the `LabelDsymbol` for `ident` */ - final LabelDsymbol searchLabel(Identifier ident, const ref Loc loc) + final LabelDsymbol searchLabel(Identifier ident, Loc loc) { Dsymbol s; if (!labtab) @@ -855,51 +575,13 @@ extern (C++) class FuncDeclaration : Declaration return level; } - /*********************************** - * Determine lexical level difference from `this` to nested function `fd`. - * Issue error if `this` cannot call `fd`. - * - * Params: - * loc = location for error messages - * sc = context - * fd = target of call - * decl = The `Declaration` that triggered this check. - * Used to provide a better error message only. - * Returns: - * 0 same level - * >0 decrease nesting by number - * -1 increase nesting by 1 (`fd` is nested within 'this') - * LevelError error - */ - extern (D) final int getLevelAndCheck(const ref Loc loc, Scope* sc, FuncDeclaration fd, - Declaration decl) - { - int level = getLevel(fd, sc.intypeof); - if (level != LevelError) - return level; - - // Don't give error if in template constraint - if (!(sc.flags & SCOPE.constraint)) - { - const(char)* xstatic = isStatic() ? "`static` " : ""; - // better diagnostics for static functions - .error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`", - xstatic, kind(), toPrettyChars(), decl.kind(), decl.toChars(), - fd.toPrettyChars()); - .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars()); - return LevelError; - } - return 1; - } - enum LevelError = -2; override const(char)* toPrettyChars(bool QualifyTypes = false) { if (isMain()) return "D main"; - else - return Dsymbol.toPrettyChars(QualifyTypes); + return Dsymbol.toPrettyChars(QualifyTypes); } /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ @@ -990,293 +672,31 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /********************************** - * Decide if attributes for this function can be inferred from examining - * the function body. - * Returns: - * true if can - */ - final bool canInferAttributes(Scope* sc) - { - if (!fbody) - return false; - - if (isVirtualMethod() && - /* - * https://issues.dlang.org/show_bug.cgi?id=21719 - * - * If we have an auto virtual function we can infer - * the attributes. - */ - !(inferRetType && !isCtorDeclaration())) - return false; // since they may be overridden - - if (sc.func && - /********** this is for backwards compatibility for the moment ********/ - (!isMember() || sc.func.isSafeBypassingInference() && !isInstantiated())) - return true; - - if (isFuncLiteralDeclaration() || // externs are not possible with literals - (storage_class & STC.inference) || // do attribute inference - (inferRetType && !isCtorDeclaration())) - return true; - - if (isInstantiated()) - { - auto ti = parent.isTemplateInstance(); - if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == ident) - return true; - } - - return false; - } - - /***************************************** - * Initialize for inferring the attributes of this function. - */ - final void initInferAttributes() - { - //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); - TypeFunction tf = type.toTypeFunction(); - if (tf.purity == PURE.impure) // purity not specified - purityInprocess = true; - - if (tf.trust == TRUST.default_) - safetyInprocess = true; - - if (!tf.isnothrow) - nothrowInprocess = true; - - if (!tf.isnogc) - nogcInprocess = true; - - if (!isVirtual() || this.isIntroducing()) - returnInprocess = true; - - // Initialize for inferring STC.scope_ - inferScope = true; - } - - final PURE isPure() - { - //printf("FuncDeclaration::isPure() '%s'\n", toChars()); - - - TypeFunction tf = type.toTypeFunction(); - if (purityInprocess) - setImpure(); - if (tf.purity == PURE.fwdref) - tf.purityLevel(); - PURE purity = tf.purity; - if (purity > PURE.weak && isNested()) - purity = PURE.weak; - if (purity > PURE.weak && needThis()) - { - // The attribute of the 'this' reference affects purity strength - if (type.mod & MODFlags.immutable_) - { - } - else if (type.mod & (MODFlags.const_ | MODFlags.wild) && purity >= PURE.const_) - purity = PURE.const_; - else - purity = PURE.weak; - } - tf.purity = purity; - // ^ This rely on the current situation that every FuncDeclaration has a - // unique TypeFunction. - return purity; - } - - extern (D) final PURE isPureBypassingInference() - { - if (purityInprocess) - return PURE.fwdref; - else - return isPure(); - } - - /************************************** - * The function is doing something impure, so mark it as impure. - * - * Params: - * loc = location of impure action - * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. - * arg0 = (optional) argument to format string - * - * Returns: `true` if there's a purity error - */ - extern (D) final bool setImpure(Loc loc = Loc.init, const(char)* fmt = null, RootObject arg0 = null) - { - if (purityInprocess) - { - purityInprocess = false; - if (fmt) - pureViolation = new AttributeViolation(loc, fmt, this, arg0); // impure action - else if (arg0) - pureViolation = new AttributeViolation(loc, fmt, arg0); // call to impure function - - if (fes) - fes.func.setImpure(loc, fmt, arg0); - } - else if (isPure()) - return true; - return false; - } - - extern (D) final uint flags() + extern (D) final uint saveFlags() { return bitFields; } - extern (D) final uint flags(uint f) + extern (D) final uint restoreFlags(uint f) { bitFields = f; return bitFields; } - final bool isSafe() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.safe; - } - - extern (D) final bool isSafeBypassingInference() - { - return !(safetyInprocess) && isSafe(); - } - - final bool isTrusted() - { - if (safetyInprocess) - setUnsafe(); - return type.toTypeFunction().trust == TRUST.trusted; - } - - /************************************** - * The function is doing something unsafe, so mark it as unsafe. - * - * Params: - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafe( - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) - { - if (safetyInprocess) - { - safetyInprocess = false; - type.toTypeFunction().trust = TRUST.system; - if (fmt || arg0) - safetyViolation = new AttributeViolation(loc, fmt, arg0, arg1, arg2); - - if (fes) - fes.func.setUnsafe(); - } - else if (isSafe()) - { - if (!gag && fmt) - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - - return true; - } - return false; - } - - /************************************** - * The function is calling `@system` function `f`, so mark it as unsafe. - * - * Params: - * f = function being called (needed for diagnostic of inferred functions) - * Returns: whether there's a safe error - */ - extern (D) final bool setUnsafeCall(FuncDeclaration f) - { - return setUnsafe(false, f.loc, null, f, null); - } - - final bool isNogc() - { - //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); - if (nogcInprocess) - setGC(loc, null); - return type.toTypeFunction().isnogc; - } - - extern (D) final bool isNogcBypassingInference() - { - return !nogcInprocess && isNogc(); - } - - /************************************** - * The function is doing something that may allocate with the GC, - * so mark it as not nogc (not no-how). - * - * Params: - * loc = location of impure action - * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. - * arg0 = (optional) argument to format string - * - * Returns: - * true if function is marked as @nogc, meaning a user error occurred - */ - extern (D) final bool setGC(Loc loc, const(char)* fmt, RootObject arg0 = null) - { - //printf("setGC() %s\n", toChars()); - if (nogcInprocess && semanticRun < PASS.semantic3 && _scope) - { - this.semantic2(_scope); - this.semantic3(_scope); - } - - if (nogcInprocess) - { - nogcInprocess = false; - if (fmt) - nogcViolation = new AttributeViolation(loc, fmt, this, arg0); // action that requires GC - else if (arg0) - nogcViolation = new AttributeViolation(loc, fmt, arg0); // call to non-@nogc function - - type.toTypeFunction().isnogc = false; - if (fes) - fes.func.setGC(Loc.init, null, null); - } - else if (isNogc()) - return true; - return false; - } - - /************************************** - * The function calls non-`@nogc` function f, mark it as not nogc. - * Params: - * f = function being called - * Returns: - * true if function is marked as @nogc, meaning a user error occurred - */ - extern (D) final bool setGCCall(FuncDeclaration f) - { - return setGC(loc, null, f); - } /************************************** * The function is doing something that may throw an exception, register that in case nothrow is being inferred * * Params: * loc = location of action - * fmt = format string for error message - * arg0 = (optional) argument to format string + * format = format string for error message + * args = arguments to format string */ - extern (D) final void setThrow(Loc loc, const(char)* fmt, RootObject arg0 = null) + extern (D) final void setThrow(Loc loc, const(char)* format, RootObject[] args...) { if (nothrowInprocess && !nothrowViolation) { - nothrowViolation = new AttributeViolation(loc, fmt, arg0); // action that requires GC + nothrowViolation = new AttributeViolation(loc, format, args); // action that requires GC } } @@ -1284,204 +704,14 @@ extern (C++) class FuncDeclaration : Declaration * The function calls non-`nothrow` function f, register that in case nothrow is being inferred * Params: * loc = location of call - * f = function being called - */ - extern (D) final void setThrowCall(Loc loc, FuncDeclaration f) - { - return setThrow(loc, null, f); - } - - extern (D) final void printGCUsage(const ref Loc loc, const(char)* warn) - { - if (!global.params.v.gc) - return; - - Module m = getModule(); - if (m && m.isRoot() && !inUnittest()) - { - message(loc, "vgc: %s", warn); - } - } - - /******************************************** - * See if pointers from function parameters, mutable globals, or uplevel functions - * could leak into return value. - * Returns: - * true if the function return value is isolated from - * any inputs to the function + * fd = function being called */ - extern (D) final bool isReturnIsolated() + extern (D) final void setThrowCall(Loc loc, FuncDeclaration fd) { - //printf("isReturnIsolated(this: %s)\n", this.toChars); - TypeFunction tf = type.toTypeFunction(); - assert(tf.next); - - Type treti = tf.next; - if (tf.isref) - return isTypeIsolatedIndirect(treti); // check influence from parameters - - return isTypeIsolated(treti); - } - - /******************** - * See if pointers from function parameters, mutable globals, or uplevel functions - * could leak into type `t`. - * Params: - * t = type to check if it is isolated - * Returns: - * true if `t` is isolated from - * any inputs to the function - */ - extern (D) final bool isTypeIsolated(Type t) - { - StringTable!Type parentTypes; - const uniqueTypeID = t.getUniqueID(); - if (uniqueTypeID) - { - const cacheResultPtr = uniqueTypeID in isTypeIsolatedCache; - if (cacheResultPtr !is null) - return *cacheResultPtr; - - parentTypes._init(); - const isIsolated = isTypeIsolated(t, parentTypes); - isTypeIsolatedCache[uniqueTypeID] = isIsolated; - return isIsolated; - } - else - { - parentTypes._init(); - return isTypeIsolated(t, parentTypes); - } - } - - ///ditto - extern (D) final bool isTypeIsolated(Type t, ref StringTable!Type parentTypes) - { - //printf("this: %s, isTypeIsolated(t: %s)\n", this.toChars(), t.toChars()); - - t = t.baseElemOf(); - switch (t.ty) - { - case Tarray: - case Tpointer: - return isTypeIsolatedIndirect(t.nextOf()); // go down one level - - case Taarray: - case Tclass: - return isTypeIsolatedIndirect(t); - - case Tstruct: - /* Drill down and check the struct's fields - */ - auto sym = t.toDsymbol(null).isStructDeclaration(); - const tName = t.toChars.toDString; - const entry = parentTypes.insert(tName, t); - if (entry == null) - { - //we've already seen this type in a parent, not isolated - return false; - } - foreach (v; sym.fields) - { - Type tmi = v.type.addMod(t.mod); - //printf("\tt = %s, v: %s, vtype: %s, tmi = %s\n", - // t.toChars(), v.toChars(), v.type.toChars(), tmi.toChars()); - if (!isTypeIsolated(tmi, parentTypes)) - return false; - } - return true; - - default: - return true; - } - } - - /******************************************** - * Params: - * t = type of object to test one level of indirection down - * Returns: - * true if an object typed `t` has no indirections - * which could have come from the function's parameters, mutable - * globals, or uplevel functions. - */ - private bool isTypeIsolatedIndirect(Type t) - { - //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars()); - assert(t); - - /* Since `t` is one level down from an indirection, it could pick - * up a reference to a mutable global or an outer function, so - * return false. - */ - if (!isPureBypassingInference() || isNested()) - return false; - - TypeFunction tf = type.toTypeFunction(); - - //printf("isTypeIsolatedIndirect(%s) t = %s\n", tf.toChars(), t.toChars()); - - foreach (i, fparam; tf.parameterList) - { - Type tp = fparam.type; - if (!tp) - continue; - - if (fparam.isLazy() || fparam.isReference()) - { - if (!traverseIndirections(tp, t)) - return false; - continue; - } - - /* Goes down one level of indirection, then calls traverseIndirection() on - * the result. - * Returns: - * true if t is isolated from tp - */ - static bool traverse(Type tp, Type t) - { - tp = tp.baseElemOf(); - switch (tp.ty) - { - case Tarray: - case Tpointer: - return traverseIndirections(tp.nextOf(), t); - - case Taarray: - case Tclass: - return traverseIndirections(tp, t); - - case Tstruct: - /* Drill down and check the struct's fields - */ - auto sym = tp.toDsymbol(null).isStructDeclaration(); - foreach (v; sym.fields) - { - Type tprmi = v.type.addMod(tp.mod); - //printf("\ttp = %s, tprmi = %s\n", tp.toChars(), tprmi.toChars()); - if (!traverse(tprmi, t)) - return false; - } - return true; - - default: - return true; - } - } - - if (!traverse(tp, t)) - return false; - } - // The 'this' reference is a parameter, too - if (AggregateDeclaration ad = isCtorDeclaration() ? null : isThis()) + if (nothrowInprocess && !nothrowViolation) { - Type tthis = ad.getType().addMod(tf.mod); - //printf("\ttthis = %s\n", tthis.toChars()); - if (!traverseIndirections(tthis, t)) - return false; + nothrowViolation = new AttributeViolation(loc, fd); // action that requires GC } - - return true; } /**************************************** @@ -1633,101 +863,6 @@ extern (C++) class FuncDeclaration : Declaration return result; } - /********************************************* - * In the current function, we are calling 'this' function. - * 1. Check to see if the current function can call 'this' function, issue error if not. - * 2. If the current function is not the parent of 'this' function, then add - * the current function to the list of siblings of 'this' function. - * 3. If the current function is a literal, and it's accessing an uplevel scope, - * then mark it as a delegate. - * Returns true if error occurs. - */ - extern (D) final bool checkNestedReference(Scope* sc, const ref Loc loc) - { - //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); - - if (auto fld = this.isFuncLiteralDeclaration()) - { - if (fld.tok == TOK.reserved) - { - fld.tok = TOK.function_; - fld.vthis = null; - } - } - - if (!parent || parent == sc.parent) - return false; - if (ident == Id.require || ident == Id.ensure) - return false; - if (!isThis() && !isNested()) - return false; - - // The current function - FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); - if (!fdthis) - return false; // out of function scope - - Dsymbol p = toParentLocal(); - Dsymbol p2 = toParent2(); - - // Function literals from fdthis to p must be delegates - ensureStaticLinkTo(fdthis, p); - if (p != p2) - ensureStaticLinkTo(fdthis, p2); - - if (isNested()) - { - // The function that this function is in - bool checkEnclosing(FuncDeclaration fdv) - { - if (!fdv) - return false; - if (fdv == fdthis) - return false; - - //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); - //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); - //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); - - // Add this function to the list of those which called us - if (fdthis != this) - { - bool found = false; - for (size_t i = 0; i < siblingCallers.length; ++i) - { - if (siblingCallers[i] == fdthis) - found = true; - } - if (!found) - { - //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); - if (!sc.intypeof && !(sc.flags & SCOPE.compile)) - { - siblingCallers.push(fdthis); - computedEscapingSiblings = false; - } - } - } - - const lv = fdthis.getLevelAndCheck(loc, sc, fdv, this); - if (lv == LevelError) - return true; // error - if (lv == -1) - return false; // downlevel call - if (lv == 0) - return false; // same level call - - return false; // Uplevel call - } - - if (checkEnclosing(p.isFuncDeclaration())) - return true; - if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) - return true; - } - return false; - } - /******************************* * Look at all the variables in this function that are referenced * by nested functions, and determine if a closure needs to be @@ -1822,75 +957,6 @@ extern (C++) class FuncDeclaration : Declaration } /*********************************************** - * Check that the function contains any closure. - * If it's @nogc, report suitable errors. - * This is mostly consistent with FuncDeclaration::needsClosure(). - * - * Returns: - * true if any errors occur. - */ - extern (C++) final bool checkClosure() - { - //printf("checkClosure() %s\n", toPrettyChars()); - if (!needsClosure()) - return false; - - if (setGC(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", this)) - { - .error(loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars()); - if (global.gag) // need not report supplemental errors - return true; - } - else if (!global.params.useGC) - { - .error(loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", kind, toPrettyChars, toChars()); - if (global.gag) // need not report supplemental errors - return true; - } - else - { - printGCUsage(loc, "using closure causes GC allocation"); - return false; - } - - FuncDeclarations a; - foreach (v; closureVars) - { - foreach (f; v.nestedrefs) - { - assert(f !is this); - - LcheckAncestorsOfANestedRef: - for (Dsymbol s = f; s && s !is this; s = s.toParentP(this)) - { - auto fx = s.isFuncDeclaration(); - if (!fx) - continue; - if (fx.isThis() || - fx.tookAddressOf || - checkEscapingSiblings(fx, this)) - { - foreach (f2; a) - { - if (f2 == f) - break LcheckAncestorsOfANestedRef; - } - a.push(f); - .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`", - f.kind, f.toPrettyChars(), v.toChars()); - if (v.ident != Id.This) - .errorSupplemental(v.loc, "`%s` declared here", v.toChars()); - - break LcheckAncestorsOfANestedRef; - } - } - } - } - - return true; - } - - /*********************************************** * Determine if function's variables are referenced by a function * nested within it. */ @@ -1922,394 +988,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /**************************************************** - * Check whether result variable can be built. - * Returns: - * `true` if the function has a return type that - * is different from `void`. - */ - extern (D) private bool canBuildResultVar() - { - auto f = cast(TypeFunction)type; - return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; - } - - /**************************************************** - * Merge into this function the 'in' contracts of all it overrides. - * 'in's are OR'd together, i.e. only one of them needs to pass. - */ - extern (D) final Statement mergeFrequire(Statement sf, Expressions* params) - { - /* If a base function and its override both have an IN contract, then - * only one of them needs to succeed. This is done by generating: - * - * void derived.in() { - * try { - * base.in(); - * } - * catch () { - * ... body of derived.in() ... - * } - * } - * - * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. - * If base.in() throws, then derived.in()'s body is executed. - */ - - foreach (fdv; foverrides) - { - /* The semantic pass on the contracts of the overridden functions must - * be completed before code generation occurs. - * https://issues.dlang.org/show_bug.cgi?id=3602 - */ - if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) - { - assert(fdv._scope); - Scope* sc = fdv._scope.push(); - sc.stc &= ~STC.override_; - fdv.semantic3(sc); - sc.pop(); - } - - sf = fdv.mergeFrequire(sf, params); - if (!sf || !fdv.fdrequire) - return null; - //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); - /* Make the call: - * try { __require(params); } - * catch (Throwable) { frequire; } - */ - params = Expression.arraySyntaxCopy(params); - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); - Statement s2 = new ExpStatement(loc, e); - - auto c = new Catch(loc, getThrowable(), null, sf); - c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); - sf = new TryCatchStatement(loc, s2, catches); - } - return sf; - } - - /**************************************************** - * Merge into this function the 'in' contracts of all it overrides. - */ - extern (D) final Statement mergeFrequireInclusivePreview(Statement sf, Expressions* params) - { - /* If a base function and its override both have an IN contract, then - * the override in contract must widen the guarantee of the base contract. - * This is checked by generating: - * - * void derived.in() { - * try { - * ... body of derived.in() ... - * } - * catch () { - * // derived in rejected this argument. so parent must also reject it, or we've tightened the contract. - * base.in(); - * assert(false, "Logic error: " ~ thr.msg); - * } - * } - */ - - foreach (fdv; foverrides) - { - /* The semantic pass on the contracts of the overridden functions must - * be completed before code generation occurs. - * https://issues.dlang.org/show_bug.cgi?id=3602 - */ - if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) - { - assert(fdv._scope); - Scope* sc = fdv._scope.push(); - sc.stc &= ~STC.override_; - fdv.semantic3(sc); - sc.pop(); - } - - sf = fdv.mergeFrequireInclusivePreview(sf, params); - if (sf && fdv.fdrequire) - { - const loc = this.fdrequire.loc; - - //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); - /* Make the call: - * try { frequire; } - * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } - */ - Identifier id = Identifier.generateId("thr"); - params = Expression.arraySyntaxCopy(params); - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); - Statement s2 = new ExpStatement(loc, e); - // assert(false, ...) - // TODO make this a runtime helper to allow: - // - chaining the original expression - // - nogc concatenation - Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); - Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); - - Statement s3 = new CompoundStatement(loc, s2, fail); - - auto c = new Catch(loc, getThrowable(), id, s3); - c.internalCatch = true; - auto catches = new Catches(); - catches.push(c); - sf = new TryCatchStatement(loc, sf, catches); - } - else - return null; - } - return sf; - } - - /**************************************************** - * Determine whether an 'out' contract is declared inside - * the given function or any of its overrides. - * Params: - * fd = the function to search - * Returns: - * true found an 'out' contract - */ - static bool needsFensure(FuncDeclaration fd) @safe - { - if (fd.fensures) - return true; - - foreach (fdv; fd.foverrides) - { - if (needsFensure(fdv)) - return true; - } - return false; - } - - /**************************************************** - * Rewrite contracts as statements. - */ - final void buildEnsureRequire() - { - - if (frequires) - { - /* in { statements1... } - * in { statements2... } - * ... - * becomes: - * in { { statements1... } { statements2... } ... } - */ - assert(frequires.length); - auto loc = (*frequires)[0].loc; - auto s = new Statements; - foreach (r; *frequires) - { - s.push(new ScopeStatement(r.loc, r, r.loc)); - } - frequire = new CompoundStatement(loc, s); - } - - if (fensures) - { - /* out(id1) { statements1... } - * out(id2) { statements2... } - * ... - * becomes: - * out(__result) { { ref id1 = __result; { statements1... } } - * { ref id2 = __result; { statements2... } } ... } - */ - assert(fensures.length); - auto loc = (*fensures)[0].ensure.loc; - auto s = new Statements; - foreach (r; *fensures) - { - if (r.id && canBuildResultVar()) - { - auto rloc = r.ensure.loc; - auto resultId = new IdentifierExp(rloc, Id.result); - auto init = new ExpInitializer(rloc, resultId); - auto stc = STC.ref_ | STC.temp | STC.result; - auto decl = new VarDeclaration(rloc, null, r.id, init, stc); - auto sdecl = new ExpStatement(rloc, decl); - s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); - } - else - { - s.push(r.ensure); - } - } - fensure = new CompoundStatement(loc, s); - } - - if (!isVirtual()) - return; - - /* Rewrite contracts as nested functions, then call them. Doing it as nested - * functions means that overriding functions can call them. - */ - TypeFunction f = cast(TypeFunction) type; - - /* Make a copy of the parameters and make them all ref */ - static Parameters* toRefCopy(ParameterList parameterList) - { - auto result = new Parameters(); - - foreach (n, p; parameterList) - { - p = p.syntaxCopy(); - if (!p.isLazy()) - p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_; - p.defaultArg = null; // won't be the same with ref - result.push(p); - } - - return result; - } - - if (frequire) - { - /* in { ... } - * becomes: - * void __require(ref params) { ... } - * __require(params); - */ - Loc loc = frequire.loc; - fdrequireParams = new Expressions(); - if (parameters) - { - foreach (vd; *parameters) - fdrequireParams.push(new VarExp(loc, vd)); - } - auto fo = cast(TypeFunction)(originalType ? originalType : f); - auto fparams = toRefCopy(fo.parameterList); - auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); - tf.isnothrow = f.isnothrow; - tf.isnogc = f.isnogc; - tf.purity = f.purity; - tf.trust = f.trust; - auto fd = new FuncDeclaration(loc, loc, Id.require, STC.undefined_, tf); - fd.fbody = frequire; - Statement s1 = new ExpStatement(loc, fd); - Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdrequireParams); - Statement s2 = new ExpStatement(loc, e); - frequire = new CompoundStatement(loc, s1, s2); - fdrequire = fd; - } - - /* We need to set fdensureParams here and not in the block below to - * have the parameters available when calling a base class ensure(), - * even if this function doesn't have an out contract. - */ - fdensureParams = new Expressions(); - if (canBuildResultVar()) - fdensureParams.push(new IdentifierExp(loc, Id.result)); - if (parameters) - { - foreach (vd; *parameters) - fdensureParams.push(new VarExp(loc, vd)); - } - - if (fensure) - { - /* out (result) { ... } - * becomes: - * void __ensure(ref tret result, ref params) { ... } - * __ensure(result, params); - */ - Loc loc = fensure.loc; - auto fparams = new Parameters(); - if (canBuildResultVar()) - { - Parameter p = new Parameter(loc, STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); - fparams.push(p); - } - auto fo = cast(TypeFunction)(originalType ? originalType : f); - fparams.pushSlice((*toRefCopy(fo.parameterList))[]); - auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); - tf.isnothrow = f.isnothrow; - tf.isnogc = f.isnogc; - tf.purity = f.purity; - tf.trust = f.trust; - auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.undefined_, tf); - fd.fbody = fensure; - Statement s1 = new ExpStatement(loc, fd); - Expression e = new CallExp(loc, new VarExp(loc, fd, false), fdensureParams); - Statement s2 = new ExpStatement(loc, e); - fensure = new CompoundStatement(loc, s1, s2); - fdensure = fd; - } - } - - /**************************************************** - * Merge into this function the 'out' contracts of all it overrides. - * 'out's are AND'd together, i.e. all of them need to pass. - */ - extern (D) final Statement mergeFensure(Statement sf, Identifier oid, Expressions* params) - { - /* Same comments as for mergeFrequire(), except that we take care - * of generating a consistent reference to the 'result' local by - * explicitly passing 'result' to the nested function as a reference - * argument. - * This won't work for the 'this' parameter as it would require changing - * the semantic code for the nested function so that it looks on the parameter - * list for the 'this' pointer, something that would need an unknown amount - * of tweaking of various parts of the compiler that I'd rather leave alone. - */ - foreach (fdv; foverrides) - { - /* The semantic pass on the contracts of the overridden functions must - * be completed before code generation occurs. - * https://issues.dlang.org/show_bug.cgi?id=3602 and - * https://issues.dlang.org/show_bug.cgi?id=5230 - */ - if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) - { - assert(fdv._scope); - Scope* sc = fdv._scope.push(); - sc.stc &= ~STC.override_; - fdv.semantic3(sc); - sc.pop(); - } - - sf = fdv.mergeFensure(sf, oid, params); - if (fdv.fdensure) - { - //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); - // Make the call: __ensure(result, params) - params = Expression.arraySyntaxCopy(params); - if (canBuildResultVar()) - { - Type t1 = fdv.type.nextOf().toBasetype(); - Type t2 = this.type.nextOf().toBasetype(); - if (t1.isBaseOf(t2, null)) - { - /* Making temporary reference variable is necessary - * in covariant return. - * https://issues.dlang.org/show_bug.cgi?id=5204 - * https://issues.dlang.org/show_bug.cgi?id=10479 - */ - Expression* eresult = &(*params)[0]; - auto ei = new ExpInitializer(Loc.initial, *eresult); - auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); - v.storage_class |= STC.temp; - auto de = new DeclarationExp(Loc.initial, v); - auto ve = new VarExp(Loc.initial, v); - *eresult = new CommaExp(Loc.initial, de, ve); - } - } - Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, false), params); - Statement s2 = new ExpStatement(loc, e); - - if (sf) - { - sf = new CompoundStatement(sf.loc, s2, sf); - } - else - sf = s2; - } - } - return sf; - } - /********************************************* * Returns: the function's parameter list, and whether * it is variadic or not. @@ -2318,8 +996,7 @@ extern (C++) class FuncDeclaration : Declaration { if (type) { - TypeFunction fdtype = type.isTypeFunction(); - if (fdtype) // Could also be TypeError + if (TypeFunction fdtype = type.isTypeFunction()) // Could also be TypeError return fdtype.parameterList; } @@ -2329,12 +1006,12 @@ extern (C++) class FuncDeclaration : Declaration /********************************** * Generate a FuncDeclaration for a runtime library function. */ - static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, StorageClass stc = 0) + extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, const(char)* name, STC stc = STC.none) { return genCfunc(fparams, treturn, Identifier.idPool(name[0 .. strlen(name)]), stc); } - static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, StorageClass stc = 0) + extern(D) static FuncDeclaration genCfunc(Parameters* fparams, Type treturn, Identifier id, STC stc = STC.none) { FuncDeclaration fd; TypeFunction tf; @@ -2366,185 +1043,7 @@ extern (C++) class FuncDeclaration : Declaration return fd; } - /+ - + Checks the parameter and return types iff this is a `main` function. - + - + The following signatures are allowed for a `D main`: - + - Either no or a single parameter of type `string[]` - + - Return type is either `void`, `int` or `noreturn` - + - + The following signatures are standard C: - + - `int main()` - + - `int main(int, char**)` - + - + This function accepts the following non-standard extensions: - + - `char** envp` as a third parameter - + - `void` / `noreturn` as return type - + - + This function will issue errors for unexpected arguments / return types. - +/ - extern (D) final void checkMain() - { - if (ident != Id.main || isMember() || isNested()) - return; // Not a main function - - TypeFunction tf = type.toTypeFunction(); - - Type retType = tf.nextOf(); - if (!retType) - { - // auto main(), check after semantic - assert(this.inferRetType); - return; - } - - /// Checks whether `t` is equivalent to `char**` - /// Ignores qualifiers and treats enums according to their base type - static bool isCharPtrPtr(Type t) - { - auto tp = t.toBasetype().isTypePointer(); - if (!tp) - return false; - - tp = tp.next.toBasetype().isTypePointer(); - if (!tp) - return false; - - return tp.next.toBasetype().ty == Tchar; - } - - // Neither of these qualifiers is allowed because they affect the ABI - enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_; - - const nparams = tf.parameterList.length; - bool argerr; - - const linkage = resolvedLinkage(); - if (linkage == LINK.d) - { - if (nparams == 1) - { - auto fparam0 = tf.parameterList[0]; - auto t = fparam0.type.toBasetype(); - if (t.ty != Tarray || - t.nextOf().ty != Tarray || - t.nextOf().nextOf().ty != Tchar || - fparam0.storageClass & invalidSTC) - { - argerr = true; - } - } - - if (tf.parameterList.varargs || nparams >= 2 || argerr) - .error(loc, "%s `%s` parameter list must be empty or accept one parameter of type `string[]`", kind, toPrettyChars); - } - - else if (linkage == LINK.c) - { - if (nparams == 2 || nparams == 3) - { - // Argument count must be int - auto argCount = tf.parameterList[0]; - argerr |= !!(argCount.storageClass & invalidSTC); - argerr |= argCount.type.toBasetype().ty != Tint32; - - // Argument pointer must be char** - auto argPtr = tf.parameterList[1]; - argerr |= !!(argPtr.storageClass & invalidSTC); - argerr |= !isCharPtrPtr(argPtr.type); - - // `char** environ` is a common extension, see J.5.1 of the C standard - if (nparams == 3) - { - auto envPtr = tf.parameterList[2]; - argerr |= !!(envPtr.storageClass & invalidSTC); - argerr |= !isCharPtrPtr(envPtr.type); - } - } - else - argerr = nparams != 0; - - // Disallow variadic main() - except for K&R declarations in C files. - // E.g. int main(), int main(argc, argv) int argc, char** argc { ... } - if (tf.parameterList.varargs && (!this.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams))) - argerr |= true; - - if (argerr) - { - .error(loc, "%s `%s` parameters must match one of the following signatures", kind, toPrettyChars); - loc.errorSupplemental("`main()`"); - loc.errorSupplemental("`main(int argc, char** argv)`"); - loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]"); - } - } - else - return; // Neither C nor D main, ignore (should probably be an error) - - // Allow enums with appropriate base types (same ABI) - retType = retType.toBasetype(); - - if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn) - .error(loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", kind, toPrettyChars, tf.nextOf().toChars()); - } - - /*********************************************** - * Check all return statements for a function to verify that returning - * using NRVO is possible. - * - * Returns: - * `false` if the result cannot be returned by hidden reference. - */ - extern (D) final bool checkNRVO() - { - if (!isNRVO() || returns is null) - return false; - - auto tf = type.toTypeFunction(); - if (tf.isref) - return false; - - foreach (rs; *returns) - { - if (auto ve = rs.exp.isVarExp()) - { - auto v = ve.var.isVarDeclaration(); - if (!v || v.isReference()) - return false; - else if (nrvo_var is null) - { - // Variables in the data segment (e.g. globals, TLS or not), - // parameters and closure variables cannot be NRVOed. - if (v.isDataseg() || v.isParameter() || v.toParent2() != this) - return false; - if (v.nestedrefs.length && needsClosure()) - return false; - // don't know if the return storage is aligned - version (MARS) - { - if (alignSectionVars && (*alignSectionVars).contains(v)) - return false; - } - // The variable type needs to be equivalent to the return type. - if (!v.type.equivalent(tf.next)) - return false; - //printf("Setting nrvo to %s\n", v.toChars()); - nrvo_var = v; - } - else if (nrvo_var != v) - return false; - } - else //if (!exp.isLvalue()) // keep NRVO-ability - return false; - } - return true; - } - - override final inout(FuncDeclaration) isFuncDeclaration() inout - { - return this; - } - - inout(FuncDeclaration) toAliasFunc() inout + inout(FuncDeclaration) toAliasFunc() inout @safe { return this; } @@ -2555,118 +1054,6 @@ extern (C++) class FuncDeclaration : Declaration } } -/*************************************************** - * Visit each overloaded function/template in turn, and call dg(s) on it. - * Exit when no more, or dg(s) returns nonzero. - * - * Params: - * fstart = symbol to start from - * dg = the delegate to be called on the overload - * sc = context used to check if symbol is accessible (and therefore visible), - * can be null - * - * Returns: - * ==0 continue - * !=0 done (and the return value from the last dg() call) - */ -extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) -{ - Dsymbols visited; - - int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc) - { - // Detect cyclic calls. - if (visited.contains(fstart)) - return 0; - visited.push(fstart); - - Dsymbol next; - for (auto d = fstart; d; d = next) - { - import dmd.access : checkSymbolAccess; - if (auto od = d.isOverDeclaration()) - { - /* The scope is needed here to check whether a function in - an overload set was added by means of a private alias (or a - selective import). If the scope where the alias is created - is imported somewhere, the overload set is visible, but the private - alias is not. - */ - if (sc) - { - if (checkSymbolAccess(sc, od)) - { - if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) - return r; - } - } - else if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) - return r; - next = od.overnext; - } - else if (auto fa = d.isFuncAliasDeclaration()) - { - if (fa.hasOverloads) - { - if (int r = overloadApplyRecurse(fa.funcalias, dg, sc)) - return r; - } - else if (auto fd = fa.toAliasFunc()) - { - if (int r = dg(fd)) - return r; - } - else - { - .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); - break; - } - next = fa.overnext; - } - else if (auto ad = d.isAliasDeclaration()) - { - if (sc) - { - if (checkSymbolAccess(sc, ad)) - next = ad.toAlias(); - } - else - next = ad.toAlias(); - if (next == ad) - break; - if (next == fstart) - break; - } - else if (auto td = d.isTemplateDeclaration()) - { - if (int r = dg(td)) - return r; - next = td.overnext; - } - else if (auto fd = d.isFuncDeclaration()) - { - if (int r = dg(fd)) - return r; - next = fd.overnext; - } - else if (auto os = d.isOverloadSet()) - { - foreach (ds; os.a) - if (int r = dg(ds)) - return r; - } - else - { - .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); - break; - // BUG: should print error message? - } - } - return 0; - } - return overloadApplyRecurse(fstart, dg, sc); -} - /** Checks for mismatching modifiers between `lhsMod` and `rhsMod` and prints the mismatching modifiers to `buf`. @@ -2748,133 +1135,6 @@ unittest assert(mismatches.isMutable); } -/************************************** - * Returns an indirect type one step from t. - */ -Type getIndirection(Type t) -{ - t = t.baseElemOf(); - if (t.ty == Tarray || t.ty == Tpointer) - return t.nextOf().toBasetype(); - if (t.ty == Taarray || t.ty == Tclass) - return t; - if (t.ty == Tstruct) - return t.hasPointers() ? t : null; // TODO - - // should consider TypeDelegate? - return null; -} - -/************************************** - * Performs type-based alias analysis between a newly created value and a pre- - * existing memory reference: - * - * Assuming that a reference A to a value of type `ta` was available to the code - * that created a reference B to a value of type `tb`, it returns whether B - * might alias memory reachable from A based on the types involved (either - * directly or via any number of indirections in either A or B). - * - * This relation is not symmetric in the two arguments. For example, a - * a `const(int)` reference can point to a pre-existing `int`, but not the other - * way round. - * - * Examples: - * - * ta, tb, result - * `const(int)`, `int`, `false` - * `int`, `const(int)`, `true` - * `int`, `immutable(int)`, `false` - * const(immutable(int)*), immutable(int)*, false // BUG: returns true - * - * Params: - * ta = value type being referred to - * tb = referred to value type that could be constructed from ta - * - * Returns: - * true if reference to `tb` is isolated from reference to `ta` - */ -private bool traverseIndirections(Type ta, Type tb) -{ - //printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars()); - - static bool traverse(Type ta, Type tb, ref scope AssocArray!(const(char)*, bool) table, bool reversePass) - { - //printf("traverse(%s, %s)\n", ta.toChars(), tb.toChars()); - ta = ta.baseElemOf(); - tb = tb.baseElemOf(); - - // First, check if the pointed-to types are convertible to each other such - // that they might alias directly. - static bool mayAliasDirect(Type source, Type target) - { - return - // if source is the same as target or can be const-converted to target - source.constConv(target) != MATCH.nomatch || - // if target is void and source can be const-converted to target - (target.ty == Tvoid && MODimplicitConv(source.mod, target.mod)); - } - - if (mayAliasDirect(reversePass ? tb : ta, reversePass ? ta : tb)) - { - //printf(" true mayalias %s %s %d\n", ta.toChars(), tb.toChars(), reversePass); - return false; - } - if (ta.nextOf() && ta.nextOf() == tb.nextOf()) - { - //printf(" next==next %s %s %d\n", ta.toChars(), tb.toChars(), reversePass); - return true; - } - - if (tb.ty == Tclass || tb.ty == Tstruct) - { - /* Traverse the type of each field of the aggregate - */ - bool* found = table.getLvalue(tb.deco); - if (*found == true) - return true; // We have already seen this symbol, break the cycle - else - *found = true; - - AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration(); - foreach (v; sym.fields) - { - Type tprmi = v.type.addMod(tb.mod); - //printf("\ttb = %s, tprmi = %s\n", tb.toChars(), tprmi.toChars()); - if (!traverse(ta, tprmi, table, reversePass)) - return false; - } - } - else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer) - { - Type tind = tb.nextOf(); - if (!traverse(ta, tind, table, reversePass)) - return false; - } - else if (tb.hasPointers()) - { - // BUG: consider the context pointer of delegate types - return false; - } - - // Still no match, so try breaking up ta if we have not done so yet. - if (!reversePass) - { - scope newTable = AssocArray!(const(char)*, bool)(); - return traverse(tb, ta, newTable, true); - } - - return true; - } - - // To handle arbitrary levels of indirections in both parameters, we - // recursively descend into aggregate members/levels of indirection in both - // `ta` and `tb` while avoiding cycles. Start with the original types. - scope table = AssocArray!(const(char)*, bool)(); - const result = traverse(ta, tb, table, false); - //printf(" returns %d\n", result); - return result; -} - /* For all functions between outerFunc and f, mark them as needing * a closure. */ @@ -2907,7 +1167,7 @@ private void markAsNeedingClosure(Dsymbol f, FuncDeclaration outerFunc) * Returns: * true if any closures were needed */ -private bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) +bool checkEscapingSiblings(FuncDeclaration f, FuncDeclaration outerFunc, void* p = null) { static struct PrevSibling { @@ -2978,6 +1238,7 @@ extern (C++) final class FuncAliasDeclaration : FuncDeclaration super(funcalias.loc, funcalias.endloc, ident, funcalias.storage_class, funcalias.type); assert(funcalias != this); this.funcalias = funcalias; + this.dsym = DSYM.funcAliasDeclaration; this.hasOverloads = hasOverloads; if (hasOverloads) @@ -2994,11 +1255,6 @@ extern (C++) final class FuncAliasDeclaration : FuncDeclaration userAttribDecl = funcalias.userAttribDecl; } - override inout(FuncAliasDeclaration) isFuncAliasDeclaration() inout - { - return this; - } - override const(char)* kind() const { return "function alias"; @@ -3025,15 +1281,16 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration // backend bool deferToObj; - extern (D) this(const ref Loc loc, const ref Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, StorageClass storage_class = STC.undefined_) + extern (D) this(Loc loc, Loc endloc, Type type, TOK tok, ForeachStatement fes, Identifier id = null, STC storage_class = STC.none) { super(loc, endloc, null, storage_class, type); + this.dsym = DSYM.funcLiteralDeclaration; this.ident = id ? id : Id.empty; this.tok = tok; this.fes = fes; // Always infer scope for function literals // See https://issues.dlang.org/show_bug.cgi?id=20362 - this.inferScope = true; + this.scopeInprocess = true; //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this.ident.toChars(), type.toChars()); } @@ -3073,61 +1330,6 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration return false; } - /******************************* - * Modify all expression type of return statements to tret. - * - * On function literals, return type may be modified based on the context type - * after its semantic3 is done, in FuncExp::implicitCastTo. - * - * A function() dg = (){ return new B(); } // OK if is(B : A) == true - * - * If B to A conversion is convariant that requires offseet adjusting, - * all return statements should be adjusted to return expressions typed A. - */ - extern (D) void modifyReturns(Scope* sc, Type tret) - { - import dmd.statement_rewrite_walker; - - extern (C++) final class RetWalker : StatementRewriteWalker - { - alias visit = typeof(super).visit; - public: - Scope* sc; - Type tret; - FuncLiteralDeclaration fld; - - override void visit(ReturnStatement s) - { - Expression exp = s.exp; - if (exp && !exp.type.equals(tret)) - s.exp = exp.implicitCastTo(sc, tret); - } - } - - if (semanticRun < PASS.semantic3done) - return; - - if (fes) - return; - - scope RetWalker w = new RetWalker(); - w.sc = sc; - w.tret = tret; - w.fld = this; - fbody.accept(w); - - // Also update the inferred function type to match the new return type. - // This is required so the code generator does not try to cast the - // modified returns back to the original type. - if (inferRetType && type.nextOf() != tret) - type.toTypeFunction().next = tret; - } - - override inout(FuncLiteralDeclaration) isFuncLiteralDeclaration() inout - { - return this; - } - override const(char)* kind() const { // GCC requires the (char*) casts @@ -3138,8 +1340,7 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration { if (parent) { - TemplateInstance ti = parent.isTemplateInstance(); - if (ti) + if (TemplateInstance ti = parent.isTemplateInstance()) return ti.tempdecl.toPrettyChars(QualifyTypes); } return Dsymbol.toPrettyChars(QualifyTypes); @@ -3155,11 +1356,12 @@ extern (C++) final class FuncLiteralDeclaration : FuncDeclaration */ extern (C++) final class CtorDeclaration : FuncDeclaration { - bool isCpCtor; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Type type, bool isCpCtor = false) + bool isCpCtor; // copy constructor + bool isMoveCtor; // move constructor (aka rvalue constructor) + extern (D) this(Loc loc, Loc endloc, STC stc, Type type) { super(loc, endloc, Id.ctor, stc, type); - this.isCpCtor = isCpCtor; + this.dsym = DSYM.ctorDeclaration; //printf("CtorDeclaration(loc = %s) %s %p\n", loc.toChars(), toChars(), this); } @@ -3176,11 +1378,6 @@ extern (C++) final class CtorDeclaration : FuncDeclaration return isCpCtor ? "copy constructor" : "constructor"; } - override const(char)* toChars() const - { - return "this"; - } - override bool isVirtual() const { return false; @@ -3196,11 +1393,6 @@ extern (C++) final class CtorDeclaration : FuncDeclaration return (isThis() && vthis && global.params.useInvariants == CHECKENABLE.on); } - override inout(CtorDeclaration) isCtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3211,9 +1403,10 @@ extern (C++) final class CtorDeclaration : FuncDeclaration */ extern (C++) final class PostBlitDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id) + extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id) { super(loc, endloc, id, stc, null); + this.dsym = DSYM.postBlitDeclaration; } override PostBlitDeclaration syntaxCopy(Dsymbol s) @@ -3244,11 +1437,6 @@ extern (C++) final class PostBlitDeclaration : FuncDeclaration return false; // cannot overload postblits } - override inout(PostBlitDeclaration) isPostBlitDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3259,14 +1447,16 @@ extern (C++) final class PostBlitDeclaration : FuncDeclaration */ extern (C++) final class DtorDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc) + extern (D) this(Loc loc, Loc endloc) { - super(loc, endloc, Id.dtor, STC.undefined_, null); + super(loc, endloc, Id.dtor, STC.none, null); + this.dsym = DSYM.dtorDeclaration; } - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id) + extern (D) this(Loc loc, Loc endloc, STC stc, Identifier id) { super(loc, endloc, id, stc, null); + this.dsym = DSYM.dtorDeclaration; } override DtorDeclaration syntaxCopy(Dsymbol s) @@ -3282,11 +1472,6 @@ extern (C++) final class DtorDeclaration : FuncDeclaration return "destructor"; } - override const(char)* toChars() const - { - return "~this"; - } - override bool isVirtual() const { // D dtor's don't get put into the vtbl[] @@ -3309,11 +1494,6 @@ extern (C++) final class DtorDeclaration : FuncDeclaration return false; // cannot overload destructors } - override inout(DtorDeclaration) isDtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3324,14 +1504,16 @@ extern (C++) final class DtorDeclaration : FuncDeclaration */ extern (C++) class StaticCtorDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc("_staticCtor", loc), STC.static_ | stc, null); + this.dsym = DSYM.staticCtorDeclaration; } - extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, string name, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); + this.dsym = DSYM.staticCtorDeclaration; } override StaticCtorDeclaration syntaxCopy(Dsymbol s) @@ -3362,16 +1544,6 @@ extern (C++) class StaticCtorDeclaration : FuncDeclaration return false; } - override final bool hasStaticCtorOrDtor() @nogc nothrow pure @safe - { - return true; - } - - override final inout(StaticCtorDeclaration) isStaticCtorDeclaration() inout @nogc nothrow pure @safe - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3385,9 +1557,10 @@ extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration /// Exclude this constructor from cyclic dependency check bool standalone; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, "_sharedStaticCtor", stc); + this.dsym = DSYM.sharedStaticCtorDeclaration; } override SharedStaticCtorDeclaration syntaxCopy(Dsymbol s) @@ -3398,11 +1571,6 @@ extern (C++) final class SharedStaticCtorDeclaration : StaticCtorDeclaration return scd; } - override inout(SharedStaticCtorDeclaration) isSharedStaticCtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3415,14 +1583,16 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration { VarDeclaration vgate; // 'gate' variable - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc("_staticDtor", loc), STC.static_ | stc, null); + this.dsym = DSYM.staticDtorDeclaration; } - extern (D) this(const ref Loc loc, const ref Loc endloc, string name, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, string name, STC stc) { super(loc, endloc, Identifier.generateIdWithLoc(name, loc), STC.static_ | stc, null); + this.dsym = DSYM.staticDtorDeclaration; } override StaticDtorDeclaration syntaxCopy(Dsymbol s) @@ -3443,11 +1613,6 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration return false; } - override final bool hasStaticCtorOrDtor() - { - return true; - } - override final bool addPreInvariant() { return false; @@ -3458,11 +1623,6 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration return false; } - override final inout(StaticDtorDeclaration) isStaticDtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3473,9 +1633,10 @@ extern (C++) class StaticDtorDeclaration : FuncDeclaration */ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc) + extern (D) this(Loc loc, Loc endloc, STC stc) { super(loc, endloc, "_sharedStaticDtor", stc); + this.dsym = DSYM.sharedStaticDtorDeclaration; } override SharedStaticDtorDeclaration syntaxCopy(Dsymbol s) @@ -3486,11 +1647,6 @@ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration return sdd; } - override inout(SharedStaticDtorDeclaration) isSharedStaticDtorDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3501,11 +1657,12 @@ extern (C++) final class SharedStaticDtorDeclaration : StaticDtorDeclaration */ extern (C++) final class InvariantDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, Identifier id, Statement fbody) + extern (D) this(Loc loc, Loc endloc, STC 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; + this.dsym = DSYM.invariantDeclaration; } override InvariantDeclaration syntaxCopy(Dsymbol s) @@ -3531,11 +1688,6 @@ extern (C++) final class InvariantDeclaration : FuncDeclaration return false; } - override inout(InvariantDeclaration) isInvariantDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3561,10 +1713,11 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration // toObjFile() these nested functions after this one FuncDeclarations deferredNested; - extern (D) this(const ref Loc loc, const ref Loc endloc, StorageClass stc, char* codedoc) + extern (D) this(Loc loc, Loc endloc, STC stc, char* codedoc) { super(loc, endloc, Identifier.generateIdWithLoc("__unittest", loc), stc, null); this.codedoc = codedoc; + this.dsym = DSYM.unitTestDeclaration; } override UnitTestDeclaration syntaxCopy(Dsymbol s) @@ -3595,11 +1748,6 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration return false; } - override inout(UnitTestDeclaration) isUnitTestDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); @@ -3610,9 +1758,10 @@ extern (C++) final class UnitTestDeclaration : FuncDeclaration */ extern (C++) final class NewDeclaration : FuncDeclaration { - extern (D) this(const ref Loc loc, StorageClass stc) + extern (D) this(Loc loc, STC stc) { super(loc, Loc.initial, Id.classNew, STC.static_ | stc, null); + this.dsym = DSYM.newDeclaration; } override NewDeclaration syntaxCopy(Dsymbol s) @@ -3643,230 +1792,42 @@ extern (C++) final class NewDeclaration : FuncDeclaration return false; } - override inout(NewDeclaration) isNewDeclaration() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); } } -/************************************** - * When a traits(compiles) is used on a function literal call - * we need to take into account if the body of the function - * violates any attributes, however, we must not affect the - * attribute inference on the outer function. The attributes - * of the function literal still need to be inferred, therefore - * we need a way to check for the scope that the traits compiles - * introduces. - * - * Params: - * sc = scope to be checked for - * - * Returns: `true` if the provided scope is the root - * of the traits compiles list of scopes. - */ -bool isRootTraitsCompilesScope(Scope* sc) -{ - return (sc.flags & SCOPE.compile) && !(sc.func.flags & SCOPE.compile); -} - -/************************************** - * A statement / expression in this scope is not `@safe`, - * so mark the enclosing function as `@system` - * - * Params: - * sc = scope that the unsafe statement / expression is in - * gag = surpress error message (used in escape.d) - * loc = location of error - * fmt = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether there's a safe error - */ -bool setUnsafe(Scope* sc, - bool gag = false, Loc loc = Loc.init, const(char)* fmt = null, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - if (sc.intypeof) - return false; // typeof(cast(int*)0) is safe - - if (sc.flags & SCOPE.debug_) // debug {} scopes are permissive - return false; - - if (!sc.func) - { - if (sc.varDecl) - { - if (sc.varDecl.storage_class & STC.safe) - { - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - else if (!(sc.varDecl.storage_class & STC.trusted)) - { - sc.varDecl.storage_class |= STC.system; - sc.varDecl.systemInferred = true; - } - } - return false; - } - - - if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) - { - if (sc.func.isSafeBypassingInference()) - { - // Message wil be gagged, but still call error() to update global.errors and for - // -verrors=spec - .error(loc, fmt, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - return true; - } - return false; - } - - return sc.func.setUnsafe(gag, loc, fmt, arg0, arg1, arg2); -} - -/*************************************** - * Like `setUnsafe`, but for safety errors still behind preview switches - * - * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, - * the behavior changes based on the setting: - * - * - In case of `-revert=fs`, it does nothing. - * - In case of `-preview=fs`, it's the same as `setUnsafe` - * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. - * - * Params: - * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope - * fs = feature state from the preview flag - * gag = surpress error message - * loc = location of error - * msg = printf-style format string - * arg0 = (optional) argument for first %s format specifier - * arg1 = (optional) argument for second %s format specifier - * arg2 = (optional) argument for third %s format specifier - * Returns: whether an actual safe error (not deprecation) occured - */ -bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* msg, - RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null) -{ - //printf("setUnsafePreview() fs:%d %s\n", fs, msg); - with (FeatureState) final switch (fs) - { - case disabled: - return false; - - case enabled: - return sc.setUnsafe(gag, loc, msg, arg0, arg1, arg2); - - case default_: - if (!sc.func) - return false; - if (sc.func.isSafeBypassingInference()) - { - if (!gag && !sc.isDeprecated()) - { - deprecation(loc, msg, arg0 ? arg0.toChars() : "", arg1 ? arg1.toChars() : "", arg2 ? arg2.toChars() : ""); - } - } - else if (!sc.func.safetyViolation) - { - import dmd.func : AttributeViolation; - sc.func.safetyViolation = new AttributeViolation(loc, msg, arg0, arg1, arg2); - } - return false; - } -} - /// Stores a reason why a function failed to infer a function attribute like `@safe` or `pure` /// /// Has two modes: -/// - a regular safety error, stored in (fmtStr, arg0, arg1) +/// - a regular safety error, stored in `action` /// - a call to a function without the attribute, which is a special case, because in that case, /// that function might recursively also have a `AttributeViolation`. This way, in case /// of a big call stack, the error can go down all the way to the root cause. -/// The `FunctionDeclaration` is then stored in `arg0` and `fmtStr` must be `null`. struct AttributeViolation { - /// location of error - Loc loc = Loc.init; - /// printf-style format string - const(char)* fmtStr = null; - /// Arguments for up to two `%s` format specifiers in format string - RootObject arg0 = null; - /// ditto - RootObject arg1 = null; - /// ditto - RootObject arg2 = null; -} + Loc loc; /// location of error -/// Print the reason why `fd` was inferred `@system` as a supplemental error -/// Params: -/// fd = function to check -/// maxDepth = up to how many functions deep to report errors -/// deprecation = print deprecations instead of errors -/// stc = storage class of attribute to check -void errorSupplementalInferredAttr(FuncDeclaration fd, int maxDepth, bool deprecation, STC stc) -{ - auto errorFunc = deprecation ? &deprecationSupplemental : &errorSupplemental; + FuncDeclaration fd; /// function is the focus of the violation - AttributeViolation* s; - const(char)* attr; - if (stc & STC.safe) - { - s = fd.safetyViolation; - attr = "@safe"; - } - else if (stc & STC.pure_) - { - s = fd.pureViolation; - attr = "pure"; - } - else if (stc & STC.nothrow_) - { - s = fd.nothrowViolation; - attr = "nothrow"; - } - else if (stc & STC.nogc) - { - s = fd.nogcViolation; - attr = "@nogc"; - } + // -- OR -- - if (s) + string action; /// Action that made the attribute fail to get inferred + + this(Loc loc, FuncDeclaration fd) { this.loc = loc; this.fd = fd; } + + this(Loc loc, const(char)* fmt, RootObject[] args) { - if (s.fmtStr) - { - errorFunc(s.loc, deprecation ? - "which wouldn't be `%s` because of:" : - "which wasn't inferred `%s` because of:", attr); - if (stc == STC.nogc || stc == STC.pure_) - { - auto f = (cast(Dsymbol) s.arg0).isFuncDeclaration(); - errorFunc(s.loc, s.fmtStr, f.kind(), f.toPrettyChars(), s.arg1 ? s.arg1.toChars() : ""); - } - else - { - errorFunc(s.loc, s.fmtStr, - s.arg0 ? s.arg0.toChars() : "", s.arg1 ? s.arg1.toChars() : "", s.arg2 ? s.arg2.toChars() : ""); - } - } - else if (auto sa = s.arg0.isDsymbol()) - { - if (FuncDeclaration fd2 = sa.isFuncDeclaration()) - { - if (maxDepth > 0) - { - errorFunc(s.loc, "which calls `%s`", fd2.toPrettyChars()); - errorSupplementalInferredAttr(fd2, maxDepth - 1, deprecation, stc); - } - } - } + this.loc = loc; + assert(args.length <= 4); // expand if necessary + OutBuffer buf; + buf.printf(fmt, + args.length > 0 && args[0] ? args[0].toErrMsg() : "", + args.length > 1 && args[1] ? args[1].toErrMsg() : "", + args.length > 2 && args[2] ? args[2].toErrMsg() : "", + args.length > 3 && args[3] ? args[3].toErrMsg() : "", + ); + this.action = buf.extractSlice(); } } diff --git a/gcc/d/dmd/funcsem.d b/gcc/d/dmd/funcsem.d index 2cadc40..1231496 100644 --- a/gcc/d/dmd/funcsem.d +++ b/gcc/d/dmd/funcsem.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html, Functions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/funcsem.d, _funcsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/funcsem.d, _funcsem.d) * Documentation: https://dlang.org/phobos/dmd_funcsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/funcsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/funcsem.d */ module dmd.funcsem; @@ -54,9 +54,9 @@ import dmd.rootobject; import dmd.root.filename; import dmd.root.string; import dmd.root.stringtable; +import dmd.safe; import dmd.semantic2; import dmd.semantic3; -import dmd.statement_rewrite_walker; import dmd.statement; import dmd.statementsem; import dmd.target; @@ -64,6 +64,11 @@ import dmd.templatesem; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.statement_rewrite_walker; + +version (IN_GCC) {} +else version (IN_LLVM) {} +else version = MARS; /* Tweak all return statements and dtor call for nrvo_var, for correct NRVO. */ @@ -149,6 +154,28 @@ public: } } +/**************************************** + * Only one entry point function is allowed. Print error if more than one. + * Params: + * fd = a "main" function + * Returns: + * true if haven't seen "main" before + */ +extern (C++) bool onlyOneMain(FuncDeclaration fd) +{ + if (auto lastMain = FuncDeclaration.lastMain) + { + const format = (target.os == Target.OS.Windows) + ? "only one entry point `main`, `WinMain` or `DllMain` is allowed" + : "only one entry point `main` is allowed"; + error(fd.loc, format.ptr); + errorSupplemental(lastMain.loc, "previously found `%s` here", lastMain.toFullSignature()); + return false; + } + FuncDeclaration.lastMain = fd; + return true; +} + /********************************** * Main semantic routine for functions. */ @@ -210,11 +237,11 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) //printf("function storage_class = x%llx, sc.stc = x%llx, %x\n", storage_class, sc.stc, Declaration.isFinal()); - if (sc.flags & SCOPE.compile) + if (sc.traitsCompiles) funcdecl.skipCodegen = true; funcdecl._linkage = sc.linkage; - if (sc.flags & SCOPE.Cfile && funcdecl.isFuncLiteralDeclaration()) + if (sc.inCfile && funcdecl.isFuncLiteralDeclaration()) funcdecl._linkage = LINK.d; // so they are uniquely mangled if (auto fld = funcdecl.isFuncLiteralDeclaration()) @@ -239,7 +266,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) funcdecl.visibility = sc.visibility; funcdecl.userAttribDecl = sc.userAttribDecl; - UserAttributeDeclaration.checkGNUABITag(funcdecl, funcdecl._linkage); + checkGNUABITag(funcdecl, funcdecl._linkage); checkMustUseReserved(funcdecl); if (!funcdecl.originalType) @@ -259,7 +286,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) return null; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* C11 allows a function to be declared with a typedef, D does not. */ @@ -328,15 +355,15 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) } } - if (tf.isref) + if (tf.isRef) sc.stc |= STC.ref_; if (tf.isScopeQual) sc.stc |= STC.scope_; - if (tf.isnothrow) + if (tf.isNothrow) sc.stc |= STC.nothrow_; - if (tf.isnogc) + if (tf.isNogc) sc.stc |= STC.nogc; - if (tf.isproperty) + if (tf.isProperty) sc.stc |= STC.property; if (tf.purity == PURE.fwdref) sc.stc |= STC.pure_; @@ -354,7 +381,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) if (funcdecl.isCtorDeclaration()) { - tf.isctor = true; + tf.isCtor = true; Type tret = ad.handleType(); assert(tret); tret = tret.addStorageClass(funcdecl.storage_class | sc.stc); @@ -365,7 +392,7 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) } // 'return' on a non-static class member function implies 'scope' as well - if (ad && ad.isClassDeclaration() && (tf.isreturn || sc.stc & STC.return_) && !(sc.stc & STC.static_)) + if (ad && ad.isClassDeclaration() && (tf.isReturn || sc.stc & STC.return_) && !(sc.stc & STC.static_)) sc.stc |= STC.scope_; // If 'this' has no pointers, remove 'scope' as it has no meaning @@ -376,11 +403,11 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) { sc.stc &= ~STC.scope_; tf.isScopeQual = false; - if (tf.isreturnscope) + if (tf.isReturnScope) { sc.stc &= ~(STC.return_ | STC.returnScope); - tf.isreturn = false; - tf.isreturnscope = false; + tf.isReturn = false; + tf.isReturnScope = false; } } @@ -426,12 +453,12 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) TypeFunction tfo = funcdecl.originalType.toTypeFunction(); tfo.mod = f.mod; tfo.isScopeQual = f.isScopeQual; - tfo.isreturninferred = f.isreturninferred; - tfo.isscopeinferred = f.isscopeinferred; - tfo.isref = f.isref; - tfo.isnothrow = f.isnothrow; - tfo.isnogc = f.isnogc; - tfo.isproperty = f.isproperty; + tfo.isReturnInferred = f.isReturnInferred; + tfo.isScopeInferred = f.isScopeInferred; + tfo.isRef = f.isRef; + tfo.isNothrow = f.isNothrow; + tfo.isNogc = f.isNogc; + tfo.isProperty = f.isProperty; tfo.purity = f.purity; tfo.trust = f.trust; @@ -468,10 +495,10 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) funcdecl.overnext = null; // don't overload the redeclarations } - if ((funcdecl.storage_class & STC.auto_) && !f.isref && !funcdecl.inferRetType) + if ((funcdecl.storage_class & STC.auto_) && !f.isRef && !funcdecl.inferRetType) .error(funcdecl.loc, "%s `%s` storage class `auto` has no effect if return type is not inferred", funcdecl.kind, funcdecl.toPrettyChars); - if (f.isreturn && !funcdecl.needThis() && !funcdecl.isNested()) + if (f.isReturn && !funcdecl.needThis() && !funcdecl.isNested()) { /* Non-static nested functions have a hidden 'this' pointer to which * the 'return' applies @@ -742,7 +769,10 @@ void funcDeclarationSemantic(Scope* sc, FuncDeclaration funcdecl) { if (fdv.isFuture()) { - deprecation(funcdecl.loc, "`@__future` base class method `%s` is being overridden by `%s`; rename the latter", fdv.toPrettyChars(), funcdecl.toPrettyChars()); + deprecation(funcdecl.loc, "method `%s` implicitly overrides `@__future` base class method; rename the former", + funcdecl.toPrettyChars()); + deprecationSupplemental(fdv.loc, "base method `%s` defined here", + fdv.toPrettyChars()); // Treat 'this' as an introducing function, giving it a separate hierarchy in the vtbl[] goto Lintro; } @@ -1040,8 +1070,7 @@ Ldone: } // If it's a member template - ClassDeclaration cd = ti.tempdecl.isClassMember(); - if (cd) + if (ClassDeclaration cd = ti.tempdecl.isClassMember()) { .error(funcdecl.loc, "%s `%s` cannot use template to add virtual function to class `%s`", funcdecl.kind, funcdecl.toPrettyChars, cd.toChars()); } @@ -1074,7 +1103,7 @@ Ldone: { printedMain = true; auto name = mod.srcfile.toChars(); - auto path = FileName.searchPath(global.path, name, true); + auto path = FileName.searchPath(global.importPaths, name, true); message("entry %-10s\t%s", type, path ? path : name); } } @@ -1117,6 +1146,28 @@ Ldone: } } +/***************************************** + * Initialize for inferring the attributes of this function. + */ +private void initInferAttributes(FuncDeclaration fd) +{ + //printf("initInferAttributes() for %s (%s)\n", toPrettyChars(), ident.toChars()); + TypeFunction tf = fd.type.toTypeFunction(); + if (tf.purity == PURE.impure) // purity not specified + fd.purityInprocess = true; + + if (tf.trust == TRUST.default_) + fd.safetyInprocess = true; + + if (!tf.isNothrow) + fd.nothrowInprocess = true; + + if (!tf.isNogc) + fd.nogcInprocess = true; + + // Initialize for inferring STC.scope_ + fd.scopeInprocess = true; +} /**************************************************** * Resolve forward reference of function signature - @@ -1138,8 +1189,8 @@ bool functionSemantic(FuncDeclaration fd) if (!fd.originalType) // semantic not yet run { TemplateInstance spec = fd.isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; + const olderrs = global.errors; + const oldgag = global.gag; if (global.gag && !spec) global.gag = 0; dsymbolSemantic(fd, fd._scope); @@ -1194,8 +1245,8 @@ bool functionSemantic3(FuncDeclaration fd) * we need to temporarily ungag errors. */ TemplateInstance spec = fd.isSpeculative(); - uint olderrs = global.errors; - uint oldgag = global.gag; + const olderrs = global.errors; + const oldgag = global.gag; if (global.gag && !spec) global.gag = 0; semantic3(fd, fd._scope); @@ -1224,6 +1275,7 @@ extern (D) void declareThis(FuncDeclaration fd, Scope* sc) const bool dualCtx = (fd.toParent2() != fd.toParentLocal()); if (dualCtx) fd.hasDualContext = true; + auto ad = fd.isThis(); if (!dualCtx && !ad && !fd.isNested()) { @@ -1262,11 +1314,11 @@ extern (D) void declareThis(FuncDeclaration fd, Scope* sc) if (auto tf = fd.type.isTypeFunction()) { - if (tf.isreturn) + if (tf.isReturn) fd.vthis.storage_class |= STC.return_; if (tf.isScopeQual) fd.vthis.storage_class |= STC.scope_; - if (tf.isreturnscope) + if (tf.isReturnScope) fd.vthis.storage_class |= STC.returnScope; } @@ -1282,7 +1334,7 @@ extern (D) void declareThis(FuncDeclaration fd, Scope* sc) * Check that this function type is properly resolved. * If not, report "forward reference error" and return true. */ -extern (D) bool checkForwardRef(FuncDeclaration fd, const ref Loc loc) +extern (D) bool checkForwardRef(FuncDeclaration fd, Loc loc) { if (!functionSemantic(fd)) return true; @@ -1318,66 +1370,66 @@ int findVtblIndex(FuncDeclaration fd, Dsymbol[] vtbl) import dmd.typesem : covariant; FuncDeclaration mismatch = null; - StorageClass mismatchstc = 0; + STC mismatchstc = STC.none; int mismatchvi = -1; int exactvi = -1; int bestvi = -1; for (int vi = 0; vi < cast(int)vtbl.length; vi++) { FuncDeclaration fdv = vtbl[vi].isFuncDeclaration(); - if (fdv && fdv.ident == fd.ident) + if (!fdv || fdv.ident != fd.ident) + continue; + + if (fd.type.equals(fdv.type)) // if exact match { - if (fd.type.equals(fdv.type)) // if exact match + if (fdv.parent.isClassDeclaration()) { - if (fdv.parent.isClassDeclaration()) - { - if (fdv.isFuture()) - { - bestvi = vi; - continue; // keep looking - } - return vi; // no need to look further - } - - if (exactvi >= 0) + if (fdv.isFuture()) { - .error(fd.loc, "%s `%s` cannot determine overridden function", fd.kind, fd.toPrettyChars); - return exactvi; + bestvi = vi; + continue; // keep looking } - exactvi = vi; - bestvi = vi; - continue; + return vi; // no need to look further } - StorageClass stc = 0; - const cov = fd.type.covariant(fdv.type, &stc); - //printf("\tbaseclass cov = %d\n", cov); - final switch (cov) + if (exactvi >= 0) { - case Covariant.distinct: - // types are distinct - break; + .error(fd.loc, "%s `%s` cannot determine overridden function", fd.kind, fd.toPrettyChars); + return exactvi; + } + exactvi = vi; + bestvi = vi; + continue; + } - case Covariant.yes: - bestvi = vi; // covariant, but not identical - break; - // keep looking for an exact match + STC stc = STC.none; + const cov = fd.type.covariant(fdv.type, &stc); + //printf("\tbaseclass cov = %d\n", cov); + final switch (cov) + { + case Covariant.distinct: + // types are distinct + break; - case Covariant.no: - mismatchvi = vi; - mismatchstc = stc; - mismatch = fdv; // overrides, but is not covariant - break; - // keep looking for an exact match + case Covariant.yes: + bestvi = vi; // covariant, but not identical + break; + // keep looking for an exact match - case Covariant.fwdref: - return -2; // forward references - } + case Covariant.no: + mismatchvi = vi; + mismatchstc = stc; + mismatch = fdv; // overrides, but is not covariant + break; + // keep looking for an exact match + + case Covariant.fwdref: + return -2; // forward references } } if (fd._linkage == LINK.cpp && bestvi != -1) { - StorageClass stc = 0; + STC stc = STC.none; FuncDeclaration fdv = vtbl[bestvi].isFuncDeclaration(); assert(fdv && fdv.ident == fd.ident); if (fd.type.covariant(fdv.type, &stc, /*cppCovariant=*/true) == Covariant.no) @@ -1475,16 +1527,17 @@ enum FuncResolveFlag : ubyte * Returns: * if match is found, then function symbol, else null */ -FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, +FuncDeclaration resolveFuncCall(Loc loc, Scope* sc, Dsymbol s, Objects* tiargs, Type tthis, ArgumentList argumentList, FuncResolveFlag flags) { + //printf("resolveFuncCall() %s\n", s.toChars()); auto fargs = argumentList.arguments; if (!s) return null; // no match version (none) { - printf("resolveFuncCall('%s')\n", s.toChars()); + printf("resolveFuncCall() %s)\n", s.toChars()); if (tthis) printf("\tthis: %s\n", tthis.toChars()); if (fargs) @@ -1496,7 +1549,6 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, printf("\t%s: %s\n", arg.toChars(), arg.type.toChars()); } } - printf("\tfnames: %s\n", fnames ? fnames.toChars() : "null"); } if (tiargs && arrayObjectIsError(*tiargs)) @@ -1564,9 +1616,26 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, const(char)* lastprms = parametersTypeToChars(tf1.parameterList); const(char)* nextprms = parametersTypeToChars(tf2.parameterList); - .error(loc, "`%s.%s` called with argument types `%s` matches both:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", + string match = ""; + final switch (m.last) + { + case MATCH.convert: + match = "after implicit conversions"; + break; + case MATCH.constant: + match = "after qualifier conversion"; + break; + case MATCH.exact: + match = "exactly"; + break; + case MATCH.nomatch: + assert(0); + } + + .error(loc, "`%s.%s` called with argument types `%s` matches multiple overloads %.*s:\n%s: `%s%s%s`\nand:\n%s: `%s%s%s`", s.parent.toPrettyChars(), s.ident.toChars(), fargsBuf.peekChars(), + match.fTuple.expand, m.lastf.loc.toChars(), m.lastf.toPrettyChars(), lastprms, tf1.modToChars(), m.nextf.loc.toChars(), m.nextf.toPrettyChars(), nextprms, tf2.modToChars()); return null; @@ -1615,9 +1684,12 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, { .error(loc, "none of the overloads of `%s` are callable using argument types `!(%s)%s`", od.ident.toChars(), tiargsBuf.peekChars(), fargsBuf.peekChars()); + if (!global.gag || global.params.v.showGaggedErrors) + printCandidates(loc, od, sc.isDeprecated()); return null; } + import dmd.expressionsem : checkDisabled; // remove when deprecation period of class allocators and deallocators is over if (fd.isNewDeclaration() && fd.checkDisabled(loc, sc)) return null; @@ -1694,39 +1766,40 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, fd.kind(), fd.toPrettyChars(), parametersTypeToChars(tf.parameterList), tf.modToChars(), fargsBuf.peekChars()); + if (global.gag && !global.params.v.showGaggedErrors) + return null; + // re-resolve to check for supplemental message - if (!global.gag || global.params.v.showGaggedErrors) + if (tthis) { - if (tthis) + if (auto classType = tthis.isTypeClass()) { - if (auto classType = tthis.isTypeClass()) + if (auto baseClass = classType.sym.baseClass) { - if (auto baseClass = classType.sym.baseClass) + if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) { - if (auto baseFunction = baseClass.search(baseClass.loc, fd.ident)) + MatchAccumulator mErr; + functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, argumentList); + if (mErr.last > MATCH.nomatch && mErr.lastf) { - MatchAccumulator mErr; - functionResolve(mErr, baseFunction, loc, sc, tiargs, baseClass.type, argumentList); - if (mErr.last > MATCH.nomatch && mErr.lastf) - { - errorSupplemental(loc, "%s `%s` hides base class function `%s`", - fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); - errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", - fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); - return null; - } + errorSupplemental(loc, "%s `%s` hides base class function `%s`", + fd.kind, fd.toPrettyChars(), mErr.lastf.toPrettyChars()); + errorSupplemental(loc, "add `alias %s = %s` to `%s`'s body to merge the overload sets", + fd.toChars(), mErr.lastf.toPrettyChars(), tthis.toChars()); + return null; } } } } + } - void errorHelper2(const(char)* failMessage) scope - { - errorSupplemental(loc, failMessage); - } - - functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper2); + void errorHelper2(const(char)* failMessage) scope + { + errorSupplemental(loc, failMessage); } + + functionResolve(m, orig_s, loc, sc, tiargs, tthis, argumentList, &errorHelper2); + return null; } @@ -1737,16 +1810,29 @@ FuncDeclaration resolveFuncCall(const ref Loc loc, Scope* sc, Dsymbol s, * declaration = the declaration to print overload candidates for * showDeprecated = If `false`, `deprecated` function won't be shown */ -private void printCandidates(Decl)(const ref Loc loc, Decl declaration, bool showDeprecated) -if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) +private void printCandidates(Decl)(Loc loc, Decl declaration, bool showDeprecated) { // max num of overloads to print (-v or -verror-supplements overrides this). const uint DisplayLimit = global.params.v.errorSupplementCount(); const(char)* constraintsTip; - // determine if the first candidate was printed - int printed; - bool matchSymbol(Dsymbol s, bool print, bool single_candidate = false) + int printed = 0; // number of candidates printed + int count = 0; // total candidates + bool child; // true if inside an eponymous template + const(char)* errorPrefix() @safe + { + if (child) + return " - Containing: "; + + // align with blank spaces after first message + enum plural = "Candidates are: "; + enum spaces = " "; + if (printed) + return spaces; + + return (count == 1) ? "Candidate is: " : plural; + } + bool matchSymbol(Dsymbol s, bool print) { if (auto fd = s.isFuncDeclaration()) { @@ -1762,16 +1848,14 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) return true; auto tf = cast(TypeFunction) fd.type; OutBuffer buf; - buf.writestring(fd.toPrettyChars()); + buf.writestring(child ? fd.toChars() : fd.toPrettyChars()); buf.writestring(parametersTypeToChars(tf.parameterList)); if (tf.mod) { buf.writeByte(' '); buf.MODtoBuffer(tf.mod); } - .errorSupplemental(fd.loc, - printed ? " `%s`" : - single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", buf.peekChars()); + .errorSupplemental(fd.loc, "%s`%s`", errorPrefix(), buf.peekChars()); } else if (auto td = s.isTemplateDeclaration()) { @@ -1779,35 +1863,46 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) if (!print) return true; + + // if td.onemember is a function, toCharsMaybeConstraints can print it + // without us recursing, otherwise we have to handle it. + // td.onemember may not have overloads set + // (see fail_compilation/onemember_overloads.d) + // assume if more than one member it is overloaded internally + bool recurse = td.onemember && (!td.onemember.isFuncDeclaration || + td.members.length > 1); OutBuffer buf; HdrGenState hgs; - hgs.skipConstraints = true; + hgs.skipConstraints = true; // failing constraint should get printed below + hgs.showOneMember = !recurse; toCharsMaybeConstraints(td, buf, hgs); const tmsg = buf.peekChars(); - const cmsg = td.getConstraintEvalError(constraintsTip); - - // add blank space if there are multiple candidates - // the length of the blank space is `strlen("Candidates are: ")` + const cmsg = child ? null : td.getConstraintEvalError(constraintsTip); if (cmsg) - { - .errorSupplemental(td.loc, - printed ? " `%s`\n%s" : - single_candidate ? "Candidate is: `%s`\n%s" : "Candidates are: `%s`\n%s", - tmsg, cmsg); - } + .errorSupplemental(td.loc, "%s`%s`\n%s", errorPrefix(), tmsg, cmsg); else + .errorSupplemental(td.loc, "%s`%s`", errorPrefix(), tmsg); + + if (recurse) { - .errorSupplemental(td.loc, - printed ? " `%s`" : - single_candidate ? "Candidate is: `%s`" : "Candidates are: `%s`", - tmsg); + child = true; + foreach (d; *td.members) + { + if (d.ident != td.ident) + continue; + + if (auto fd2 = d.isFuncDeclaration()) + matchSymbol(fd2, print); + else if (auto td2 = d.isTemplateDeclaration()) + matchSymbol(td2, print); + } + child = false; } } return true; } // determine if there's > 1 candidate - int count = 0; overloadApply(declaration, (s) { if (matchSymbol(s, false)) count++; @@ -1817,7 +1912,7 @@ if (is(Decl == TemplateDeclaration) || is(Decl == FuncDeclaration)) overloadApply(declaration, (s) { if (global.params.v.verbose || printed < DisplayLimit) { - if (matchSymbol(s, true, count == 1)) + if (matchSymbol(s, true)) printed++; } else @@ -1862,33 +1957,450 @@ Expression addInvariant(AggregateDeclaration ad, VarDeclaration vthis) break; inv = cd.inv; } - if (inv) + if (!inv) + return e; + + version (all) + { + // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394 + // For the correct mangling, + // run attribute inference on inv if needed. + functionSemantic(inv); + } + + //e = new DsymbolExp(Loc.initial, inv); + //e = new CallExp(Loc.initial, e); + //e = e.semantic(sc2); + + /* https://issues.dlang.org/show_bug.cgi?id=13113 + * Currently virtual invariant calls completely + * bypass attribute enforcement. + * Change the behavior of pre-invariant call by following it. + */ + e = new ThisExp(Loc.initial); + e.type = ad.type.addMod(vthis.type.mod); + e = new DotVarExp(Loc.initial, e, inv, false); + e.type = inv.type; + e = new CallExp(Loc.initial, e); + e.type = Type.tvoid; + return e; +} + +/******************************************** + * Find function in overload list that exactly matches t. + */ +FuncDeclaration overloadExactMatch(FuncDeclaration thisfd, Type t) +{ + FuncDeclaration fd; + overloadApply(thisfd, (Dsymbol s) + { + auto f = s.isFuncDeclaration(); + if (!f) + return 0; + if (f.storage_class & STC.disable) + return 0; + if (t.equals(f.type)) + { + fd = f; + return 1; + } + /* Allow covariant matches, as long as the return type + * is just a const conversion. + * This allows things like pure functions to match with an impure function type. + */ + if (t.ty == Tfunction) + { + auto tf = cast(TypeFunction)f.type; + if (tf.covariant(t) == Covariant.yes && + tf.nextOf().implicitConvTo(t.nextOf()) >= MATCH.constant) + { + fd = f; + return 1; + } + } + return 0; + }); + return fd; +} + +/**************************************************** + * Determine if fd1 overrides fd2. + * Return !=0 if it does. + */ +int overrides(FuncDeclaration fd1, FuncDeclaration fd2) +{ + if (fd1.ident != fd2.ident) + return 0; + + const cov = fd1.type.covariant(fd2.type); + if (cov == Covariant.distinct) + return 0; + + ClassDeclaration cd1 = fd1.toParent().isClassDeclaration(); + ClassDeclaration cd2 = fd2.toParent().isClassDeclaration(); + + if (cd1 && cd2 && cd2.isBaseOf(cd1, null)) + return 1; + return 0; +} + +/************************************* + * Determine partial specialization order of functions `f` vs `g`. + * This is very similar to TemplateDeclaration::leastAsSpecialized(). + * Params: + * f = first function + * g = second function + * names = names of parameters + * Returns: + * match 'this' is at least as specialized as g + * 0 g is more specialized than 'this' + */ +MATCH leastAsSpecialized(FuncDeclaration f, FuncDeclaration g, Identifiers* names) +{ + enum LOG_LEASTAS = 0; + static if (LOG_LEASTAS) + { + import core.stdc.stdio : printf; + printf("leastAsSpecialized(%s, %s, %s)\n", f.toChars(), g.toChars(), names ? names.toChars() : "null"); + printf("%s, %s\n", f.type.toChars(), g.type.toChars()); + } + + /* This works by calling g() with f()'s parameters, and + * if that is possible, then f() is at least as specialized + * as g() is. + */ + + TypeFunction tf = f.type.toTypeFunction(); + TypeFunction tg = g.type.toTypeFunction(); + + /* If both functions have a 'this' pointer, and the mods are not + * the same and g's is not const, then this is less specialized. + */ + if (f.needThis() && g.needThis() && tf.mod != tg.mod) { - version (all) + if (f.isCtorDeclaration()) + { + if (!MODimplicitConv(tg.mod, tf.mod)) + return MATCH.nomatch; + } + else { - // Workaround for https://issues.dlang.org/show_bug.cgi?id=13394 - // For the correct mangling, - // run attribute inference on inv if needed. - functionSemantic(inv); + if (!MODimplicitConv(tf.mod, tg.mod)) + return MATCH.nomatch; } + } - //e = new DsymbolExp(Loc.initial, inv); - //e = new CallExp(Loc.initial, e); - //e = e.semantic(sc2); + /* Create a dummy array of arguments out of the parameters to f() + */ + Expressions args; + foreach (u, p; tf.parameterList) + { + Expression e; + if (p.isReference()) + { + e = new IdentifierExp(Loc.initial, p.ident); + e.type = p.type; + } + else + e = p.type.defaultInitLiteral(Loc.initial); + args.push(e); + } - /* https://issues.dlang.org/show_bug.cgi?id=13113 - * Currently virtual invariant calls completely - * bypass attribute enforcement. - * Change the behavior of pre-invariant call by following it. + MATCH m = callMatch(g, tg, null, ArgumentList(&args, names), 1); + if (m > MATCH.nomatch) + { + /* A variadic parameter list is less specialized than a + * non-variadic one. */ - e = new ThisExp(Loc.initial); - e.type = ad.type.addMod(vthis.type.mod); - e = new DotVarExp(Loc.initial, e, inv, false); - e.type = inv.type; - e = new CallExp(Loc.initial, e); - e.type = Type.tvoid; + if (tf.parameterList.varargs && !tg.parameterList.varargs) + goto L1; // less specialized + + static if (LOG_LEASTAS) + { + printf(" matches %d, so is least as specialized\n", m); + } + return m; } - return e; +L1: + static if (LOG_LEASTAS) + { + printf(" doesn't match, so is not as specialized\n"); + } + return MATCH.nomatch; +} + +/******************************************** + * Find function in overload list that matches to the 'this' modifier. + * There's four result types. + * + * 1. If the 'tthis' matches only one candidate, it's an "exact match". + * Returns the function and 'hasOverloads' is set to false. + * eg. If 'tthis" is mutable and there's only one mutable method. + * 2. If there's two or more match candidates, but a candidate function will be + * a "better match". + * Returns the better match function but 'hasOverloads' is set to true. + * eg. If 'tthis' is mutable, and there's both mutable and const methods, + * the mutable method will be a better match. + * 3. If there's two or more match candidates, but there's no better match, + * Returns null and 'hasOverloads' is set to true to represent "ambiguous match". + * eg. If 'tthis' is mutable, and there's two or more mutable methods. + * 4. If there's no candidates, it's "no match" and returns null with error report. + * e.g. If 'tthis' is const but there's no const methods. + */ +FuncDeclaration overloadModMatch(FuncDeclaration thisfd, Loc loc, Type tthis, ref bool hasOverloads) +{ + //printf("FuncDeclaration::overloadModMatch('%s')\n", toChars()); + MatchAccumulator m; + overloadApply(thisfd, (Dsymbol s) + { + auto f = s.isFuncDeclaration(); + if (!f || f == m.lastf) // skip duplicates + return 0; + auto tf = f.type.toTypeFunction(); + //printf("tf = %s\n", tf.toChars()); + MATCH match; + int lastIsBetter() + { + //printf("\tlastbetter\n"); + m.count++; // count up + return 0; + } + int currIsBetter() + { + //printf("\tisbetter\n"); + if (m.last <= MATCH.convert) + { + // clear last secondary matching + m.nextf = null; + m.count = 0; + } + m.last = match; + m.lastf = f; + m.count++; // count up + return 0; + } + if (tthis) // non-static functions are preferred than static ones + { + if (f.needThis()) + match = f.isCtorDeclaration() ? MATCH.exact : MODmethodConv(tthis.mod, tf.mod); + else + match = MATCH.constant; // keep static function in overload candidates + } + else // static functions are preferred than non-static ones + { + if (f.needThis()) + match = MATCH.convert; + else + match = MATCH.exact; + } + if (match == MATCH.nomatch) + return 0; + if (match > m.last) return currIsBetter(); + if (match < m.last) return lastIsBetter(); + // See if one of the matches overrides the other. + if (m.lastf.overrides(f)) return lastIsBetter(); + if (f.overrides(m.lastf)) return currIsBetter(); + //printf("\tambiguous\n"); + m.nextf = f; + m.count++; + return 0; + }); + if (m.count == 1) // exact match + { + hasOverloads = false; + } + else if (m.count > 1) // better or ambiguous match + { + hasOverloads = true; + } + else // no match + { + hasOverloads = true; + auto tf = thisfd.type.toTypeFunction(); + assert(tthis); + assert(!MODimplicitConv(tthis.mod, tf.mod)); // modifier mismatch + { + OutBuffer thisBuf, funcBuf; + MODMatchToBuffer(&thisBuf, tthis.mod, tf.mod); + MODMatchToBuffer(&funcBuf, tf.mod, tthis.mod); + .error(loc, "%smethod %s is not callable using a %sobject", thisfd.kind, thisfd.toPrettyChars, + funcBuf.peekChars(), thisfd.toPrettyChars(), thisBuf.peekChars()); + } + } + return m.lastf; +} + +/*********************************** + * Determine lexical level difference from `fd` to nested function `target`. + * Issue error if `fd` cannot call `target`. + * + * Params: + * fd = function + * loc = location for error messages + * sc = context + * target = target of call + * decl = The `Declaration` that triggered this check. + * Used to provide a better error message only. + * Returns: + * 0 same level + * >0 decrease nesting by number + * -1 increase nesting by 1 (`target` is nested within 'fd') + * LevelError error + */ +int getLevelAndCheck(FuncDeclaration fd, Loc loc, Scope* sc, FuncDeclaration target, + Declaration decl) +{ + int level = fd.getLevel(target, sc.intypeof); + if (level != fd.LevelError) + return level; + // Don't give error if in template constraint + if (!sc.inTemplateConstraint) + { + const(char)* xstatic = fd.isStatic() ? "`static` " : ""; + // better diagnostics for static functions + .error(loc, "%s%s `%s` cannot access %s `%s` in frame of function `%s`", + xstatic, fd.kind(), fd.toPrettyChars(), decl.kind(), decl.toChars(), + target.toPrettyChars()); + .errorSupplemental(decl.loc, "`%s` declared here", decl.toChars()); + return fd.LevelError; + } + return 1; +} + +/********************************** + * Decide if attributes for this function can be inferred from examining + * the function body. + * Params: + * fd = function to infer attributes for + * sc = context + * Returns: + * true if can + */ +bool canInferAttributes(FuncDeclaration fd, Scope* sc) +{ + if (!fd.fbody) + return false; + if (fd.isVirtualMethod() && + /* + * https://issues.dlang.org/show_bug.cgi?id=21719 + * + * If we have an auto virtual function we can infer + * the attributes. + */ + !(fd.inferRetType && !fd.isCtorDeclaration())) + return false; // since they may be overridden + if (sc.func && + /********** this is for backwards compatibility for the moment ********/ + (!fd.isMember() || sc.func.isSafeBypassingInference() && !fd.isInstantiated())) + return true; + if (fd.isFuncLiteralDeclaration() || // externs are not possible with literals + (fd.storage_class & STC.inference) || // do attribute inference + fd.isGenerated || // compiler generated function + (fd.inferRetType && !fd.isCtorDeclaration())) + return true; + if (fd.isInstantiated()) + { + auto ti = fd.parent.isTemplateInstance(); + if (ti is null || ti.isTemplateMixin() || ti.tempdecl.ident == fd.ident) + return true; + } + return false; +} + +/********************************************* + * In the current function 'sc.func', we are calling 'fd'. + * 1. Check to see if the current function can call 'fd' , issue error if not. + * 2. If the current function is not the parent of 'fd' , then add + * the current function to the list of siblings of 'fd' . + * 3. If the current function is a literal, and it's accessing an uplevel scope, + * then mark it as a delegate. + * Returns true if error occurs. + */ +bool checkNestedFuncReference(FuncDeclaration fd, Scope* sc, Loc loc) +{ + //printf("FuncDeclaration::checkNestedFuncReference() %s\n", toPrettyChars()); + if (auto fld = fd.isFuncLiteralDeclaration()) + { + if (fld.tok == TOK.reserved) + { + fld.tok = TOK.function_; + fld.vthis = null; + } + } + if (!fd.parent || fd.parent == sc.parent) + return false; + if (fd.ident == Id.require || fd.ident == Id.ensure) + return false; + if (!fd.isThis() && !fd.isNested()) + return false; + // The current function + FuncDeclaration fdthis = sc.parent.isFuncDeclaration(); + if (!fdthis) + return false; // out of function scope + Dsymbol p = fd.toParentLocal(); + Dsymbol p2 = fd.toParent2(); + // Function literals from fdthis to p must be delegates + ensureStaticLinkTo(fdthis, p); + if (p != p2) + ensureStaticLinkTo(fdthis, p2); + if (!fd.isNested()) + return false; + + // The function that this function is in + bool checkEnclosing(FuncDeclaration fdv) + { + if (!fdv) + return false; + if (fdv == fdthis) + return false; + //printf("this = %s in [%s]\n", this.toChars(), this.loc.toChars()); + //printf("fdv = %s in [%s]\n", fdv .toChars(), fdv .loc.toChars()); + //printf("fdthis = %s in [%s]\n", fdthis.toChars(), fdthis.loc.toChars()); + // Add this function to the list of those which called us + if (fdthis != fd) + { + bool found = false; + for (size_t i = 0; i < fd.siblingCallers.length; ++i) + { + if (fd.siblingCallers[i] == fdthis) + found = true; + } + if (!found) + { + //printf("\tadding sibling %s to %s\n", fdthis.toPrettyChars(), toPrettyChars()); + if (!sc.intypeof && !sc.traitsCompiles) + { + fd.siblingCallers.push(fdthis); + fd.computedEscapingSiblings = false; + } + } + } + const lv = fdthis.getLevelAndCheck(loc, sc, fdv, fd); + if (lv == fd.LevelError) + return true; // error + if (lv == -1) + return false; // downlevel call + if (lv == 0) + return false; // same level call + return false; // Uplevel call + } + if (checkEnclosing(p.isFuncDeclaration())) + return true; + if (checkEnclosing(p == p2 ? null : p2.isFuncDeclaration())) + return true; + return false; +} + +/**************************************************** + * Check whether result variable can be built. + * Returns: + * `true` if the function has a return type that + * is different from `void`. + */ +private bool canBuildResultVar(FuncDeclaration fd) +{ + auto f = cast(TypeFunction)fd.type; + return f && f.nextOf() && f.nextOf().toBasetype().ty != Tvoid; } /**************************************************** @@ -1914,7 +2426,7 @@ void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret) if (sc && fd.vresult.semanticRun == PASS.initial) { TypeFunction tf = fd.type.toTypeFunction(); - if (tf.isref) + if (tf.isRef) fd.vresult.storage_class |= STC.ref_; fd.vresult.type = tret; fd.vresult.dsymbolSemantic(sc); @@ -1923,3 +2435,1134 @@ void buildResultVar(FuncDeclaration fd, Scope* sc, Type tret) assert(fd.vresult.parent == fd); } } + +/**************************************************** + * Merge into this function the 'in' contracts of all it overrides. + * 'in's are OR'd together, i.e. only one of them needs to pass. + */ +Statement mergeFrequire(FuncDeclaration fd, Statement sf, Expressions* params) +{ + /* If a base function and its override both have an IN contract, then + * only one of them needs to succeed. This is done by generating: + * + * void derived.in() { + * try { + * base.in(); + * } + * catch () { + * ... body of derived.in() ... + * } + * } + * + * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. + * If base.in() throws, then derived.in()'s body is executed. + */ + foreach (fdv; fd.foverrides) + { + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs. + * https://issues.dlang.org/show_bug.cgi?id=3602 + */ + if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) + { + assert(fdv._scope); + Scope* sc = fdv._scope.push(); + sc.stc &= ~STC.override_; + fdv.semantic3(sc); + sc.pop(); + } + sf = fdv.mergeFrequire(sf, params); + if (!sf || !fdv.fdrequire) + return null; + //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); + /* Make the call: + * try { __require(params); } + * catch (Throwable) { frequire; } + */ + params = Expression.arraySyntaxCopy(params); + Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdrequire, false), params); + Statement s2 = new ExpStatement(fd.loc, e); + auto c = new Catch(fd.loc, getThrowable(), null, sf); + c.internalCatch = true; + auto catches = new Catches(); + catches.push(c); + sf = new TryCatchStatement(fd.loc, s2, catches); + } + return sf; +} + +/**************************************************** + * Merge into this function the 'in' contracts of all it overrides. + */ +Statement mergeFrequireInclusivePreview(FuncDeclaration fd, Statement sf, Expressions* params) +{ + /* If a base function and its override both have an IN contract, then + * the override in contract must widen the guarantee of the base contract. + * This is checked by generating: + * + * void derived.in() { + * try { + * ... body of derived.in() ... + * } + * catch () { + * // derived in rejected this argument. so parent must also reject it, or we've tightened the contract. + * base.in(); + * assert(false, "Logic error: " ~ thr.msg); + * } + * } + */ + foreach (fdv; fd.foverrides) + { + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs. + * https://issues.dlang.org/show_bug.cgi?id=3602 + */ + if (fdv.frequires && fdv.semanticRun != PASS.semantic3done) + { + assert(fdv._scope); + Scope* sc = fdv._scope.push(); + sc.stc &= ~STC.override_; + fdv.semantic3(sc); + sc.pop(); + } + sf = fdv.mergeFrequireInclusivePreview(sf, params); + if (!sf || !fdv.fdrequire) + return null; + + const loc = fd.fdrequire.loc; + //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); + /* Make the call: + * try { frequire; } + * catch (Throwable thr) { __require(params); assert(false, "Logic error: " ~ thr.msg); } + */ + Identifier id = Identifier.generateId("thr"); + params = Expression.arraySyntaxCopy(params); + Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, false), params); + Statement s2 = new ExpStatement(loc, e); + // assert(false, ...) + // TODO make this a runtime helper to allow: + // - chaining the original expression + // - nogc concatenation + Expression msg = new StringExp(loc, "Logic error: in-contract was tighter than parent in-contract"); + Statement fail = new ExpStatement(loc, new AssertExp(loc, IntegerExp.literal!0, msg)); + Statement s3 = new CompoundStatement(loc, s2, fail); + auto c = new Catch(loc, getThrowable(), id, s3); + c.internalCatch = true; + auto catches = new Catches(); + catches.push(c); + sf = new TryCatchStatement(loc, sf, catches); + } + return sf; +} + +/**************************************************** + * Rewrite contracts as statements. + */ +void buildEnsureRequire(FuncDeclaration thisfd) +{ + if (thisfd.frequires) + { + /* in { statements1... } + * in { statements2... } + * ... + * becomes: + * in { { statements1... } { statements2... } ... } + */ + assert(thisfd.frequires.length); + auto loc = (*thisfd.frequires)[0].loc; + auto s = new Statements; + foreach (r; *thisfd.frequires) + { + s.push(new ScopeStatement(r.loc, r, r.loc)); + } + thisfd.frequire = new CompoundStatement(loc, s); + } + if (thisfd.fensures) + { + /* out(id1) { statements1... } + * out(id2) { statements2... } + * ... + * becomes: + * out(__result) { { ref id1 = __result; { statements1... } } + * { ref id2 = __result; { statements2... } } ... } + */ + assert(thisfd.fensures.length); + auto loc = (*thisfd.fensures)[0].ensure.loc; + auto s = new Statements; + foreach (r; *thisfd.fensures) + { + if (r.id && thisfd.canBuildResultVar()) + { + auto rloc = r.ensure.loc; + auto resultId = new IdentifierExp(rloc, Id.result); + auto init = new ExpInitializer(rloc, resultId); + auto stc = STC.ref_ | STC.temp | STC.result; + auto decl = new VarDeclaration(rloc, null, r.id, init, stc); + auto sdecl = new ExpStatement(rloc, decl); + s.push(new ScopeStatement(rloc, new CompoundStatement(rloc, sdecl, r.ensure), rloc)); + } + else + { + s.push(r.ensure); + } + } + thisfd.fensure = new CompoundStatement(loc, s); + } + if (!thisfd.isVirtual()) + return; + /* Rewrite contracts as nested functions, then call them. Doing it as nested + * functions means that overriding functions can call them. + */ + auto f = cast(TypeFunction) thisfd.type; + /* Make a copy of the parameters and make them all ref */ + static Parameters* toRefCopy(ParameterList parameterList) + { + auto result = new Parameters(); + foreach (n, p; parameterList) + { + p = p.syntaxCopy(); + if (!p.isLazy()) + p.storageClass = (p.storageClass | STC.ref_) & ~STC.out_; + p.defaultArg = null; // won't be the same with ref + result.push(p); + } + return result; + } + if (thisfd.frequire) + { + /* in { ... } + * becomes: + * void __require(ref params) { ... } + * __require(params); + */ + Loc loc = thisfd.frequire.loc; + thisfd.fdrequireParams = new Expressions(); + if (thisfd.parameters) + { + foreach (vd; *thisfd.parameters) + thisfd.fdrequireParams.push(new VarExp(loc, vd)); + } + auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f); + auto fparams = toRefCopy(fo.parameterList); + auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); + tf.isNothrow = f.isNothrow; + tf.isNogc = f.isNogc; + tf.purity = f.purity; + tf.trust = f.trust; + auto fd = new FuncDeclaration(loc, loc, Id.require, STC.none, tf); + fd.fbody = thisfd.frequire; + Statement s1 = new ExpStatement(loc, fd); + Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdrequireParams); + Statement s2 = new ExpStatement(loc, e); + thisfd.frequire = new CompoundStatement(loc, s1, s2); + thisfd.fdrequire = fd; + } + /* We need to set fdensureParams here and not in the block below to + * have the parameters available when calling a base class ensure(), + * even if this function doesn't have an out contract. + */ + thisfd.fdensureParams = new Expressions(); + if (thisfd.canBuildResultVar()) + thisfd.fdensureParams.push(new IdentifierExp(thisfd.loc, Id.result)); + if (thisfd.parameters) + { + foreach (vd; *thisfd.parameters) + thisfd.fdensureParams.push(new VarExp(thisfd.loc, vd)); + } + if (thisfd.fensure) + { + /* out (result) { ... } + * becomes: + * void __ensure(ref tret result, ref params) { ... } + * __ensure(result, params); + */ + Loc loc = thisfd.fensure.loc; + auto fparams = new Parameters(); + if (thisfd.canBuildResultVar()) + { + Parameter p = new Parameter(loc, STC.ref_ | STC.const_, f.nextOf(), Id.result, null, null); + fparams.push(p); + } + auto fo = cast(TypeFunction)(thisfd.originalType ? thisfd.originalType : f); + fparams.pushSlice((*toRefCopy(fo.parameterList))[]); + auto tf = new TypeFunction(ParameterList(fparams), Type.tvoid, LINK.d); + tf.isNothrow = f.isNothrow; + tf.isNogc = f.isNogc; + tf.purity = f.purity; + tf.trust = f.trust; + auto fd = new FuncDeclaration(loc, loc, Id.ensure, STC.none, tf); + fd.fbody = thisfd.fensure; + Statement s1 = new ExpStatement(loc, fd); + Expression e = new CallExp(loc, new VarExp(loc, fd, false), thisfd.fdensureParams); + Statement s2 = new ExpStatement(loc, e); + thisfd.fensure = new CompoundStatement(loc, s1, s2); + thisfd.fdensure = fd; + } +} + +/**************************************************** + * Determine whether an 'out' contract is declared inside + * the given function or any of its overrides. + * Params: + * fd = the function to search + * Returns: + * true found an 'out' contract + */ +bool needsFensure(FuncDeclaration fd) @safe +{ + if (fd.fensures) + return true; + + foreach (fdv; fd.foverrides) + { + if (needsFensure(fdv)) + return true; + } + return false; +} + +/**************************************************** + * Merge into this function the 'out' contracts of all it overrides. + * 'out's are AND'd together, i.e. all of them need to pass. + */ +Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Expressions* params) +{ + /* Same comments as for mergeFrequire(), except that we take care + * of generating a consistent reference to the 'result' local by + * explicitly passing 'result' to the nested function as a reference + * argument. + * This won't work for the 'this' parameter as it would require changing + * the semantic code for the nested function so that it looks on the parameter + * list for the 'this' pointer, something that would need an unknown amount + * of tweaking of various parts of the compiler that I'd rather leave alone. + */ + foreach (fdv; fd.foverrides) + { + /* The semantic pass on the contracts of the overridden functions must + * be completed before code generation occurs. + * https://issues.dlang.org/show_bug.cgi?id=3602 and + * https://issues.dlang.org/show_bug.cgi?id=5230 + */ + if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) + { + assert(fdv._scope); + Scope* sc = fdv._scope.push(); + sc.stc &= ~STC.override_; + fdv.semantic3(sc); + sc.pop(); + } + sf = fdv.mergeFensure(sf, oid, params); + if (!fdv.fdensure) + continue; + + //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); + // Make the call: __ensure(result, params) + params = Expression.arraySyntaxCopy(params); + if (fd.canBuildResultVar()) + { + Type t1 = fdv.type.nextOf().toBasetype(); + Type t2 = fd.type.nextOf().toBasetype(); + if (t1.isBaseOf(t2, null)) + { + /* Making temporary reference variable is necessary + * in covariant return. + * https://issues.dlang.org/show_bug.cgi?id=5204 + * https://issues.dlang.org/show_bug.cgi?id=10479 + */ + Expression* eresult = &(*params)[0]; + auto ei = new ExpInitializer(Loc.initial, *eresult); + auto v = new VarDeclaration(Loc.initial, t1, Identifier.generateId("__covres"), ei); + v.storage_class |= STC.temp; + auto de = new DeclarationExp(Loc.initial, v); + auto ve = new VarExp(Loc.initial, v); + *eresult = new CommaExp(Loc.initial, de, ve); + } + } + Expression e = new CallExp(fd.loc, new VarExp(fd.loc, fdv.fdensure, false), params); + Statement s2 = new ExpStatement(fd.loc, e); + if (sf) + { + sf = new CompoundStatement(sf.loc, s2, sf); + } + else + sf = s2; + } + return sf; +} + +/******************************* + * Modify all expression type of return statements to tret. + * + * On function literals, return type may be modified based on the context type + * after its semantic3 is done, in FuncExp::implicitCastTo. + * + * A function() dg = (){ return new B(); } // OK if is(B : A) == true + * + * If B to A conversion is convariant that requires offseet adjusting, + * all return statements should be adjusted to return expressions typed A. + */ +void modifyReturns(FuncLiteralDeclaration fld, Scope* sc, Type tret) +{ + extern (C++) final class RetWalker : StatementRewriteWalker + { + alias visit = typeof(super).visit; + public: + Scope* sc; + Type tret; + FuncLiteralDeclaration fld; + override void visit(ReturnStatement s) + { + Expression exp = s.exp; + if (exp && !exp.type.equals(tret)) + s.exp = exp.implicitCastTo(sc, tret); + } + } + if (fld.semanticRun < PASS.semantic3done) + return; + if (fld.fes) + return; + scope RetWalker w = new RetWalker(); + w.sc = sc; + w.tret = tret; + w.fld = fld; + fld.fbody.accept(w); + // Also update the inferred function type to match the new return type. + // This is required so the code generator does not try to cast the + // modified returns back to the original type. + if (fld.inferRetType && fld.type.nextOf() != tret) + fld.type.toTypeFunction().next = tret; +} + +/************************************** + * When a traits(compiles) is used on a function literal call + * we need to take into account if the body of the function + * violates any attributes, however, we must not affect the + * attribute inference on the outer function. The attributes + * of the function literal still need to be inferred, therefore + * we need a way to check for the scope that the traits compiles + * introduces. + * + * Params: + * sc = scope to be checked for + * + * Returns: `true` if the provided scope is the root + * of the traits compiles list of scopes. + */ +bool isRootTraitsCompilesScope(Scope* sc) @safe +{ + return (sc.traitsCompiles) && !sc.func.skipCodegen; +} + +/+ + + Checks the parameter and return types iff this is a `main` function. + + + + The following signatures are allowed for a `D main`: + + - Either no or a single parameter of type `string[]` + + - Return type is either `void`, `int` or `noreturn` + + + + The following signatures are standard C: + + - `int main()` + + - `int main(int, char**)` + + + + This function accepts the following non-standard extensions: + + - `char** envp` as a third parameter + + - `void` / `noreturn` as return type + + + + This function will issue errors for unexpected arguments / return types. + +/ +extern (D) void checkMain(FuncDeclaration fd) +{ + if (fd.ident != Id.main || fd.isMember() || fd.isNested()) + return; // Not a main function + + TypeFunction tf = fd.type.toTypeFunction(); + + Type retType = tf.nextOf(); + if (!retType) + { + // auto main(), check after semantic + assert(fd.inferRetType); + return; + } + + /// Checks whether `t` is equivalent to `char**` + /// Ignores qualifiers and treats enums according to their base type + static bool isCharPtrPtr(Type t) + { + auto tp = t.toBasetype().isTypePointer(); + if (!tp) + return false; + + tp = tp.next.toBasetype().isTypePointer(); + if (!tp) + return false; + + return tp.next.toBasetype().ty == Tchar; + } + + // Neither of these qualifiers is allowed because they affect the ABI + enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_; + + const nparams = tf.parameterList.length; + bool argerr; + + const linkage = fd.resolvedLinkage(); + if (linkage == LINK.d) + { + if (nparams == 1) + { + auto fparam0 = tf.parameterList[0]; + auto t = fparam0.type.toBasetype(); + if (t.ty != Tarray || + t.nextOf().ty != Tarray || + t.nextOf().nextOf().ty != Tchar || + fparam0.storageClass & invalidSTC) + { + argerr = true; + } + } + + if (tf.parameterList.varargs || nparams >= 2 || argerr) + .error(fd.loc, "%s `%s` parameter list must be empty or accept one parameter of type `string[]`", fd.kind, fd.toPrettyChars); + } + + else if (linkage == LINK.c) + { + if (nparams == 2 || nparams == 3) + { + // Argument count must be int + auto argCount = tf.parameterList[0]; + argerr |= !!(argCount.storageClass & invalidSTC); + argerr |= argCount.type.toBasetype().ty != Tint32; + + // Argument pointer must be char** + auto argPtr = tf.parameterList[1]; + argerr |= !!(argPtr.storageClass & invalidSTC); + argerr |= !isCharPtrPtr(argPtr.type); + + // `char** environ` is a common extension, see J.5.1 of the C standard + if (nparams == 3) + { + auto envPtr = tf.parameterList[2]; + argerr |= !!(envPtr.storageClass & invalidSTC); + argerr |= !isCharPtrPtr(envPtr.type); + } + } + else + argerr = nparams != 0; + + // Disallow variadic main() - except for K&R declarations in C files. + // E.g. int main(), int main(argc, argv) int argc, char** argc { ... } + if (tf.parameterList.varargs && (!fd.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams))) + argerr |= true; + + if (argerr) + { + .error(fd.loc, "%s `%s` parameters must match one of the following signatures", fd.kind, fd.toPrettyChars); + fd.loc.errorSupplemental("`main()`"); + fd.loc.errorSupplemental("`main(int argc, char** argv)`"); + fd.loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]"); + } + } + else + return; // Neither C nor D main, ignore (should probably be an error) + + // Allow enums with appropriate base types (same ABI) + retType = retType.toBasetype(); + + if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn) + .error(fd.loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", fd.kind, fd.toPrettyChars, tf.nextOf().toChars()); +} + +/*********************************************** + * Check all return statements for a function to verify that returning + * using NRVO is possible. + * + * Returns: + * `false` if the result cannot be returned by hidden reference. + */ +extern (D) bool checkNRVO(FuncDeclaration fd) +{ + //printf("checkNRVO*() %s\n", fd.ident.toChars()); + if (!fd.isNRVO() || fd.returns is null) + return false; + + auto tf = fd.type.toTypeFunction(); + if (tf.isRef) + return false; + + foreach (rs; *fd.returns) + { + if (auto ve = rs.exp.isVarExp()) + { + auto v = ve.var.isVarDeclaration(); + if (!v || v.isReference()) + return false; + if (fd.nrvo_var is null) + { + // Variables in the data segment (e.g. globals, TLS or not), + // parameters and closure variables cannot be NRVOed. + if (v.isDataseg() || v.isParameter() || v.toParent2() != fd) + return false; + if (v.nestedrefs.length && fd.needsClosure()) + return false; + // don't know if the return storage is aligned + version (MARS) + { + if (fd.alignSectionVars && (*fd.alignSectionVars).contains(v)) + return false; + } + // The variable type needs to be equivalent to the return type. + if (!v.type.equivalent(tf.next)) + return false; + //printf("Setting nrvo to %s\n", v.toChars()); + fd.nrvo_var = v; + } + else if (fd.nrvo_var != v) + return false; + } + else //if (!exp.isLvalue()) // keep NRVO-ability + return false; + } + return true; +} + +/************************************** + * The function is doing something impure, so mark it as impure. + * + * Params: + * fd = function declaration to mark + * loc = location of impure action + * fmt = format string for error message + * args = argument to format string + * + * Returns: `true` if there's a purity error + */ +extern (D) bool setImpure(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...) +{ + if (fd.purityInprocess) + { + fd.purityInprocess = false; + if (fmt) + fd.pureViolation = new AttributeViolation(loc, fmt, args); // impure action + else if (args.length > 0) + { + if (auto sa = args[0].isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + fd.pureViolation = new AttributeViolation(loc, fd2); // call to impure function + } + } + } + + if (fd.fes) + fd.fes.func.setImpure(loc, fmt, args); + } + else if (fd.isPure()) + return true; + return false; +} + +PURE isPure(FuncDeclaration fd) +{ + //printf("FuncDeclaration::isPure() '%s'\n", toChars()); + + + TypeFunction tf = fd.type.toTypeFunction(); + if (fd.purityInprocess) + fd.setImpure(Loc.initial, null); + if (tf.purity == PURE.fwdref) + tf.purityLevel(); + PURE purity = tf.purity; + if (purity > PURE.weak && fd.isNested()) + purity = PURE.weak; + if (purity > PURE.weak && fd.needThis()) + { + // The attribute of the 'this' reference affects purity strength + if (fd.type.mod & MODFlags.immutable_) + { + } + else if (fd.type.mod & (MODFlags.const_ | MODFlags.wild) && purity >= PURE.const_) + purity = PURE.const_; + else + purity = PURE.weak; + } + tf.purity = purity; + // ^ This rely on the current situation that every FuncDeclaration has a + // unique TypeFunction. + return purity; +} + +extern (D) PURE isPureBypassingInference(FuncDeclaration fd) +{ + if (fd.purityInprocess) + return PURE.fwdref; + else + return fd.isPure(); +} + +/************************************** + * Performs type-based alias analysis between a newly created value and a pre- + * existing memory reference: + * + * Assuming that a reference A to a value of type `ta` was available to the code + * that created a reference B to a value of type `tb`, it returns whether B + * might alias memory reachable from A based on the types involved (either + * directly or via any number of indirections in either A or B). + * + * This relation is not symmetric in the two arguments. For example, a + * a `const(int)` reference can point to a pre-existing `int`, but not the other + * way round. + * + * Examples: + * + * ta, tb, result + * `const(int)`, `int`, `false` + * `int`, `const(int)`, `true` + * `int`, `immutable(int)`, `false` + * const(immutable(int)*), immutable(int)*, false // BUG: returns true + * + * Params: + * ta = value type being referred to + * tb = referred to value type that could be constructed from ta + * + * Returns: + * true if reference to `tb` is isolated from reference to `ta` + */ +bool traverseIndirections(Type ta, Type tb) +{ + //printf("traverseIndirections(%s, %s)\n", ta.toChars(), tb.toChars()); + + static bool traverse(Type ta, Type tb, ref scope AssocArray!(const(char)*, bool) table, bool reversePass) + { + //printf("traverse(%s, %s)\n", ta.toChars(), tb.toChars()); + ta = ta.baseElemOf(); + tb = tb.baseElemOf(); + + // First, check if the pointed-to types are convertible to each other such + // that they might alias directly. + static bool mayAliasDirect(Type source, Type target) + { + return + // if source is the same as target or can be const-converted to target + source.constConv(target) != MATCH.nomatch || + // if target is void and source can be const-converted to target + (target.ty == Tvoid && MODimplicitConv(source.mod, target.mod)); + } + + if (mayAliasDirect(reversePass ? tb : ta, reversePass ? ta : tb)) + { + //printf(" true mayalias %s %s %d\n", ta.toChars(), tb.toChars(), reversePass); + return false; + } + if (ta.nextOf() && ta.nextOf() == tb.nextOf()) + { + //printf(" next==next %s %s %d\n", ta.toChars(), tb.toChars(), reversePass); + return true; + } + + if (tb.ty == Tclass || tb.ty == Tstruct) + { + /* Traverse the type of each field of the aggregate + */ + bool* found = table.getLvalue(tb.deco); + if (*found == true) + return true; // We have already seen this symbol, break the cycle + else + *found = true; + + AggregateDeclaration sym = tb.toDsymbol(null).isAggregateDeclaration(); + foreach (v; sym.fields) + { + Type tprmi = v.type.addMod(tb.mod); + //printf("\ttb = %s, tprmi = %s\n", tb.toChars(), tprmi.toChars()); + if (!traverse(ta, tprmi, table, reversePass)) + return false; + } + } + else if (tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tpointer) + { + Type tind = tb.nextOf(); + if (!traverse(ta, tind, table, reversePass)) + return false; + } + else if (tb.hasPointers()) + { + // BUG: consider the context pointer of delegate types + return false; + } + + // Still no match, so try breaking up ta if we have not done so yet. + if (!reversePass) + { + scope newTable = AssocArray!(const(char)*, bool)(); + return traverse(tb, ta, newTable, true); + } + + return true; + } + + // To handle arbitrary levels of indirections in both parameters, we + // recursively descend into aggregate members/levels of indirection in both + // `ta` and `tb` while avoiding cycles. Start with the original types. + scope table = AssocArray!(const(char)*, bool)(); + const result = traverse(ta, tb, table, false); + //printf(" returns %d\n", result); + return result; +} + +/******************************************** + * Params: + * fd = function declaration to check + * t = type of object to test one level of indirection down + * Returns: + * true if an object typed `t` has no indirections + * which could have come from the function's parameters, mutable + * globals, or uplevel functions. + */ +bool isTypeIsolatedIndirect(FuncDeclaration fd, Type t) +{ + //printf("isTypeIsolatedIndirect(t: %s)\n", t.toChars()); + assert(t); + + /* Since `t` is one level down from an indirection, it could pick + * up a reference to a mutable global or an outer function, so + * return false. + */ + if (!fd.isPureBypassingInference() || fd.isNested()) + return false; + + TypeFunction tf = fd.type.toTypeFunction(); + + //printf("isTypeIsolatedIndirect(%s) t = %s\n", tf.toChars(), t.toChars()); + + foreach (i, fparam; tf.parameterList) + { + Type tp = fparam.type; + if (!tp) + continue; + + if (fparam.isLazy() || fparam.isReference()) + { + if (!traverseIndirections(tp, t)) + return false; + continue; + } + + /* Goes down one level of indirection, then calls traverseIndirection() on + * the result. + * Returns: + * true if t is isolated from tp + */ + static bool traverse(Type tp, Type t) + { + tp = tp.baseElemOf(); + switch (tp.ty) + { + case Tarray: + case Tpointer: + return traverseIndirections(tp.nextOf(), t); + + case Taarray: + case Tclass: + return traverseIndirections(tp, t); + + case Tstruct: + /* Drill down and check the struct's fields + */ + auto sym = tp.toDsymbol(null).isStructDeclaration(); + foreach (v; sym.fields) + { + Type tprmi = v.type.addMod(tp.mod); + //printf("\ttp = %s, tprmi = %s\n", tp.toChars(), tprmi.toChars()); + if (!traverse(tprmi, t)) + return false; + } + return true; + + default: + return true; + } + } + + if (!traverse(tp, t)) + return false; + } + // The 'this' reference is a parameter, too + if (AggregateDeclaration ad = fd.isCtorDeclaration() ? null : fd.isThis()) + { + Type tthis = ad.getType().addMod(tf.mod); + //printf("\ttthis = %s\n", tthis.toChars()); + if (!traverseIndirections(tthis, t)) + return false; + } + + return true; +} + +/******************************************** + * See if pointers from function parameters, mutable globals, or uplevel functions + * could leak into return value. + * Returns: + * true if the function return value is isolated from + * any inputs to the function + */ +extern (D) bool isReturnIsolated(FuncDeclaration fd) +{ + //printf("isReturnIsolated(this: %s)\n", this.toChars); + TypeFunction tf = fd.type.toTypeFunction(); + assert(tf.next); + + Type treti = tf.next; + if (tf.isRef) + return fd.isTypeIsolatedIndirect(treti); // check influence from parameters + + return fd.isTypeIsolated(treti); +} + +/******************** + * See if pointers from function parameters, mutable globals, or uplevel functions + * could leak into type `t`. + * Params: + * t = type to check if it is isolated + * Returns: + * true if `t` is isolated from + * any inputs to the function + */ +extern (D) bool isTypeIsolated(FuncDeclaration fd, Type t) +{ + StringTable!Type parentTypes; + const uniqueTypeID = t.getUniqueID(); + if (uniqueTypeID) + { + const cacheResultPtr = uniqueTypeID in fd.isTypeIsolatedCache; + if (cacheResultPtr !is null) + return *cacheResultPtr; + + parentTypes._init(); + const isIsolated = fd.isTypeIsolated(t, parentTypes); + fd.isTypeIsolatedCache[uniqueTypeID] = isIsolated; + return isIsolated; + } + else + { + parentTypes._init(); + return fd.isTypeIsolated(t, parentTypes); + } +} + +///ditto +extern (D) bool isTypeIsolated(FuncDeclaration fd, Type t, ref StringTable!Type parentTypes) +{ + //printf("this: %s, isTypeIsolated(t: %s)\n", this.toChars(), t.toChars()); + + t = t.baseElemOf(); + switch (t.ty) + { + case Tarray: + case Tpointer: + return fd.isTypeIsolatedIndirect(t.nextOf()); // go down one level + + case Taarray: + case Tclass: + return fd.isTypeIsolatedIndirect(t); + + case Tstruct: + /* Drill down and check the struct's fields + */ + auto sym = t.toDsymbol(null).isStructDeclaration(); + const tName = t.toChars.toDString; + const entry = parentTypes.insert(tName, t); + if (entry == null) + { + //we've already seen this type in a parent, not isolated + return false; + } + foreach (v; sym.fields) + { + Type tmi = v.type.addMod(t.mod); + //printf("\tt = %s, v: %s, vtype: %s, tmi = %s\n", + // t.toChars(), v.toChars(), v.type.toChars(), tmi.toChars()); + if (!fd.isTypeIsolated(tmi, parentTypes)) + return false; + } + return true; + + default: + return true; + } +} + +/** + * Check signature of `pragma(printf)` function, print error if invalid. + * + * printf/scanf-like functions must be of the form: + * extern (C/C++) T printf([parameters...], const(char)* format, ...); + * or: + * extern (C/C++) T vprintf([parameters...], const(char)* format, va_list); + * + * Params: + * funcdecl = function to check + * f = function type + * sc = scope + */ +private void checkPrintfScanfSignature(FuncDeclaration funcdecl, TypeFunction f, Scope* sc) +{ + static bool isPointerToChar(Parameter p) + { + if (auto tptr = p.type.isTypePointer()) + { + return tptr.next.ty == Tchar; + } + return false; + } + + bool isVa_list(Parameter p) + { + return p.type.equals(target.va_listType(funcdecl.loc, sc)); + } + + const nparams = f.parameterList.length; + const p = (funcdecl.printf ? Id.printf : Id.scanf).toChars(); + if (!(f.linkage == LINK.c || f.linkage == LINK.cpp)) + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have `extern(C)` or `extern(C++)` linkage," + ~" not `extern(%s)`", + p, funcdecl.toChars(), f.linkage.linkageToChars()); + } + if (f.parameterList.varargs == VarArg.variadic) + { + if (!(nparams >= 1 && isPointerToChar(f.parameterList[nparams - 1]))) + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have" + ~ " signature `%s %s([parameters...], const(char)*, ...)` not `%s`", + p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars(), funcdecl.type.toChars()); + } + } + else if (f.parameterList.varargs == VarArg.none) + { + if(!(nparams >= 2 && isPointerToChar(f.parameterList[nparams - 2]) && + isVa_list(f.parameterList[nparams - 1]))) + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have"~ + " signature `%s %s([parameters...], const(char)*, va_list)`", + p, funcdecl.toChars(), f.next.toChars(), funcdecl.toChars()); + } + else + { + .error(funcdecl.loc, "`pragma(%s)` function `%s` must have C-style variadic `...` or `va_list` parameter", + p, funcdecl.toChars()); + } +} + +/*************************************************** + * Visit each overloaded function/template in turn, and call dg(s) on it. + * Exit when no more, or dg(s) returns nonzero. + * + * Params: + * fstart = symbol to start from + * dg = the delegate to be called on the overload + * sc = context used to check if symbol is accessible (and therefore visible), + * can be null + * + * Returns: + * ==0 continue + * !=0 done (and the return value from the last dg() call) + */ +extern (D) int overloadApply(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc = null) +{ + Dsymbols visited; + + int overloadApplyRecurse(Dsymbol fstart, scope int delegate(Dsymbol) dg, Scope* sc) + { + // Detect cyclic calls. + if (visited.contains(fstart)) + return 0; + visited.push(fstart); + + Dsymbol next; + for (auto d = fstart; d; d = next) + { + import dmd.access : checkSymbolAccess; + if (auto od = d.isOverDeclaration()) + { + /* The scope is needed here to check whether a function in + an overload set was added by means of a private alias (or a + selective import). If the scope where the alias is created + is imported somewhere, the overload set is visible, but the private + alias is not. + */ + if (sc) + { + if (checkSymbolAccess(sc, od)) + { + if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) + return r; + } + } + else if (int r = overloadApplyRecurse(od.aliassym, dg, sc)) + return r; + next = od.overnext; + } + else if (auto fa = d.isFuncAliasDeclaration()) + { + if (fa.hasOverloads) + { + if (int r = overloadApplyRecurse(fa.funcalias, dg, sc)) + return r; + } + else if (auto fd = fa.toAliasFunc()) + { + if (int r = dg(fd)) + return r; + } + else + { + .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); + break; + } + next = fa.overnext; + } + else if (auto ad = d.isAliasDeclaration()) + { + if (sc) + { + if (checkSymbolAccess(sc, ad)) + next = ad.toAlias(); + } + else + next = ad.toAlias(); + if (next == ad) + break; + if (next == fstart) + break; + } + else if (auto td = d.isTemplateDeclaration()) + { + if (int r = dg(td)) + return r; + next = td.overnext; + } + else if (auto fd = d.isFuncDeclaration()) + { + if (int r = dg(fd)) + return r; + next = fd.overnext; + } + else if (auto os = d.isOverloadSet()) + { + foreach (ds; os.a) + if (int r = dg(ds)) + return r; + } + else + { + .error(d.loc, "%s `%s` is aliased to a function", d.kind, d.toPrettyChars); + break; + // BUG: should print error message? + } + } + return 0; + } + return overloadApplyRecurse(fstart, dg, sc); +} diff --git a/gcc/d/dmd/globals.d b/gcc/d/dmd/globals.d index af7b1fa..624738e 100644 --- a/gcc/d/dmd/globals.d +++ b/gcc/d/dmd/globals.d @@ -1,12 +1,12 @@ /** * Stores command line options and contains other miscellaneous declarations. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/globals.d, _globals.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/globals.d, _globals.d) * Documentation: https://dlang.org/phobos/dmd_globals.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/globals.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/globals.d */ module dmd.globals; @@ -40,24 +40,6 @@ enum DiagnosticReporting : ubyte off, /// disable diagnostic } -/// In which context checks for assertions, contracts, bounds checks etc. are enabled -enum CHECKENABLE : ubyte -{ - _default, /// initial value - off, /// never do checking - on, /// always do checking - safeonly, /// do checking only in @safe functions -} - -/// What should happend when an assertion fails -enum CHECKACTION : ubyte -{ - D, /// call D assert on failure - C, /// call C assert on failure - halt, /// cause program halt on failure - context, /// call D assert with the error context on failure -} - /** Each flag represents a field that can be included in the JSON output. @@ -80,6 +62,7 @@ enum CppStdRevision : uint cpp14 = 2014_02, cpp17 = 2017_03, cpp20 = 2020_02, + cpp23 = 2023_02, } /// Trivalent boolean to represent the state of a `revert`able change @@ -90,6 +73,23 @@ enum FeatureState : ubyte enabled = 2, /// Specified as `-preview=` } +/// Different identifier tables specifiable by CLI +enum CLIIdentifierTable : ubyte +{ + default_ = 0, /// Not specified by user + C99 = 1, /// Tables from C99 standard + C11 = 2, /// Tables from C11 standard + UAX31 = 3, /// Tables from the Unicode Standard Annex 31: UNICODE IDENTIFIERS AND SYNTAX + All = 4, /// The least restrictive set of all other tables +} + +/// Specifies the mode for error printing +enum ErrorPrintMode : ubyte +{ + simpleError, // Print errors without squiggles and carets + printErrorContext, // Print errors with context (source line and caret) +} + extern(C++) struct Output { bool doOutput; // Output is enabled @@ -134,15 +134,15 @@ extern(C++) struct Verbose bool complex = true; // identify complex/imaginary type usage bool vin; // identify 'in' parameters bool showGaggedErrors; // print gagged errors anyway - bool printErrorContext; // print errors with the error context (the error line in the source file) bool logo; // print compiler logo bool color; // use ANSI colors in console output bool cov; // generate code coverage data + ErrorPrintMode errorPrintMode; // enum for error printing mode MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages uint errorLimit = 20; uint errorSupplementLimit = 6; // Limit the number of supplemental messages for each error (0 means unlimited) - uint errorSupplementCount() + uint errorSupplementCount() @safe { if (verbose) return uint.max; @@ -152,10 +152,15 @@ extern(C++) struct Verbose } } +extern (C++) struct ImportPathInfo { + const(char)* path; // char*'s of where to look for import modules +} + /// Put command line switches in here extern (C++) struct Param { bool obj = true; // write object file + bool readStdin; // saw "-" on command line, read source file from stdin bool multiobj; // break one object file into multiple ones bool trace; // insert profiling hooks bool tracegc; // instrument calls to 'new' @@ -165,7 +170,7 @@ extern (C++) struct Param bool useInline = false; // inline expand functions bool release; // build release version bool preservePaths; // true means don't strip path from source file - DiagnosticReporting warnings = DiagnosticReporting.off; // how compiler warnings are handled + DiagnosticReporting useWarnings = DiagnosticReporting.off; // how compiler warnings are handled bool cov; // generate code coverage data ubyte covPercent; // 0..100 code coverage percentage required bool ctfe_cov = false; // generate coverage data for ctfe @@ -196,6 +201,8 @@ extern (C++) struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md FeatureState noSharedAccess; // read/write access to shared memory objects bool previewIn; // `in` means `[ref] scope const`, accepts rvalues bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract @@ -217,9 +224,12 @@ extern (C++) struct Param CHECKACTION checkAction = CHECKACTION.D; // action to take when bounds, asserts or switch defaults are violated + CLIIdentifierTable dIdentifierTable = CLIIdentifierTable.default_; + CLIIdentifierTable cIdentifierTable = CLIIdentifierTable.default_; + const(char)[] argv0; // program name Array!(const(char)*) modFileAliasStrings; // array of char*'s of -I module filename alias strings - Array!(const(char)*) imppath; // array of char*'s of where to look for import modules + Array!(ImportPathInfo) imppath; // array of import path information of where to look for import modules Array!(const(char)*) fileImppath; // array of char*'s of where to look for file import modules const(char)[] objdir; // .obj/.lib file output directory const(char)[] objname; // .obj file output name @@ -234,8 +244,7 @@ extern (C++) struct Param Output mixinOut; // write expanded mixins for debugging Output moduleDeps; // Generate `.deps` module dependencies - uint debuglevel; // debug level - uint versionlevel; // version level + bool debugEnabled; // Global -debug flag (no -debug=XXX) is active bool run; // run resulting executable Strings runargs; // arguments for executable @@ -253,8 +262,15 @@ extern (C++) struct Param const(char)[] exefile; const(char)[] mapfile; + bool fullyQualifiedObjectFiles; // prepend module names to object files to prevent name conflicts with -od + + // Time tracing + bool timeTrace = false; /// Whether profiling of compile time is enabled + uint timeTraceGranularityUs = 500; /// In microseconds, minimum event size to report + const(char)* timeTraceFile; /// File path of output file + /// - bool parsingUnittestsRequired() + bool parsingUnittestsRequired() @safe { return useUnitTests || ddoc.doOutput || dihdr.doOutput; } @@ -277,10 +293,11 @@ extern (C++) struct Global { const(char)[] inifilename; /// filename of configuration file as given by `-conf=`, or default value - string copyright = "Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved"; + string copyright = "Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved"; string written = "written by Walter Bright"; - Array!(const(char)*) path; /// Array of char*'s which form the import lookup path + Array!(ImportPathInfo) path; /// Array of path informations which form the import lookup path + Array!(const(char)*) importPaths; /// Array of char*'s which form the import lookup path without metadata Array!(const(char)*) filePath; /// Array of char*'s which form the file import lookup path private enum string _version = import("VERSION"); @@ -289,6 +306,7 @@ extern (C++) struct Global Param params; /// command line parameters uint errors; /// number of errors reported so far + uint deprecations; /// number of deprecations reported so far uint warnings; /// number of warnings reported so far uint gag; /// !=0 means gag reporting of errors & warnings uint gaggedErrors; /// number of errors reported while gagged @@ -310,7 +328,7 @@ extern (C++) struct Global ErrorSink errorSink; /// where the error messages go ErrorSink errorSinkNull; /// where the error messages are ignored - extern (C++) DArray!ubyte function(FileName, ref const Loc, ref OutBuffer) preprocess; + extern (C++) DArray!ubyte function(FileName, Loc, ref OutBuffer) preprocess; nothrow: @@ -380,7 +398,16 @@ extern (C++) struct Global { compileEnv.vendor = "GNU D"; } - compileEnv.versionNumber = parseVersionNumber(_version); + else version (IN_LLVM) + { + compileEnv.vendor = "LDC"; + + import dmd.console : detectTerminal; + params.v.color = detectTerminal(); + } + + params.v.errorPrintMode = ErrorPrintMode.printErrorContext; // Enable error context globally by default + compileEnv.versionNumber = parseVersionNumber(versionString()); /* Initialize date, time, and timestamp */ @@ -457,6 +484,17 @@ extern (C++) struct Global } /** + * Indicate to stateful error sinks that no more errors can be produced. + * This is to support error sinks that collect information to produce a + * single (say) report. + */ + extern(C++) void plugErrorSinks() + { + global.errorSink.plugSink(); + global.errorSinkNull.plugSink(); + } + + /** Returns: the version as the number that would be returned for __VERSION__ */ extern(C++) uint versionNumber() @safe diff --git a/gcc/d/dmd/globals.h b/gcc/d/dmd/globals.h index f553ae6..62a575e 100644 --- a/gcc/d/dmd/globals.h +++ b/gcc/d/dmd/globals.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -13,6 +13,7 @@ #include "root/dcompat.h" #include "root/ctfloat.h" #include "common/outbuffer.h" +#include "common/charactertables.h" #include "root/filename.h" #include "compiler.h" @@ -34,7 +35,8 @@ enum enum class MessageStyle : unsigned char { digitalmars, // file(line,column): message - gnu // file:line:column: message + gnu, // file:line:column: message + sarif // JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html }; // The state of array bounds checking @@ -71,7 +73,8 @@ enum CppStdRevision CppStdRevisionCpp11 = 201103, CppStdRevisionCpp14 = 201402, CppStdRevisionCpp17 = 201703, - CppStdRevisionCpp20 = 202002 + CppStdRevisionCpp20 = 202002, + CppStdRevisionCpp23 = 202302, }; /// Trivalent boolean to represent the state of a `revert`able change @@ -82,6 +85,23 @@ enum class FeatureState : unsigned char enabled = 2, /// Specified as `-preview=` }; +/// Different identifier tables specifiable by CLI +enum class CLIIdentifierTable : unsigned char +{ + default_ = 0, /// Not specified by user + C99 = 1, /// Tables from C99 standard + C11 = 2, /// Tables from C11 standard + UAX31 = 3, /// Tables from the Unicode Standard Annex 31: UNICODE IDENTIFIERS AND SYNTAX + All = 4, /// The least restrictive set of all other tables +}; + +/// Specifies the mode for error printing +enum class ErrorPrintMode : unsigned char +{ + simpleError, // Print errors without squiggles and carets + printErrorContext, // Print errors with the error line and caret +}; + struct Output { /// Configuration for the compiler generator @@ -126,20 +146,29 @@ struct Verbose d_bool complex = true; // identify complex/imaginary type usage d_bool vin; // identify 'in' parameters d_bool showGaggedErrors; // print gagged errors anyway - d_bool printErrorContext; // print errors with the error context (the error line in the source file) d_bool logo; // print compiler logo d_bool color; // use ANSI colors in console output d_bool cov; // generate code coverage data + ErrorPrintMode errorPrintMode; // enum for error printing mode MessageStyle messageStyle; // style of file/line annotations on messages unsigned errorLimit; unsigned errorSupplementLimit; // Limit the number of supplemental messages for each error (0 means unlimited) unsigned errorSupplementCount(); }; +struct ImportPathInfo +{ + const char* path; + + ImportPathInfo() : path(NULL) { } + ImportPathInfo(const char* p) : path(p) { } +}; + // Put command line switches in here struct Param { d_bool obj; // write object file + d_bool readStdin; // read source file from stdin d_bool multiobj; // break one object file into multiple ones d_bool trace; // insert profiling hooks d_bool tracegc; // instrument calls to 'new' @@ -149,7 +178,7 @@ struct Param d_bool useInline; // inline expand functions d_bool release; // build release version d_bool preservePaths; // true means don't strip path from source file - Diagnostic warnings; + Diagnostic useWarnings; d_bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required d_bool ctfe_cov; // generate coverage data for ctfe @@ -179,6 +208,9 @@ struct Param // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html // Implementation: https://github.com/dlang/dmd/pull/9817 + FeatureState safer; // safer by default (more @safe checks in unattributed code) + // https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md + FeatureState noSharedAccess; // read/write access to shared memory objects d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues d_bool inclusiveInContracts; // 'in' contracts of overridden methods must be a superset of parent contract @@ -200,9 +232,12 @@ struct Param CHECKACTION checkAction; // action to take when bounds, asserts or switch defaults are violated + CLIIdentifierTable dIdentifierTable; + CLIIdentifierTable cIdentifierTable; + DString argv0; // program name Array<const char *> modFileAliasStrings; // array of char*'s of -I module filename alias strings - Array<const char *> imppath; // array of char*'s of where to look for import modules + Array<ImportPathInfo> imppath; // array of import path information of where to look for import modules Array<const char *> fileImppath; // array of char*'s of where to look for file import modules DString objdir; // .obj/.lib file output directory DString objname; // .obj file output name @@ -217,8 +252,7 @@ struct Param Output mixinOut; // write expanded mixins for debugging Output moduleDeps; // Generate `.deps` module dependencies - unsigned debuglevel; // debug level - unsigned versionlevel; // version level + d_bool debugEnabled; // -debug flag is passed d_bool run; // run resulting executable Strings runargs; // arguments for executable @@ -236,6 +270,10 @@ struct Param DString resfile; DString exefile; DString mapfile; + bool fullyQualifiedObjectFiles; + bool timeTrace; + uint32_t timeTraceGranularityUs; + const char* timeTraceFile; }; struct structalign_t @@ -273,7 +311,11 @@ struct CompileEnv DString vendor; DString timestamp; d_bool previewIn; + d_bool transitionIn; d_bool ddocOutput; + d_bool masm; + IdentifierCharLookup cCharLookupTable; + IdentifierCharLookup dCharLookupTable; }; struct Global @@ -282,14 +324,16 @@ struct Global const DString copyright; const DString written; - Array<const char *> path; // Array of char*'s which form the import lookup path - Array<const char *> filePath; // Array of char*'s which form the file import lookup path + Array<ImportPathInfo> path; // Array of path informations which form the import lookup path + Array<const char *> importPaths; // Array of char*'s which form the import lookup path without metadata + Array<const char *> filePath; // Array of char*'s which form the file import lookup path char datetime[26]; /// string returned by ctime() CompileEnv compileEnv; Param params; unsigned errors; // number of errors reported so far + unsigned deprecations; // number of deprecations reported so far unsigned warnings; // number of warnings reported so far unsigned gag; // !=0 means gag reporting of errors & warnings unsigned gaggedErrors; // number of errors reported while gagged @@ -307,7 +351,7 @@ struct Global ErrorSink* errorSink; // where the error messages go ErrorSink* errorSinkNull; // where the error messages disappear - DArray<unsigned char> (*preprocess)(FileName, const Loc&, OutBuffer&); + DArray<unsigned char> (*preprocess)(FileName, Loc, OutBuffer&); /* Start gagging. Return the current number of gagged errors */ @@ -327,6 +371,13 @@ struct Global void _init(); /** + * Indicate to stateful error sinks that no more errors can be produced. + * This is to support error sinks that collect information to produce a + * single (say) report. + */ + void plugErrorSinks(); + + /** Returns: the version as the number that would be returned for __VERSION__ */ unsigned versionNumber(); @@ -364,43 +415,46 @@ typedef unsigned long long uinteger_t; #endif // file location +struct SourceLoc +{ + DString filename; + uint32_t line; + uint32_t column; + uint32_t fileOffset; + DString fileContent; +}; + struct Loc { private: - unsigned _linnum; - unsigned _charnum; - unsigned fileIndex; + + unsigned int index; + +#if MARS && defined(__linux__) && defined(__i386__) + unsigned int dummy; +#endif + public: static void set(bool showColumns, MessageStyle messageStyle); + static Loc singleFilename(const char* const filename); static bool showColumns; static MessageStyle messageStyle; Loc() { - _linnum = 0; - _charnum = 0; - fileIndex = 0; - } - - Loc(const char *filename, unsigned linnum, unsigned charnum) - { - this->linnum(linnum); - this->charnum(charnum); - this->filename(filename); + index = 0; } uint32_t charnum() const; - uint32_t charnum(uint32_t num); uint32_t linnum() const; - uint32_t linnum(uint32_t num); const char *filename() const; - void filename(const char *name); + SourceLoc toSourceLoc() const; const char *toChars( bool showColumns = Loc::showColumns, MessageStyle messageStyle = Loc::messageStyle) const; - bool equals(const Loc& loc) const; + bool equals(Loc loc) const; }; enum class LINK : uint8_t diff --git a/gcc/d/dmd/gluelayer.d b/gcc/d/dmd/gluelayer.d index a3a3bd0..ba7c1e9 100644 --- a/gcc/d/dmd/gluelayer.d +++ b/gcc/d/dmd/gluelayer.d @@ -3,12 +3,12 @@ * * This 'glues' either the DMC or GCC back-end to the front-end. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/gluelayer.d, _gluelayer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/gluelayer.d, _gluelayer.d) * Documentation: https://dlang.org/phobos/dmd_gluelayer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/gluelayer.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/gluelayer.d */ module dmd.gluelayer; @@ -25,7 +25,7 @@ version (NoBackend) struct Symbol; struct code; struct block; - struct Blockx; + struct BlockState; struct elem; struct TYPE; alias type = TYPE; @@ -51,9 +51,9 @@ else version (IN_GCC) } else { - public import dmd.backend.cc : block, Blockx, Symbol; + public import dmd.backend.cc : block, BlockState, Symbol; public import dmd.backend.type : type; public import dmd.backend.el : elem; - public import dmd.backend.code_x86 : code; + public import dmd.backend.x86.code_x86 : code; public import dmd.objc_glue : ObjcGlue; } diff --git a/gcc/d/dmd/hdrgen.d b/gcc/d/dmd/hdrgen.d index 41da11d..61ff273 100644 --- a/gcc/d/dmd/hdrgen.d +++ b/gcc/d/dmd/hdrgen.d @@ -3,12 +3,12 @@ * * Also used to convert AST nodes to D code in general, e.g. for error messages or `printf` debugging. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/hdrgen.d, _hdrgen.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/hdrgen.d, _hdrgen.d) * Documentation: https://dlang.org/phobos/dmd_hdrgen.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/hdrgen.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/hdrgen.d */ module dmd.hdrgen; @@ -59,9 +59,12 @@ struct HdrGenState bool ddoc; /// true if generating Ddoc file bool fullDump; /// true if generating a full AST dump file bool importcHdr; /// true if generating a .di file from an ImportC file + bool inCAlias; /// Set to prevent ImportC translating typedefs as `alias X = X` bool doFuncBodies; /// include function bodies in output bool vcg_ast; /// write out codegen-ast bool skipConstraints; // skip constraints when doing templates + bool showOneMember = true; + bool errorMsg; /// true if formatting for inside an error message bool fullQual; /// fully qualify types when printing int tpltMember; @@ -95,6 +98,87 @@ void genhdrfile(Module m, bool doFuncBodies, ref OutBuffer buf) toCBuffer(m, buf, hgs); } +/** + * Convert `o` to a string for error messages. + * Params: + * e = object to convert + * Returns: string representation of `e` + */ +const(char)* toErrMsg(const RootObject o) +{ + if (auto e = o.isExpression()) + return toErrMsg(e); + if (auto d = o.isDsymbol()) + return toErrMsg(d); + if (auto t = o.isType()) + return t.toChars(); + if (auto id = o.isIdentifier()) + return id.toChars(); + assert(0); +} + +/// ditto +const(char)* toErrMsg(const Expression e) +{ + HdrGenState hgs; + hgs.errorMsg = true; + OutBuffer buf; + toCBuffer(e, buf, hgs); + truncateForError(buf, 60); + + return buf.extractChars(); +} + +/// ditto +const(char)* toErrMsg(const Dsymbol d) +{ + if (d.isFuncDeclaration() || d.isTemplateInstance()) + { + if (d.ident && d.ident.toString.startsWith("__") && !d.isCtorDeclaration()) + { + HdrGenState hgs; + hgs.errorMsg = true; + OutBuffer buf; + toCBuffer(cast() d, buf, hgs); + truncateForError(buf, 80); + return buf.extractChars(); + } + } + + return d.toChars(); +} + +/** + * Make the content of `buf` fit inline for an error message. + * Params: + * buf = buffer with text to modify + * maxLength = truncate text when it exceeds this length + */ +private void truncateForError(ref OutBuffer buf, size_t maxLength) +{ + // Remove newlines, escape backticks ` by doubling them + for (size_t i = 0; i < buf.length; i++) + { + if (buf[i] == '\r') + buf.remove(i, 1); + if (buf[i] == '\n') + buf.peekSlice[i] = ' '; + if (buf[i] == '`') + i = buf.insert(i, "`"); + } + + // Strip trailing whitespace + while (buf.length && buf[$-1] == ' ') + buf.setsize(buf.length - 1); + + // Truncate + if (buf.length > maxLength) + { + buf.setsize(maxLength - 3); + buf.writestring("..."); + } +} + /*************************************** * Turn a Statement into a string suitable for printf. * Leaks memory. @@ -139,6 +223,42 @@ public const(char)* toChars(const Type t) return buf.extractChars(); } +public const(char)* toChars(const Dsymbol d) +{ + if (auto td = d.isTemplateDeclaration()) + { + HdrGenState hgs; + OutBuffer buf; + toCharsMaybeConstraints(td, buf, hgs); + return buf.extractChars(); + } + + if (auto ti = d.isTemplateInstance()) + { + OutBuffer buf; + toCBufferInstance(ti, buf); + return buf.extractChars(); + } + + if (auto tm = d.isTemplateMixin()) + { + OutBuffer buf; + toCBufferInstance(tm, buf); + return buf.extractChars(); + } + + if (auto tid = d.isTypeInfoDeclaration()) + { + OutBuffer buf; + buf.writestring("typeid("); + buf.writestring(tid.tinfo.toChars()); + buf.writeByte(')'); + return buf.extractChars(); + } + + return d.ident ? d.ident.toHChars2() : "__anonymous"; +} + public const(char)[] toString(const Initializer i) { OutBuffer buf; @@ -304,13 +424,12 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h buf.writenl(); } - void visitWhile(WhileStatement s) + void printConditionAssignment(Parameter p, Expression condition) { - buf.writestring("while ("); - if (auto p = s.param) + if (p) { // Print condition assignment - StorageClass stc = p.storageClass; + STC stc = p.storageClass; if (!p.type && !stc) stc = STC.auto_; if (stcToBuffer(buf, stc)) @@ -321,7 +440,13 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h buf.writestring(p.ident.toString()); buf.writestring(" = "); } - s.condition.expressionToBuffer(buf, hgs); + condition.expressionToBuffer(buf, hgs); + } + + void visitWhile(WhileStatement s) + { + buf.writestring("while ("); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s._body) @@ -412,10 +537,10 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h { buf.writestring(Token.toString(s.op)); buf.writestring(" ("); - if (s.prm.type) - typeToBuffer(s.prm.type, s.prm.ident, buf, hgs); + if (s.param.type) + typeToBuffer(s.param.type, s.param.ident, buf, hgs); else - buf.writestring(s.prm.ident.toString()); + buf.writestring(s.param.ident.toString()); buf.writestring("; "); s.lwr.expressionToBuffer(buf, hgs); buf.writestring(" .. "); @@ -459,20 +584,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitIf(IfStatement s) { buf.writestring("if ("); - if (Parameter p = s.prm) - { - StorageClass stc = p.storageClass; - if (!p.type && !stc) - stc = STC.auto_; - if (stcToBuffer(buf, stc)) - buf.writeByte(' '); - if (p.type) - typeToBuffer(p.type, p.ident, buf, hgs); - else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); - } - s.condition.expressionToBuffer(buf, hgs); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s.ifbody.isScopeStatement()) @@ -571,21 +683,7 @@ private void statementToBuffer(Statement s, ref OutBuffer buf, ref HdrGenState h void visitSwitch(SwitchStatement s) { buf.writestring(s.isFinal ? "final switch (" : "switch ("); - if (auto p = s.param) - { - // Print condition assignment - StorageClass stc = p.storageClass; - if (!p.type && !stc) - stc = STC.auto_; - if (stcToBuffer(buf, stc)) - buf.writeByte(' '); - if (p.type) - typeToBuffer(p.type, p.ident, buf, hgs); - else - buf.writestring(p.ident.toString()); - buf.writestring(" = "); - } - s.condition.expressionToBuffer(buf, hgs); + printConditionAssignment(s.param, s.condition); buf.writeByte(')'); buf.writenl(); if (s._body) @@ -901,10 +999,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitDebugSymbol(DebugSymbol s) { buf.writestring("debug = "); - if (s.ident) - buf.writestring(s.ident.toString()); - else - buf.print(s.level); + buf.writestring(s.ident.toString()); buf.writeByte(';'); buf.writenl(); } @@ -912,10 +1007,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitVersionSymbol(VersionSymbol s) { buf.writestring("version = "); - if (s.ident) - buf.writestring(s.ident.toString()); - else - buf.print(s.level); + buf.writestring(s.ident.toString()); buf.writeByte(';'); buf.writenl(); } @@ -1189,14 +1281,14 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void foreachRangeWithoutBody(ForeachRangeStatement s) { - /* s.op ( prm ; lwr .. upr ) + /* s.op ( param ; lwr .. upr ) */ buf.writestring(Token.toString(s.op)); buf.writestring(" ("); - if (s.prm.type) - typeToBuffer(s.prm.type, s.prm.ident, buf, hgs); + if (s.param.type) + typeToBuffer(s.param.type, s.param.ident, buf, hgs); else - buf.writestring(s.prm.ident.toString()); + buf.writestring(s.param.ident.toString()); buf.writestring("; "); s.lwr.expressionToBuffer(buf, hgs); buf.writestring(" .. "); @@ -1649,7 +1741,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) { if (d.storage_class & STC.local) return; - if (d.adFlags & d.hidden) + if (d.hidden) return; buf.writestring("alias "); if (d.aliassym) @@ -1686,7 +1778,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writestring(" = "); if (stcToBuffer(buf, d.storage_class)) buf.writeByte(' '); + hgs.inCAlias = hgs.importcHdr; typeToBuffer(d.type, null, buf, hgs); + hgs.inCAlias = false; hgs.declstring = false; } buf.writeByte(';'); @@ -1717,12 +1811,15 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) void visitFuncDeclaration(FuncDeclaration f) { //printf("FuncDeclaration::toCBuffer() '%s'\n", f.toChars()); - if (stcToBuffer(buf, f.storage_class)) + + // https://issues.dlang.org/show_bug.cgi?id=24891 + // return/scope storage classes are printed as part of function type + if (stcToBuffer(buf, f.storage_class & ~(STC.scope_ | STC.return_ | STC.returnScope))) buf.writeByte(' '); + typeToBuffer(f.type, f.ident, buf, hgs); auto tf = f.type.isTypeFunction(); - typeToBuffer(tf, f.ident, buf, hgs); - if (hgs.hdrgen) + if (hgs.hdrgen && tf) { // if the return type is missing (e.g. ref functions or auto) // https://issues.dlang.org/show_bug.cgi?id=20090 @@ -1760,7 +1857,7 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writestring("__error"); return; } - if (f.tok != TOK.reserved) + if (f.tok != TOK.reserved && !hgs.errorMsg) { buf.writestring(f.kind()); buf.writeByte(' '); @@ -1777,8 +1874,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) buf.writeByte(' '); buf.writestring(str); } - tf.attributesApply(&printAttribute); + if (!hgs.errorMsg) + tf.attributesApply(&printAttribute); CompoundStatement cs = f.fbody.isCompoundStatement(); Statement s1; @@ -1857,9 +1955,9 @@ void toCBuffer(Dsymbol s, ref OutBuffer buf, ref HdrGenState hgs) if (stcToBuffer(buf, d.storage_class)) buf.writeByte(' '); buf.writestring("invariant"); - if(auto es = d.fbody.isExpStatement()) + auto es = d.fbody.isExpStatement(); + if (es && es.exp && es.exp.op == EXP.assert_) { - assert(es.exp && es.exp.op == EXP.assert_); buf.writestring(" ("); (cast(AssertExp)es.exp).e1.expressionToBuffer(buf, hgs); buf.writestring(");"); @@ -1974,7 +2072,7 @@ void toCharsMaybeConstraints(const TemplateDeclaration td, ref OutBuffer buf, re } buf.writeByte(')'); - if (td.onemember) + if (hgs.showOneMember && td.onemember) { if (const fd = td.onemember.isFuncDeclaration()) { @@ -2255,9 +2353,20 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writestring(e.ident.toString()); } - void visitDsymbol(DsymbolExp e) + void visitDsymbol(Dsymbol s) + { + // For -vcg-ast, print internal names such as __invariant, __ctor etc. + // This condition is a bit kludge, and can be cleaned up if the + // mutual dependency `AST.toChars <> hdrgen.d` gets refactored + if (hgs.vcg_ast && s.ident && !s.isTemplateInstance() && !s.isTemplateDeclaration()) + buf.writestring(s.ident.toChars()); + else + buf.writestring(s.toChars()); + } + + void visitDsymbolExp(DsymbolExp e) { - buf.writestring(e.s.toChars()); + visitDsymbol(e.s); } void visitThis(ThisExp e) @@ -2362,12 +2471,12 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // to themselves, need to avoid infinite recursion: // struct S { this(int){ this.s = &this; } S* s; } // const foo = new S(0); - if (e.stageflags & stageToCBuffer) + if (e.stageflags & StructLiteralExp.StageFlags.toCBuffer) buf.writestring("<recursion>"); else { const old = e.stageflags; - e.stageflags |= stageToCBuffer; + e.stageflags |= StructLiteralExp.StageFlags.toCBuffer; argsToBuffer(e.elements, buf, hgs); e.stageflags = old; } @@ -2422,6 +2531,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte('.'); } buf.writestring("new "); + if (e.placement) + { + buf.writeByte('('); + expToBuffer(e.placement, PREC.assign, buf, hgs); + buf.writeByte(')'); + buf.writeByte(' '); + } typeToBuffer(e.newtype, null, buf, hgs); if (e.arguments && e.arguments.length) { @@ -2439,6 +2555,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte('.'); } buf.writestring("new"); + if (e.placement) + { + buf.writeByte(' '); + buf.writeByte('('); + expToBuffer(e.placement, PREC.assign, buf, hgs); + buf.writeByte(')'); + } buf.writestring(" class "); if (e.arguments && e.arguments.length) { @@ -2453,7 +2576,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitSymOff(SymOffExp e) { if (e.offset) - buf.printf("(& %s%+lld)", e.var.toChars(), e.offset); + buf.printf("(& %s + %llu)", e.var.toChars(), e.offset); else if (e.var.isTypeInfoDeclaration()) buf.writestring(e.var.toChars()); else @@ -2462,7 +2585,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitVar(VarExp e) { - buf.writestring(e.var.toChars()); + visitDsymbol(e.var); } void visitOver(OverExp e) @@ -2608,9 +2731,9 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt // the `sideeffect.copyToTemp` function. auto ve = e.e2.isVarExp(); - // not a CommaExp introduced for temporaries, go on - // the old path - if (!ve || !(ve.var.storage_class & STC.temp)) + // Not a CommaExp introduced for temporaries, or -vcg-ast, + // print the full comma + if (!ve || !(ve.var.storage_class & STC.temp) || hgs.vcg_ast) { visitBin(cast(BinExp)e); return; @@ -2681,10 +2804,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt void visitDotId(DotIdExp e) { expToBuffer(e.e1, PREC.primary, buf, hgs); - if (e.arrow) - buf.writestring("->"); - else - buf.writeByte('.'); + buf.writeByte('.'); buf.writestring(e.ident.toString()); } @@ -2699,7 +2819,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt { expToBuffer(e.e1, PREC.primary, buf, hgs); buf.writeByte('.'); - buf.writestring(e.var.toChars()); + visitDsymbol(e.var); } void visitDotTemplateInstance(DotTemplateInstanceExp e) @@ -2890,14 +3010,21 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writestring(e.value.toChars()); } + if (e.rvalue) + buf.writestring("__rvalue("); + + scope (exit) + if (e.rvalue) + buf.writeByte(')'); + switch (e.op) { default: if (auto be = e.isBinExp()) return visitBin(be); - else if (auto ue = e.isUnaExp()) + if (auto ue = e.isUnaExp()) return visitUna(ue); - else if (auto de = e.isDefaultInitExp()) + if (auto de = e.isDefaultInitExp()) return visitDefaultInit(e.isDefaultInitExp()); return visit(e); @@ -2907,7 +3034,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.float64: return visitReal(e.isRealExp()); case EXP.complex80: return visitComplex(e.isComplexExp()); case EXP.identifier: return visitIdentifier(e.isIdentifierExp()); - case EXP.dSymbol: return visitDsymbol(e.isDsymbolExp()); + case EXP.dSymbol: return visitDsymbolExp(e.isDsymbolExp()); case EXP.this_: return visitThis(e.isThisExp()); case EXP.super_: return visitSuper(e.isSuperExp()); case EXP.null_: return visitNull(e.isNullExp()); @@ -2931,7 +3058,7 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt case EXP.typeid_: return visitTypeid(e.isTypeidExp()); case EXP.traits: return visitTraits(e.isTraitsExp()); case EXP.halt: return visitHalt(e.isHaltExp()); - case EXP.is_: return visitIs(e.isExp()); + case EXP.is_: return visitIs(e.isIsExp()); case EXP.comma: return visitComma(e.isCommaExp()); case EXP.mixin_: return visitMixin(e.isMixinExp()); case EXP.import_: return visitImport(e.isImportExp()); @@ -3018,7 +3145,7 @@ void floatToBuffer(Type type, const real_t value, ref OutBuffer buf, const bool default: break; } - if (t.isimaginary()) + if (t.isImaginary()) buf.writeByte('i'); } } @@ -3126,20 +3253,14 @@ public: override void visit(DebugCondition c) { buf.writestring("debug ("); - if (c.ident) - buf.writestring(c.ident.toString()); - else - buf.print(c.level); + buf.writestring(c.ident.toString()); buf.writeByte(')'); } override void visit(VersionCondition c) { buf.writestring("version ("); - if (c.ident) - buf.writestring(c.ident.toString()); - else - buf.print(c.level); + buf.writestring(c.ident.toString()); buf.writeByte(')'); } @@ -3176,7 +3297,7 @@ void toCBuffer(const Initializer iz, ref OutBuffer buf, ref HdrGenState hgs) initializerToBuffer(cast() iz, buf, hgs); } -bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe +bool stcToBuffer(ref OutBuffer buf, STC stc) @safe { //printf("stc: %llx\n", stc); bool result = false; @@ -3192,6 +3313,12 @@ bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe stc &= ~(STC.return_ | STC.returninferred); } + // ensure `auto ref` keywords are (almost) adjacent + if (stc & STC.auto_) + { + buf.writestring("auto "); + stc &= ~STC.auto_; + } /* Put scope ref return into a standard order */ string rrs; @@ -3201,12 +3328,12 @@ bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe { case ScopeRef.None: case ScopeRef.Scope: - case ScopeRef.Ref: case ScopeRef.Return: break; case ScopeRef.ReturnScope: rrs = "return scope"; goto L1; case ScopeRef.ReturnRef: rrs = isout ? "return out" : "return ref"; goto L1; + case ScopeRef.Ref: rrs = isout ? "out" : "ref"; goto L1; case ScopeRef.RefScope: rrs = isout ? "out scope" : "ref scope"; goto L1; case ScopeRef.ReturnRef_Scope: rrs = isout ? "return out scope" : "return ref scope"; goto L1; case ScopeRef.Ref_ReturnScope: rrs = isout ? "out return scope" : "ref return scope"; goto L1; @@ -3236,11 +3363,11 @@ bool stcToBuffer(ref OutBuffer buf, StorageClass stc) @safe * and return a string representation of it. * stc is reduced by the one picked. */ -string stcToString(ref StorageClass stc) @safe +string stcToString(ref STC stc) @safe { static struct SCstring { - StorageClass stc; + STC stc; string id; } @@ -3283,7 +3410,7 @@ string stcToString(ref StorageClass stc) @safe ]; foreach (ref entry; table) { - const StorageClass tbl = entry.stc; + const STC tbl = entry.stc; assert(tbl & STC.visibleStorageClasses); if (stc & tbl) { @@ -3516,7 +3643,7 @@ private void parameterToBuffer(Parameter p, ref OutBuffer buf, ref HdrGenState h if (p.storageClass & STC.auto_) buf.writestring("auto "); - StorageClass stc = p.storageClass; + STC stc = p.storageClass; if (p.storageClass & STC.in_) { buf.writestring("in "); @@ -3792,7 +3919,8 @@ private void tiargsToBuffer(TemplateInstance ti, ref OutBuffer buf, ref HdrGenSt } else if (Expression e = isExpression(oarg)) { - if (e.op == EXP.int64 || e.op == EXP.float64 || e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_) + if (!(e.type && e.type.isTypeEnum()) && e.op == EXP.int64 || e.op == EXP.float64 || + e.op == EXP.null_ || e.op == EXP.string_ || e.op == EXP.this_) { toCBuffer(e, buf, hgs); return; @@ -3926,15 +4054,15 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te /* Use 'storage class' (prefix) style for attributes */ - if (t.mod) + if (t.mod && !(hgs.ddoc || hgs.hdrgen)) { MODtoBuffer(buf, t.mod); buf.writeByte(' '); } - void ignoreReturn(string str) + void dg(string str) { - if (str != "return") + if (str != "return" && str != "scope") { // don't write 'ref' for ctors if ((ident == Id.ctor) && str == "ref") @@ -3943,7 +4071,7 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te buf.writeByte(' '); } } - t.attributesApply(&ignoreReturn); + t.attributesApply(&dg); if (t.linkage > LINK.d && hgs.ddoc != 1 && !hgs.hdrgen) { @@ -3976,7 +4104,21 @@ private void visitFuncIdentWithPrefix(TypeFunction t, const Identifier ident, Te buf.writeByte(')'); } parametersToBuffer(t.parameterList, buf, hgs); - if (t.isreturn) + // postfix this attributes are more readable + if (t.mod && (hgs.ddoc || hgs.hdrgen)) + { + buf.writeByte(' '); + MODtoBuffer(buf, t.mod); + } + if (t.isReturnScope && !t.isReturnInferred) + { + buf.writestring(" return scope"); + } + else if (t.isScopeQual && !t.isScopeInferred) + { + buf.writestring(" scope"); + } + if (t.isReturn && !t.isReturnScope && !t.isReturnInferred) { buf.writestring(" return"); } @@ -4166,7 +4308,7 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitFunction(TypeFunction t) { - //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isref); + //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t.isRef); visitFuncIdentWithPostfix(t, null, buf, hgs, false); } @@ -4264,13 +4406,23 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) void visitTag(TypeTag t) { - if (t.mod & MODFlags.const_) - buf.writestring("const "); if (hgs.importcHdr && t.id) { + // https://issues.dlang.org/show_bug.cgi?id=24670 + // `const` must be parenthesized because it can be a return type + if (t.mod & MODFlags.const_) + buf.writestring("const("); + + // For C to D translation, `struct S` or `enum S` simply becomes `S` buf.writestring(t.id.toString()); + + if (t.mod & MODFlags.const_) + buf.writestring(")"); return; } + // The following produces something like "const enum E : short" + if (t.mod & MODFlags.const_) + buf.writestring("const "); buf.writestring(Token.toString(t.tok)); buf.writeByte(' '); if (t.id) @@ -4314,6 +4466,11 @@ private void typeToBufferx(Type t, ref OutBuffer buf, ref HdrGenState hgs) buf.writestring("noreturn"); } + if (hgs.importcHdr && !hgs.inCAlias && t.mcache && t.mcache.typedefIdent) + { + buf.writestring(t.mcache.typedefIdent.toString()); + return; + } switch (t.ty) { @@ -4488,7 +4645,15 @@ string EXPtoString(EXP op) EXP.declaration : "declaration", EXP.interval : "interval", - EXP.loweredAssignExp : "=" + EXP.loweredAssignExp : "=", + + EXP.thrownException : "CTFE ThrownException", + EXP.cantExpression : "<cant>", + EXP.voidExpression : "cast(void)0", + EXP.showCtfeContext : "<error>", + EXP.break_ : "<break>", + EXP.continue_ : "<continue>", + EXP.goto_ : "<goto>", ]; const p = strings[op]; if (!p) diff --git a/gcc/d/dmd/hdrgen.h b/gcc/d/dmd/hdrgen.h index 14793ad..2104b98 100644 --- a/gcc/d/dmd/hdrgen.h +++ b/gcc/d/dmd/hdrgen.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Dave Fladebo * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/iasm.d b/gcc/d/dmd/iasm.d index 1399ac2..689ef0f 100644 --- a/gcc/d/dmd/iasm.d +++ b/gcc/d/dmd/iasm.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/iasm.html, Inline Assembler) * - * Copyright (C) 2018-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d, _iasm.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasm.d, _iasm.d) * Documentation: https://dlang.org/phobos/dmd_iasm.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasm.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasm.d */ module dmd.iasm; @@ -39,7 +39,7 @@ else /************************ AsmStatement ***************************************/ -Statement asmSemantic(AsmStatement s, Scope *sc) +Statement asmSemantic(AsmStatement s, Scope* sc) { //printf("AsmStatement.semantic()\n"); @@ -50,7 +50,7 @@ Statement asmSemantic(AsmStatement s, Scope *sc) return null; // Assume assembler code takes care of setting the return value - sc.func.hasReturnExp |= 8; + sc.func.hasInlineAsm = true; version (NoBackend) { @@ -89,7 +89,7 @@ Statement asmSemantic(AsmStatement s, Scope *sc) /************************ CAsmDeclaration ************************************/ -void asmSemantic(CAsmDeclaration ad, Scope *sc) +void asmSemantic(CAsmDeclaration ad, Scope* sc) { version (NoBackend) { diff --git a/gcc/d/dmd/iasmgcc.d b/gcc/d/dmd/iasmgcc.d index 4b1b2e7..3d6e6ab 100644 --- a/gcc/d/dmd/iasmgcc.d +++ b/gcc/d/dmd/iasmgcc.d @@ -1,12 +1,12 @@ /** * Inline assembler for the GCC D compiler. * - * Copyright (C) 2018-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2018-2025 by The D Language Foundation, All Rights Reserved * Authors: Iain Buclaw * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d, _iasmgcc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/iasmgcc.d, _iasmgcc.d) * Documentation: https://dlang.org/phobos/dmd_iasmgcc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/iasmgcc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/iasmgcc.d */ module dmd.iasmgcc; @@ -29,6 +29,118 @@ import dmd.tokens; import dmd.statement; import dmd.statementsem; +/*********************************** + * Parse and run semantic analysis on a GccAsmStatement. + * Params: + * s = gcc asm statement being parsed + * sc = the scope where the asm statement is located + * Returns: + * the completed gcc asm statement, or null if errors occurred + */ +public Statement gccAsmSemantic(GccAsmStatement s, Scope* sc) +{ + //printf("GccAsmStatement.semantic()\n"); + const bool doUnittests = global.params.parsingUnittestsRequired(); + scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); + + // Make a safe copy of the token list before parsing. + Token* toklist = null; + Token **ptoklist = &toklist; + + for (Token* token = s.tokens; token; token = token.next) + { + *ptoklist = p.allocateToken(); + memcpy(*ptoklist, token, Token.sizeof); + ptoklist = &(*ptoklist).next; + *ptoklist = null; + } + p.token = *toklist; + p.baseLoc.startLine = s.loc.linnum; + p.linnum = s.loc.linnum; + + // Parse the gcc asm statement. + const errors = global.errors; + s = p.parseGccAsm(s); + if (errors != global.errors) + return null; + s.stc = sc.stc; + + // Fold the instruction template string. + s.insn = semanticString(sc, s.insn, "asm instruction template"); + + if (s.labels && s.outputargs) + error(s.loc, "extended asm statements with labels cannot have output constraints"); + + // Analyse all input and output operands. + if (s.args) + { + foreach (i; 0 .. s.args.length) + { + Expression e = (*s.args)[i]; + e = e.expressionSemantic(sc); + // Check argument is a valid lvalue/rvalue. + if (i < s.outputargs) + e = e.modifiableLvalue(sc); + else if (e.checkValue()) + e = ErrorExp.get(); + (*s.args)[i] = e; + + e = (*s.constraints)[i]; + e = e.expressionSemantic(sc); + assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); + (*s.constraints)[i] = e; + } + } + + // Analyse all clobbers. + if (s.clobbers) + { + foreach (i; 0 .. s.clobbers.length) + { + Expression e = (*s.clobbers)[i]; + e = e.expressionSemantic(sc); + assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); + (*s.clobbers)[i] = e; + } + } + + // Analyse all goto labels. + if (s.labels) + { + foreach (i; 0 .. s.labels.length) + { + Identifier ident = (*s.labels)[i]; + GotoStatement gs = new GotoStatement(s.loc, ident); + if (!s.gotos) + s.gotos = new GotoStatements(); + s.gotos.push(gs); + gs.statementSemantic(sc); + } + } + + return s; +} + +/*********************************** + * Run semantic analysis on an CAsmDeclaration. + * Params: + * ad = asm declaration + * sc = the scope where the asm declaration is located + */ +public void gccAsmSemantic(CAsmDeclaration ad, Scope* sc) +{ + import dmd.typesem : pointerTo; + ad.code = semanticString(sc, ad.code, "asm definition"); + ad.code.type = ad.code.type.nextOf().pointerTo(); + + // Asm definition always needs emitting into the root module. + import dmd.dmodule : Module; + if (sc._module && sc._module.isRoot()) + return; + if (Module m = Module.rootModule) + m.members.push(ad); +} + private: /*********************************** @@ -143,9 +255,9 @@ Lerror: * Returns: * array of parsed clobber expressions */ -Expressions *parseExtAsmClobbers(Parser)(Parser p) +Expressions* parseExtAsmClobbers(Parser)(Parser p) { - Expressions *clobbers; + Expressions* clobbers; while (1) { @@ -194,9 +306,9 @@ Lerror: * Returns: * array of parsed goto labels */ -Identifiers *parseExtAsmGotoLabels(Parser)(Parser p) +Identifiers* parseExtAsmGotoLabels(Parser)(Parser p) { - Identifiers *labels; + Identifiers* labels; while (1) { @@ -292,117 +404,6 @@ Ldone: return s; } -/*********************************** - * Parse and run semantic analysis on a GccAsmStatement. - * Params: - * s = gcc asm statement being parsed - * sc = the scope where the asm statement is located - * Returns: - * the completed gcc asm statement, or null if errors occurred - */ -public Statement gccAsmSemantic(GccAsmStatement s, Scope *sc) -{ - //printf("GccAsmStatement.semantic()\n"); - const bool doUnittests = global.params.parsingUnittestsRequired(); - scope p = new Parser!ASTCodegen(sc._module, ";", false, global.errorSink, &global.compileEnv, doUnittests); - - // Make a safe copy of the token list before parsing. - Token *toklist = null; - Token **ptoklist = &toklist; - - for (Token *token = s.tokens; token; token = token.next) - { - *ptoklist = p.allocateToken(); - memcpy(*ptoklist, token, Token.sizeof); - ptoklist = &(*ptoklist).next; - *ptoklist = null; - } - p.token = *toklist; - p.scanloc = s.loc; - - // Parse the gcc asm statement. - const errors = global.errors; - s = p.parseGccAsm(s); - if (errors != global.errors) - return null; - s.stc = sc.stc; - - // Fold the instruction template string. - s.insn = semanticString(sc, s.insn, "asm instruction template"); - - if (s.labels && s.outputargs) - error(s.loc, "extended asm statements with labels cannot have output constraints"); - - // Analyse all input and output operands. - if (s.args) - { - foreach (i; 0 .. s.args.length) - { - Expression e = (*s.args)[i]; - e = e.expressionSemantic(sc); - // Check argument is a valid lvalue/rvalue. - if (i < s.outputargs) - e = e.modifiableLvalue(sc); - else if (e.checkValue()) - e = ErrorExp.get(); - (*s.args)[i] = e; - - e = (*s.constraints)[i]; - e = e.expressionSemantic(sc); - assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); - (*s.constraints)[i] = e; - } - } - - // Analyse all clobbers. - if (s.clobbers) - { - foreach (i; 0 .. s.clobbers.length) - { - Expression e = (*s.clobbers)[i]; - e = e.expressionSemantic(sc); - assert(e.op == EXP.string_ && (cast(StringExp) e).sz == 1); - (*s.clobbers)[i] = e; - } - } - - // Analyse all goto labels. - if (s.labels) - { - foreach (i; 0 .. s.labels.length) - { - Identifier ident = (*s.labels)[i]; - GotoStatement gs = new GotoStatement(s.loc, ident); - if (!s.gotos) - s.gotos = new GotoStatements(); - s.gotos.push(gs); - gs.statementSemantic(sc); - } - } - - return s; -} - -/*********************************** - * Run semantic analysis on an CAsmDeclaration. - * Params: - * ad = asm declaration - * sc = the scope where the asm declaration is located - */ -public void gccAsmSemantic(CAsmDeclaration ad, Scope *sc) -{ - import dmd.typesem : pointerTo; - ad.code = semanticString(sc, ad.code, "asm definition"); - ad.code.type = ad.code.type.nextOf().pointerTo(); - - // Asm definition always needs emitting into the root module. - import dmd.dmodule : Module; - if (sc._module && sc._module.isRoot()) - return; - if (Module m = Module.rootModule) - m.members.push(ad); -} - unittest { import dmd.mtype : TypeBasic; @@ -410,7 +411,7 @@ unittest if (!global.errorSink) global.errorSink = new ErrorSinkCompiler; - uint errors = global.startGagging(); + const errors = global.startGagging(); scope(exit) global.endGagging(errors); // If this check fails, then Type._init() was called before reaching here, diff --git a/gcc/d/dmd/id.d b/gcc/d/dmd/id.d index 5ad324d..e1c22f2 100644 --- a/gcc/d/dmd/id.d +++ b/gcc/d/dmd/id.d @@ -1,12 +1,12 @@ /** * Contains the `Id` struct with a list of predefined symbols the compiler knows about. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/id.d, _id.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/id.d, _id.d) * Documentation: https://dlang.org/phobos/dmd_id.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/id.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/id.d */ module dmd.id; @@ -102,6 +102,8 @@ immutable Msgtable[] msgtable = { "ctfe", "__ctfe" }, { "offset" }, { "offsetof" }, + { "bitoffsetof" }, + { "bitwidth" }, { "ModuleInfo" }, { "ClassInfo" }, { "classinfo" }, @@ -164,6 +166,7 @@ immutable Msgtable[] msgtable = { "xopCmp", "__xopCmp" }, { "xtoHash", "__xtoHash" }, { "__tmpfordtor" }, + { "Entry" }, { "LINE", "__LINE__" }, { "FILE", "__FILE__" }, @@ -221,60 +224,15 @@ immutable Msgtable[] msgtable = { "__LOCAL_SIZE" }, // For operator overloads - { "uadd", "opPos" }, - { "neg", "opNeg" }, - { "com", "opCom" }, - { "add", "opAdd" }, - { "add_r", "opAdd_r" }, - { "sub", "opSub" }, - { "sub_r", "opSub_r" }, - { "mul", "opMul" }, - { "mul_r", "opMul_r" }, - { "div", "opDiv" }, - { "div_r", "opDiv_r" }, - { "mod", "opMod" }, - { "mod_r", "opMod_r" }, - { "eq", "opEquals" }, - { "cmp", "opCmp" }, - { "iand", "opAnd" }, - { "iand_r", "opAnd_r" }, - { "ior", "opOr" }, - { "ior_r", "opOr_r" }, - { "ixor", "opXor" }, - { "ixor_r", "opXor_r" }, - { "shl", "opShl" }, - { "shl_r", "opShl_r" }, - { "shr", "opShr" }, - { "shr_r", "opShr_r" }, - { "ushr", "opUShr" }, - { "ushr_r", "opUShr_r" }, - { "cat", "opCat" }, - { "cat_r", "opCat_r" }, - { "assign", "opAssign" }, - { "addass", "opAddAssign" }, - { "subass", "opSubAssign" }, - { "mulass", "opMulAssign" }, - { "divass", "opDivAssign" }, - { "modass", "opModAssign" }, - { "andass", "opAndAssign" }, - { "orass", "opOrAssign" }, - { "xorass", "opXorAssign" }, - { "shlass", "opShlAssign" }, - { "shrass", "opShrAssign" }, - { "ushrass", "opUShrAssign" }, - { "catass", "opCatAssign" }, - { "postinc", "opPostInc" }, - { "postdec", "opPostDec" }, - { "index", "opIndex" }, - { "indexass", "opIndexAssign" }, - { "slice", "opSlice" }, - { "sliceass", "opSliceAssign" }, - { "call", "opCall" }, - { "_cast", "opCast" }, - { "opIn" }, - { "opIn_r" }, - { "opStar" }, - { "opDot" }, + { "opEquals" }, + { "opCmp" }, + { "opAssign" }, + { "opIndex" }, + { "opIndexAssign" }, + { "opSlice" }, + { "opSliceAssign" }, + { "opCall" }, + { "opCast" }, { "opDispatch" }, { "opDollar" }, { "opUnary" }, @@ -285,9 +243,6 @@ immutable Msgtable[] msgtable = { "opOpAssign" }, { "opIndexOpAssign" }, { "opSliceOpAssign" }, - { "pow", "opPow" }, - { "pow_r", "opPow_r" }, - { "powass", "opPowAssign" }, { "classNew", "new" }, { "classDelete", "delete" }, @@ -387,6 +342,7 @@ immutable Msgtable[] msgtable = // Builtin functions { "std" }, { "core" }, + { "internal" }, { "config" }, { "c_complex_float" }, { "c_complex_double" }, @@ -448,11 +404,14 @@ immutable Msgtable[] msgtable = { "outp"}, { "outpl"}, { "outpw"}, + { "builtinsModuleName", "builtins" }, + { "ctfeWrite", "__ctfeWrite" }, // Traits { "isAbstractClass" }, { "isArithmetic" }, { "isAssociativeArray" }, + { "isBitfield" }, { "isFinalClass" }, { "isTemplate" }, { "isPOD" }, @@ -476,9 +435,12 @@ immutable Msgtable[] msgtable = { "isRef" }, { "isOut" }, { "isLazy" }, + { "isCOMClass" }, { "hasMember" }, { "identifier" }, { "fullyQualifiedName" }, + { "getBitfieldOffset" }, + { "getBitfieldWidth" }, { "getProtection" }, { "getVisibility" }, { "parent" }, @@ -510,6 +472,7 @@ immutable Msgtable[] msgtable = { "getLocation" }, { "hasPostblit" }, { "hasCopyConstructor" }, + { "hasMoveConstructor" }, { "isCopyable" }, { "toType" }, { "parameters" }, @@ -529,6 +492,9 @@ immutable Msgtable[] msgtable = { "udaMustUse", "mustuse" }, { "udaStandalone", "standalone" }, + // Editions + { "__edition_latest_do_not_use", }, + // C names, for undefined identifier error messages { "NULL" }, { "TRUE" }, @@ -553,7 +519,7 @@ immutable Msgtable[] msgtable = { "_align", "align" }, { "aligned" }, { "__pragma", "pragma" }, - { "builtins", "__builtins" }, + { "importc_builtins", "__importc_builtins" }, { "builtin_va_list", "__builtin_va_list" }, { "builtin_va_arg", "__builtin_va_arg" }, { "va_list_tag", "__va_list_tag" }, @@ -566,6 +532,7 @@ immutable Msgtable[] msgtable = { "define" }, { "undef" }, { "ident" }, + { "packed" }, ]; diff --git a/gcc/d/dmd/id.h b/gcc/d/dmd/id.h index ddc8da2..cab42bf 100644 --- a/gcc/d/dmd/id.h +++ b/gcc/d/dmd/id.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2017-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2017-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/identifier.d b/gcc/d/dmd/identifier.d index 8ace310..c213597 100644 --- a/gcc/d/dmd/identifier.d +++ b/gcc/d/dmd/identifier.d @@ -1,12 +1,12 @@ /** * Defines an identifier, which is the name of a `Dsymbol`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d, _identifier.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/identifier.d, _identifier.d) * Documentation: https://dlang.org/phobos/dmd_identifier.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/identifier.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/identifier.d */ module dmd.identifier; @@ -108,7 +108,8 @@ nothrow: const(char)* p = null; if (this == Id.ctor) p = "this"; - else if (this == Id.dtor) + else if (this == Id.dtor || this == Id.__xdtor || this == Id.__fieldDtor || + this == Id.__aggrDtor || this == Id.cppdtor || this == Id.ticppdtor) p = "~this"; else if (this == Id.unitTest) p = "unittest"; @@ -120,6 +121,8 @@ nothrow: p = "result"; else if (this == Id.returnLabel) p = "return"; + else if (this == Id.postblit) + p = "this(this)"; else { p = toChars(); @@ -211,19 +214,23 @@ nothrow: * Params: * prefix = first part of the identifier name. * loc = source location to use in the identifier name. + * parent = (optional) extra part to be used in uniqueness check, + * if (prefix1, loc1) == (prefix2, loc2), but + * parent1 != parent2, no new name will be generated. * Returns: * Identifier (inside Identifier.idPool) with deterministic name based * on the source location. */ - extern (D) static Identifier generateIdWithLoc(string prefix, const ref Loc loc) + extern (D) static Identifier generateIdWithLoc(string prefix, Loc loc, string parent = "") { // generate `<prefix>_L<line>_C<col>` + auto sl = SourceLoc(loc); OutBuffer idBuf; idBuf.writestring(prefix); idBuf.writestring("_L"); - idBuf.print(loc.linnum); + idBuf.print(sl.line); idBuf.writestring("_C"); - idBuf.print(loc.charnum); + idBuf.print(sl.column); /** * Make sure the identifiers are unique per filename, i.e., per module/mixin @@ -234,14 +241,21 @@ nothrow: * https://issues.dlang.org/show_bug.cgi?id=18880 * https://issues.dlang.org/show_bug.cgi?id=18868 * https://issues.dlang.org/show_bug.cgi?id=19058 + * + * It is a bit trickier for lambdas/dgliterals: we want them to be unique per + * module/mixin + function/template instantiation context. So we use extra parent + * argument for that when dealing with lambdas. We could have added it to prefix + * directly, but that would unnecessary lengthen symbols names. See issue: + * https://issues.dlang.org/show_bug.cgi?id=23722 */ - static struct Key { Loc loc; string prefix; } + static struct Key { string locKey; string prefix; string parent; } __gshared uint[Key] counters; + string locKey = cast(string) (sl.filename ~ idBuf[]); static if (__traits(compiles, counters.update(Key.init, () => 0u, (ref uint a) => 0u))) { // 2.082+ - counters.update(Key(loc, prefix), + counters.update(Key(locKey, prefix, parent), () => 1u, // insertion (ref uint counter) // update { @@ -253,7 +267,7 @@ nothrow: } else { - const key = Key(loc, prefix); + const key = Key(locKey, prefix, parent); if (auto pCounter = key in counters) { idBuf.writestring("_"); @@ -269,12 +283,12 @@ nothrow: /******************************************** * Create an identifier in the string table. */ - static Identifier idPool(const(char)* s, uint len) + static Identifier idPool(scope const(char)* s, uint len) { return idPool(s[0 .. len]); } - extern (D) static Identifier idPool(const(char)[] s, bool isAnonymous = false) + extern (D) static Identifier idPool(scope const(char)[] s, bool isAnonymous = false) { auto sv = stringtable.update(s); auto id = sv.value; @@ -292,7 +306,7 @@ nothrow: * s = string for keyword * value = TOK.xxxx for the keyword */ - extern (D) static void idPool(const(char)[] s, TOK value) + extern (D) static void idPool(scope const(char)[] s, TOK value) { auto sv = stringtable.insert(s, null); assert(sv); @@ -315,28 +329,83 @@ nothrow: /********************************** * ditto */ - extern (D) static bool isValidIdentifier(const(char)[] str) @safe + extern (D) static bool isValidIdentifier(const(char)[] str) @trusted { + import dmd.common.charactertables; + if (str.length == 0 || (str[0] >= '0' && str[0] <= '9')) // beware of isdigit() on signed chars { return false; } - size_t idx = 0; - while (idx < str.length) + // In a previous implementation this was implemented quite naively, + // by utilizing the libc. + // However we can do better, by copying the lexer approach to identifier validation. + + const(char)* p = &str[0], pEnd = str.ptr + str.length; + + // handle start characters { - dchar dc; - const s = utf_decodeChar(str, idx, dc); - if (s || - !((dc >= 0x80 && isUniAlpha(dc)) || isalnum(dc) || dc == '_')) + const c = *p; + + if (isidchar(c)) + p++; + else if (c & 0x80) { + size_t countDecoded; + dchar decoded; + + if (utf_decodeChar(p[0 .. pEnd - p], countDecoded, decoded) is null || + isAnyStart(decoded)) + p += countDecoded; + else + return false; + } + else return false; + } + + // handle continue characters + while(p !is pEnd) + { + const c = *p; + + if (isidchar(c)) // handles ASCII subset + { + p++; + continue; } + else if (c & 0x80) + { + size_t countDecoded; + dchar decoded; + + if (utf_decodeChar(p[0 .. pEnd - p], countDecoded, decoded) is null || + isAnyContinue(decoded)) + { + p += countDecoded; + continue; + } + else + return false; + } + else + return false; } + return true; } + /// + unittest + { + assert(Identifier.isValidIdentifier("tes123_t".ptr)); + assert(!Identifier.isValidIdentifier("tes123_^t".ptr)); + assert(Identifier.isValidIdentifier("te123s_ÄŸt".ptr)); + assert(!Identifier.isValidIdentifier("t^e123s_ÄŸt".ptr)); + } + extern (D) static Identifier lookup(const(char)* s, size_t len) { return lookup(s[0 .. len]); diff --git a/gcc/d/dmd/identifier.h b/gcc/d/dmd/identifier.h index 4f26801..eb06c25 100644 --- a/gcc/d/dmd/identifier.h +++ b/gcc/d/dmd/identifier.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/impcnvtab.d b/gcc/d/dmd/impcnvtab.d index b899f81..b2ab919 100644 --- a/gcc/d/dmd/impcnvtab.d +++ b/gcc/d/dmd/impcnvtab.d @@ -6,12 +6,12 @@ * Specification: $(LINK2 https://dlang.org/spec/type.html#integer-promotions, Integer Promotions), * $(LINK2 https://dlang.org/spec/type.html#usual-arithmetic-conversions, Usual Arithmetic Conversions). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d, _impcnvtab.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/impcnvtab.d, _impcnvtab.d) * Documentation: https://dlang.org/phobos/dmd_impcnvtab.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/impcnvtab.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/impcnvtab.d */ module dmd.impcnvtab; diff --git a/gcc/d/dmd/imphint.d b/gcc/d/dmd/imphint.d index ea2f13d..382a0f3 100644 --- a/gcc/d/dmd/imphint.d +++ b/gcc/d/dmd/imphint.d @@ -3,12 +3,12 @@ * * For example, prompt to `import std.stdio` when using `writeln`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/imphint.d, _imphint.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/imphint.d, _imphint.d) * Documentation: https://dlang.org/phobos/dmd_imphint.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/imphint.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/imphint.d */ module dmd.imphint; @@ -80,6 +80,8 @@ shared static this() "writeln": "std.stdio", "__va_argsave_t": "core.stdc.stdarg", "__va_list_tag": "core.stdc.stdarg", + "InterpolationHeader": "core.interpolation", + "InterpolationFooter": "core.interpolation", ]; } diff --git a/gcc/d/dmd/import.h b/gcc/d/dmd/import.h index bfbb551..14bd889 100644 --- a/gcc/d/dmd/import.h +++ b/gcc/d/dmd/import.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -44,6 +44,5 @@ public: Dsymbol *toAlias() override; bool overloadInsert(Dsymbol *s) override; - Import *isImport() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/importc.d b/gcc/d/dmd/importc.d index ece56c8..2f88a21 100644 --- a/gcc/d/dmd/importc.d +++ b/gcc/d/dmd/importc.d @@ -3,12 +3,12 @@ * * Specification: C11 * - * Copyright: Copyright (C) 2021-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2021-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/importc.d, _importc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/importc.d, _importc.d) * Documentation: https://dlang.org/phobos/dmd_importc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/importc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/importc.d */ module dmd.importc; @@ -41,7 +41,7 @@ import dmd.typesem; */ Type cAdjustParamType(Type t, Scope* sc) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return t; Type tb = t.toBasetype(); @@ -77,7 +77,7 @@ Type cAdjustParamType(Type t, Scope* sc) Expression arrayFuncConv(Expression e, Scope* sc) { //printf("arrayFuncConv() %s\n", e.toChars()); - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return e; auto t = e.type.toBasetype(); @@ -121,7 +121,6 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) if (e.isErrorExp()) return e; - Dsymbol s; auto t = e.type; if (t.isTypePointer()) { @@ -131,6 +130,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) error(e.loc, "since `%s` is a pointer, use `%s->%s` instead of `%s.%s`", pe, pe, id.toChars(), pe, id.toChars()); e = new PtrExp(e.loc, e); } + Dsymbol s; if (auto ts = t.isTypeStruct()) s = ts.sym.search(e.loc, id, 0); if (!s) @@ -154,7 +154,7 @@ Expression fieldLookup(Expression e, Scope* sc, Identifier id, bool arrow) */ Expression carraySemantic(ArrayExp ae, Scope* sc) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return null; auto e1 = ae.e1.expressionSemantic(sc); @@ -166,7 +166,7 @@ Expression carraySemantic(ArrayExp ae, Scope* sc) * So, rewrite as an IndexExp if we can. */ auto t1 = e1.type.toBasetype(); - if (t1.isTypeDArray() || t1.isTypeSArray()) + if (t1.isStaticOrDynamicArray()) { e2 = e2.expressionSemantic(sc).arrayFuncConv(sc); // C doesn't do array bounds checking, so `true` turns it off @@ -176,7 +176,7 @@ Expression carraySemantic(ArrayExp ae, Scope* sc) e1 = e1.arrayFuncConv(sc); // e1 might still be a function call e2 = e2.expressionSemantic(sc); auto t2 = e2.type.toBasetype(); - if (t2.isTypeDArray() || t2.isTypeSArray()) + if (t2.isStaticOrDynamicArray()) { return new IndexExp(ae.loc, e2, e1, true).expressionSemantic(sc); // swap operands } diff --git a/gcc/d/dmd/init.d b/gcc/d/dmd/init.d index 62bd41e..55fb6f3 100644 --- a/gcc/d/dmd/init.d +++ b/gcc/d/dmd/init.d @@ -1,12 +1,12 @@ /** * Defines initializers of variables, e.g. the array literal in `int[3] x = [0, 1, 2]`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/init.d, _init.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/init.d, _init.d) * Documentation: https://dlang.org/phobos/dmd_init.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/init.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/init.d */ module dmd.init; @@ -38,6 +38,7 @@ extern (C++) class Initializer : ASTNode { Loc loc; InitKind kind; + bool semanticDone = false; /// initializerSemantic has been run on this override DYNCAST dyncast() const { @@ -45,44 +46,44 @@ extern (C++) class Initializer : ASTNode } - extern (D) this(const ref Loc loc, InitKind kind) @safe + extern (D) this(Loc loc, InitKind kind) @safe { this.loc = loc; this.kind = kind; } - final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure + final inout(ErrorInitializer) isErrorInitializer() inout @nogc nothrow pure @trusted { // Use void* cast to skip dynamic casting call return kind == InitKind.error ? cast(inout ErrorInitializer)cast(void*)this : null; } - final inout(VoidInitializer) isVoidInitializer() inout @nogc nothrow pure + final inout(VoidInitializer) isVoidInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.void_ ? cast(inout VoidInitializer)cast(void*)this : null; } - final inout(DefaultInitializer) isDefaultInitializer() inout @nogc nothrow pure + final inout(DefaultInitializer) isDefaultInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.default_ ? cast(inout DefaultInitializer)cast(void*)this : null; } - final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure + final inout(StructInitializer) isStructInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.struct_ ? cast(inout StructInitializer)cast(void*)this : null; } - final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure + final inout(ArrayInitializer) isArrayInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.array ? cast(inout ArrayInitializer)cast(void*)this : null; } - final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure + final inout(ExpInitializer) isExpInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.exp ? cast(inout ExpInitializer)cast(void*)this : null; } - final inout(CInitializer) isCInitializer() inout @nogc nothrow pure + final inout(CInitializer) isCInitializer() inout @nogc nothrow pure @trusted { return kind == InitKind.C_ ? cast(inout CInitializer)cast(void*)this : null; } @@ -99,7 +100,7 @@ extern (C++) final class VoidInitializer : Initializer { Type type; // type that this will initialize to - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, InitKind.void_); } @@ -117,7 +118,7 @@ extern (C++) final class DefaultInitializer : Initializer { Type type; // type that this will initialize to - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, InitKind.default_); } @@ -150,7 +151,7 @@ extern (C++) final class StructInitializer : Initializer Identifiers field; // of Identifier *'s Initializers value; // parallel array of Initializer *'s - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, InitKind.struct_); } @@ -176,10 +177,9 @@ extern (C++) final class ArrayInitializer : Initializer Initializers value; // of Initializer *'s uint dim; // length of array being initialized Type type; // type that array will be used to initialize - bool sem; // true if semantic() is run bool isCarray; // C array semantics - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, InitKind.array); } @@ -215,7 +215,7 @@ extern (C++) final class ExpInitializer : Initializer bool expandTuples; Expression exp; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, InitKind.exp); this.exp = exp; @@ -256,9 +256,8 @@ extern (C++) final class CInitializer : Initializer { DesigInits initializerList; /// initializer-list Type type; /// type that array will be used to initialize - bool sem; /// true if semantic() is run - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(loc, InitKind.C_); } diff --git a/gcc/d/dmd/init.h b/gcc/d/dmd/init.h index 2485d78..a832b9e 100644 --- a/gcc/d/dmd/init.h +++ b/gcc/d/dmd/init.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -33,6 +33,7 @@ class Initializer : public ASTNode public: Loc loc; unsigned char kind; + d_bool semanticDone; DYNCAST dyncast() const override { return DYNCAST_INITIALIZER; } @@ -85,7 +86,6 @@ public: Initializers value; // of Initializer *'s unsigned dim; // length of array being initialized Type *type; // type that array will be used to initialize - d_bool sem; // true if semantic() is run d_bool isCarray; // C array semantics bool isAssociativeArray() const; @@ -119,13 +119,12 @@ class CInitializer final : public Initializer public: DesigInits initializerList; Type *type; // type that array will be used to initialize - d_bool sem; // true if semantic() is run void accept(Visitor *v) override { v->visit(this); } }; namespace dmd { - Expression *initializerToExpression(Initializer *init, Type *t = NULL, const bool isCfile = false); + Expression *initializerToExpression(Initializer *init, Type *t = nullptr, const bool isCfile = false); Initializer *initializerSemantic(Initializer *init, Scope *sc, Type *&tx, NeedInterpret needInterpret); } diff --git a/gcc/d/dmd/initsem.d b/gcc/d/dmd/initsem.d index b07699e..1ebccf77 100644 --- a/gcc/d/dmd/initsem.d +++ b/gcc/d/dmd/initsem.d @@ -1,12 +1,12 @@ /** * Semantic analysis of initializers. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/initsem.d, _initsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/initsem.d, _initsem.d) * Documentation: https://dlang.org/phobos/dmd_initsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/initsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/initsem.d */ module dmd.initsem; @@ -41,6 +41,7 @@ import dmd.location; import dmd.mtype; import dmd.opover; import dmd.optimize; +import dmd.safe : setUnsafe; import dmd.statement; import dmd.target; import dmd.tokens; @@ -106,6 +107,9 @@ Expression toAssocArrayLiteral(ArrayInitializer ai) Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedInterpret needInterpret) { //printf("initializerSemantic() tx: %p %s\n", tx, tx.toChars()); + if (init.semanticDone) + return init; + Type t = tx; static Initializer err() @@ -161,7 +165,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn // Convert initializer to Expression `ex` auto tm = fieldType.addMod(t.mod); auto iz = i.value[j].initializerSemantic(sc, tm, needInterpret); - auto ex = iz.initializerToExpression(null, (sc.flags & SCOPE.Cfile) != 0); + auto ex = iz.initializerToExpression(null, sc.inCfile); if (ex.op != EXP.error) i.value[j] = iz; return ex; @@ -203,11 +207,6 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn const(uint) amax = 0x80000000; bool errors = false; //printf("ArrayInitializer::semantic(%s), ai: %s\n", t.toChars(), toChars(i)); - if (i.sem) // if semantic() already run - { - return i; - } - i.sem = true; t = t.toBasetype(); switch (t.ty) { @@ -305,7 +304,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn } if (auto tsa = t.isTypeSArray()) { - if (sc.flags & SCOPE.Cfile && tsa.isIncomplete()) + if (sc.inCfile && tsa.isIncomplete()) { // Change to array of known length auto tn = tsa.next.toBasetype(); @@ -369,7 +368,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn // If the result will be implicitly cast, move the cast into CTFE // to avoid premature truncation of polysemous types. // eg real [] x = [1.1, 2.2]; should use real precision. - if (i.exp.implicitConvTo(t) && !(sc.flags & SCOPE.Cfile)) + if (i.exp.implicitConvTo(t) && !sc.inCfile) { i.exp = i.exp.implicitCastTo(sc, t); } @@ -377,7 +376,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn { return i; } - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { /* the interpreter turns (char*)"string" into &"string"[0] which then * it cannot interpret. Resolve that case by doing optimize() first @@ -440,7 +439,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn Type typeb = se.type.toBasetype(); TY tynto = tb.nextOf().ty; if (!se.committed && - (typeb.ty == Tarray || typeb.ty == Tsarray) && tynto.isSomeChar && + typeb.isStaticOrDynamicArray() && tynto.isSomeChar && se.numberOfCodeUnits(tynto) < tb.isTypeSArray().dim.toInteger()) { i.exp = se.castTo(sc, t); @@ -450,7 +449,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn /* Lop off terminating 0 of initializer for: * static char s[5] = "hello"; */ - if (sc.flags & SCOPE.Cfile && + if (sc.inCfile && typeb.ty == Tsarray && tynto.isSomeChar && tb.isTypeSArray().dim.toInteger() + 1 == typeb.isTypeSArray().dim.toInteger()) @@ -463,7 +462,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn * Initialize an array of unknown size with a string. * Change to static array of known size */ - if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + if (sc.inCfile && i.exp.isStringExp() && tb.isTypeSArray() && tb.isTypeSArray().isIncomplete()) { StringExp se = i.exp.isStringExp(); @@ -490,7 +489,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else i.exp = e.optimize(WANTvalue); } - else if (search_function(sd, Id.call)) + else if (search_function(sd, Id.opCall)) { /* https://issues.dlang.org/show_bug.cgi?id=1547 * @@ -500,7 +499,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn * i.exp = typeof(sd).opCall(arguments) */ - Expression e = typeDotIdExp(i.loc, sd.type, Id.call); + Expression e = typeDotIdExp(i.loc, sd.type, Id.opCall); e = new CallExp(i.loc, e, i.exp); e = e.expressionSemantic(sc); e = resolveProperties(sc, e); @@ -549,7 +548,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn { i.exp = i.exp.implicitCastTo(sc, t); } - else if (sc.flags & SCOPE.Cfile && i.exp.isStringExp() && + else if (sc.inCfile && i.exp.isStringExp() && tta && (tta.next.ty == Tint8 || tta.next.ty == Tuns8) && ti.ty == Tsarray && ti.nextOf().ty == Tchar) { @@ -586,7 +585,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn const errors = global.startGagging(); i.exp = i.exp.implicitCastTo(sc, t); if (global.endGagging(errors)) - error(currExp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toChars(), et.toChars(), t.toChars()); + error(currExp.loc, "cannot implicitly convert expression `%s` of type `%s` to `%s`", currExp.toErrMsg(), et.toChars(), t.toChars()); } } L1: @@ -692,51 +691,49 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn auto di = ci.initializerList[index]; if (di.designatorList && fieldi != 0) break; // back to top level - else + + VarDeclaration field; + while (1) // skip field if it overlaps with previously seen fields { - VarDeclaration field; - while (1) // skip field if it overlaps with previously seen fields - { - field = sd.fields[fieldi]; - ++fieldi; - if (!overlaps(field, sd.fields[], si)) - break; - if (fieldi == nfields) - break; - } - auto tn = field.type.toBasetype(); - auto tnsa = tn.isTypeSArray(); - auto tns = tn.isTypeStruct(); - auto ix = di.initializer; - if (tnsa && ix.isExpInitializer()) - { - ExpInitializer ei = ix.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) - { - si.addInit(field.ident, ei); - ++index; - } - else - si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template - } - else if (tns && ix.isExpInitializer()) + field = sd.fields[fieldi]; + ++fieldi; + if (!overlaps(field, sd.fields[], si)) + break; + if (fieldi == nfields) + break; + } + auto tn = field.type.toBasetype(); + auto tnsa = tn.isTypeSArray(); + auto tns = tn.isTypeStruct(); + auto ix = di.initializer; + if (tnsa && ix.isExpInitializer()) + { + ExpInitializer ei = ix.isExpInitializer(); + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { - /* Disambiguate between an exp representing the entire - * struct, and an exp representing the first field of the struct - */ - if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct - { - si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); - ++index; - } - else // field initializers for struct - si.addInit(field.ident, subStruct(tns, index)); // the first field + si.addInit(field.ident, ei); + ++index; } else + si.addInit(field.ident, subArray(tnsa, index)); // fwd ref of subArray is why subStruct is a template + } + else if (tns && ix.isExpInitializer()) + { + /* Disambiguate between an exp representing the entire + * struct, and an exp representing the first field of the struct + */ + if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct { - si.addInit(field.ident, ix); + si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); ++index; } + else // field initializers for struct + si.addInit(field.ident, subStruct(tns, index)); // the first field + } + else + { + si.addInit(field.ident, ix); + ++index; } } //printf("subStruct() returns ai: %s, index: %d\n", si.toChars(), cast(int)index); @@ -774,7 +771,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else if (tnsa && di.initializer.isExpInitializer()) { ExpInitializer ei = di.initializer.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { ai.addInit(null, ei); ++index; @@ -837,103 +834,110 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn error(ci.loc, "`.%s` is not a field of `%s`\n", id.toChars(), sd.toChars()); return err(); } - else - { - if (fieldi == nfields) - break; - auto ix = di.initializer; + if (fieldi == nfields) + break; - /* If a C initializer is wrapped in a C initializer, with no designators, - * peel off the outer one - */ - if (ix.isCInitializer()) + auto ix = di.initializer; + + /* If a C initializer is wrapped in a C initializer, with no designators, + * peel off the outer one + */ + if (ix.isCInitializer()) + { + CInitializer cix = ix.isCInitializer(); + if (cix.initializerList.length == 1) { - CInitializer cix = ix.isCInitializer(); - if (cix.initializerList.length == 1) + DesigInit dix = cix.initializerList[0]; + if (!dix.designatorList) { - DesigInit dix = cix.initializerList[0]; - if (!dix.designatorList) - { - Initializer inix = dix.initializer; - if (inix.isCInitializer()) - ix = inix; - } + Initializer inix = dix.initializer; + if (inix.isCInitializer()) + ix = inix; } } + } - if (auto cix = ix.isCInitializer()) + if (auto cix = ix.isCInitializer()) + { + /* ImportC loses the structure from anonymous structs, but this is retained + * by the initializer syntax. if a CInitializer has a Designator, it is probably + * a nested anonymous struct + */ + int found; + foreach (dix; cix.initializerList) { - /* ImportC loses the structure from anonymous structs, but this is retained - * by the initializer syntax. if a CInitializer has a Designator, it is probably - * a nested anonymous struct - */ - if (cix.initializerList.length) + Designators* dlistx = dix.designatorList; + if (!dlistx) + continue; + if ((*dlistx).length == 1 && (*dlistx)[0].ident) { - DesigInit dix = cix.initializerList[0]; - Designators* dlistx = dix.designatorList; - if (dlistx && (*dlistx).length == 1 && (*dlistx)[0].ident) + auto id = (*dlistx)[0].ident; + foreach (k, f; sd.fields[]) // linear search for now { - auto id = (*dlistx)[0].ident; - foreach (k, f; sd.fields[]) // linear search for now + if (f.ident == id) { - if (f.ident == id) - { - fieldi = k; - si.addInit(id, dix.initializer); - ++fieldi; - ++index; - continue Loop1; - } + fieldi = k; + si.addInit(id, dix.initializer); + ++fieldi; + ++index; + ++found; + break; } } } + else { + error(ci.loc, "only 1 designator currently allowed for C struct field initializer `%s`", toChars(ci)); + } } - VarDeclaration field; - while (1) // skip field if it overlaps with previously seen fields - { - field = sd.fields[fieldi]; - ++fieldi; - if (!overlaps(field, sd.fields[], si)) - break; - if (fieldi == nfields) - break; - } + if (found == cix.initializerList.length) + continue Loop1; + } - auto tn = field.type.toBasetype(); - auto tnsa = tn.isTypeSArray(); - auto tns = tn.isTypeStruct(); + VarDeclaration field; + while (1) // skip field if it overlaps with previously seen fields + { + field = sd.fields[fieldi]; + ++fieldi; + if (!overlaps(field, sd.fields[], si)) + break; + if (fieldi == nfields) + break; + } - if (tnsa && ix.isExpInitializer()) - { - ExpInitializer ei = ix.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) - { - si.addInit(field.ident, ei); - ++index; - } - else - si.addInit(field.ident, subArray(tnsa, index)); - } - else if (tns && ix.isExpInitializer()) + auto tn = field.type.toBasetype(); + auto tnsa = tn.isTypeSArray(); + auto tns = tn.isTypeStruct(); + + if (tnsa && ix.isExpInitializer()) + { + ExpInitializer ei = ix.isExpInitializer(); + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { - /* Disambiguate between an exp representing the entire - * struct, and an exp representing the first field of the struct - */ - if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct - { - si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); - ++index; - } - else // field initializers for struct - si.addInit(field.ident, subStruct(tns, index)); // the first field + si.addInit(field.ident, ei); + ++index; } else + si.addInit(field.ident, subArray(tnsa, index)); + } + else if (tns && ix.isExpInitializer()) + { + /* Disambiguate between an exp representing the entire + * struct, and an exp representing the first field of the struct + */ + if (representsStruct(ix.isExpInitializer(), tns)) // initializer represents the entire struct { - si.addInit(field.ident, di.initializer); + si.addInit(field.ident, initializerSemantic(ix, sc, tn, needInterpret)); ++index; } + else // field initializers for struct + si.addInit(field.ident, subStruct(tns, index)); // the first field + } + else + { + si.addInit(field.ident, di.initializer); + ++index; } } return initializerSemantic(si, sc, t, needInterpret); @@ -945,7 +949,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn /* If it's an array of integral being initialized by `{ string }` * replace with `string` */ - if (tn.isintegral()) + if (tn.isIntegral()) { if (ExpInitializer ei = isBraceExpression()) { @@ -1008,7 +1012,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn else if (tnsa && di.initializer.isExpInitializer()) { ExpInitializer ei = di.initializer.isExpInitializer(); - if (ei.exp.isStringExp() && tnsa.nextOf().isintegral()) + if (ei.exp.isStringExp() && tnsa.nextOf().isIntegral()) { ai.addInit(null, ei); ++index; @@ -1050,6 +1054,7 @@ Initializer initializerSemantic(Initializer init, Scope* sc, ref Type tx, NeedIn mixin VisitInitializer!Initializer visit; auto result = visit.VisitInitializer(init); + result.semanticDone = true; return (result !is null) ? result : new ErrorInitializer(); } @@ -1090,64 +1095,50 @@ Initializer inferType(Initializer init, Scope* sc) { //printf("ArrayInitializer::inferType() %s\n", toChars()); Expressions* keys = null; - Expressions* values; - if (init.isAssociativeArray()) + Expressions* values = new Expressions(init.value.length); + Initializer no() { + if (keys) + error(init.loc, "not an associative array initializer"); + else + error(init.loc, "cannot infer type from array initializer"); + return new ErrorInitializer(); + } + const bool isAssoc = init.isAssociativeArray(); + if (isAssoc) keys = new Expressions(init.value.length); - values = new Expressions(init.value.length); - for (size_t i = 0; i < init.value.length; i++) + else + values.zero(); + + for (size_t i = 0; i < init.value.length; i++) + { + if (isAssoc) { Expression e = init.index[i]; if (!e) - goto Lno; + return no(); (*keys)[i] = e; - Initializer iz = init.value[i]; - if (!iz) - goto Lno; - iz = iz.inferType(sc); - if (iz.isErrorInitializer()) - { - return iz; - } - (*values)[i] = iz.isExpInitializer().exp; - assert(!(*values)[i].isErrorExp()); } - Expression e = new AssocArrayLiteralExp(init.loc, keys, values); - auto ei = new ExpInitializer(init.loc, e); - return ei.inferType(sc); - } - else - { - auto elements = new Expressions(init.value.length); - elements.zero(); - for (size_t i = 0; i < init.value.length; i++) - { + else assert(!init.index[i]); // already asserted by isAssociativeArray() - Initializer iz = init.value[i]; - if (!iz) - goto Lno; - iz = iz.inferType(sc); - if (iz.isErrorInitializer()) - { - return iz; - } - (*elements)[i] = iz.isExpInitializer().exp; - assert(!(*elements)[i].isErrorExp()); + Initializer iz = init.value[i]; + if (!iz) + return no(); + iz = iz.inferType(sc); + if (iz.isErrorInitializer()) + { + return iz; } - Expression e = new ArrayLiteralExp(init.loc, null, elements); - auto ei = new ExpInitializer(init.loc, e); - return ei.inferType(sc); + (*values)[i] = iz.isExpInitializer().exp; + assert(!(*values)[i].isErrorExp()); } - Lno: - if (keys) - { - error(init.loc, "not an associative array initializer"); - } - else - { - error(init.loc, "cannot infer type from array initializer"); - } - return new ErrorInitializer(); + + Expression e; + e = isAssoc + ? new AssocArrayLiteralExp(init.loc, keys, values) + : new ArrayLiteralExp(init.loc, null, values); + auto ei = new ExpInitializer(init.loc, e); + return ei.inferType(sc); } Initializer visitExp(ExpInitializer init) @@ -1477,10 +1468,10 @@ private bool hasNonConstPointers(Expression e) } if (auto se = ae.e1.isStructLiteralExp()) { - if (!(se.stageflags & stageSearchPointers)) + if (!(se.stageflags & StructLiteralExp.StageFlags.searchPointers)) { const old = se.stageflags; - se.stageflags |= stageSearchPointers; + se.stageflags |= StructLiteralExp.StageFlags.searchPointers; bool ret = checkArray(se.elements); se.stageflags = old; return ret; @@ -1601,7 +1592,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* (vd.offset & (target.ptrsize - 1)))) { if (sc.setUnsafe(false, argLoc, - "field `%s.%s` cannot assign to misaligned pointers in `@safe` code", sd, vd)) + "field `%s.%s` assigning to misaligned pointers", sd, vd)) { errors = true; elems[fieldi] = ErrorExp.get(); // for better diagnostics on multiple errors @@ -1639,7 +1630,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope* continue; } - elems[fieldi] = doCopyOrMove(sc, ex); + elems[fieldi] = doCopyOrMove(sc, ex, null, false); ++fieldi; } if (errors) diff --git a/gcc/d/dmd/inline.d b/gcc/d/dmd/inline.d index 3e163ae..72f2da4 100644 --- a/gcc/d/dmd/inline.d +++ b/gcc/d/dmd/inline.d @@ -4,12 +4,12 @@ * The AST is traversed, and every function call is considered for inlining using `inlinecost.d`. * The function call is then inlined if this cost is below a threshold. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/inline.d, _inline.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/inline.d, _inline.d) * Documentation: https://dlang.org/phobos/dmd_inline.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/inline.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/inline.d */ module dmd.inline; diff --git a/gcc/d/dmd/intrange.d b/gcc/d/dmd/intrange.d index 29c8b50..f68e302 100644 --- a/gcc/d/dmd/intrange.d +++ b/gcc/d/dmd/intrange.d @@ -1,22 +1,21 @@ /** * Implement $(LINK2 https://digitalmars.com/articles/b62.html, Value Range Propagation). * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/intrange.d, _intrange.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/intrange.d, _intrange.d) * Documentation: https://dlang.org/phobos/dmd_intrange.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/intrange.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/intrange.d */ module dmd.intrange; import core.stdc.stdio; -import dmd.astenums; -import dmd.mtype; -import dmd.expression; -import dmd.globals; +import dmd.astenums : Tdchar; +import dmd.mtype : Type; +import dmd.globals : uinteger_t; private uinteger_t copySign(uinteger_t x, bool sign) @safe { @@ -70,7 +69,7 @@ struct SignExtendedNumber } if (value < a.value) return -1; - else if (value > a.value) + if (value > a.value) return 1; else return 0; @@ -122,10 +121,10 @@ struct SignExtendedNumber SignExtendedNumber opBinary(string op : "+")(SignExtendedNumber rhs) { uinteger_t sum = value + rhs.value; - bool carry = sum < value && sum < rhs.value; + const carry = sum < value && sum < rhs.value; if (negative != rhs.negative) return SignExtendedNumber(sum, !carry); - else if (negative) + if (negative) return SignExtendedNumber(carry ? sum : 0, true); else return SignExtendedNumber(carry ? ulong.max : sum, false); @@ -142,7 +141,7 @@ struct SignExtendedNumber SignExtendedNumber opBinary(string op : "*")(SignExtendedNumber rhs) { - // perform *saturated* multiplication, otherwise we may get bogus ranges + // perform* saturated* multiplication, otherwise we may get bogus ranges // like 0x10 * 0x10 == 0x100 == 0. /* Special handling for zeros: @@ -155,7 +154,7 @@ struct SignExtendedNumber { if (!negative) return this; - else if (rhs.negative) + if (rhs.negative) return max(); else return rhs.value == 0 ? rhs : this; @@ -249,7 +248,7 @@ struct SignExtendedNumber // shifts will give huge result. if (value == 0) return this; - else if (rhs.negative) + if (rhs.negative) return extreme(negative); uinteger_t v = copySign(value, negative); @@ -278,7 +277,7 @@ struct SignExtendedNumber { if (rhs.negative || rhs.value > 63) return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0); - else if (isMinimum()) + if (isMinimum()) return rhs.value == 0 ? this : SignExtendedNumber(-1UL << (64 - rhs.value), true); uinteger_t x = value ^ -cast(int)negative; @@ -317,12 +316,12 @@ struct IntRange static IntRange fromType(Type type) { - return fromType(type, type.isunsigned()); + return fromType(type, type.isUnsigned()); } static IntRange fromType(Type type, bool isUnsigned) { - if (!type.isintegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return widest(); uinteger_t mask = type.sizemask(); @@ -444,24 +443,22 @@ struct IntRange IntRange _cast(Type type) { - if (!type.isintegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return this; - else if (!type.isunsigned()) + if (!type.isUnsigned()) return castSigned(type.sizemask()); - else if (type.toBasetype().ty == Tdchar) + if (type.toBasetype().ty == Tdchar) return castDchar(); - else return castUnsigned(type.sizemask()); } IntRange castUnsigned(Type type) { - if (!type.isintegral() || type.toBasetype().ty == Tvector) + if (!type.isIntegral() || type.toBasetype().isTypeVector()) return castUnsigned(ulong.max); - else if (type.toBasetype().ty == Tdchar) + if (type.toBasetype().ty == Tdchar) return castDchar(); - else - return castUnsigned(type.sizemask()); + return castUnsigned(type.sizemask()); } bool contains(IntRange a) @safe @@ -479,14 +476,11 @@ struct IntRange { if (imax.negative) return this; - else if (!imin.negative) + if (!imin.negative) return IntRange(-imax, -imin); - else - { - SignExtendedNumber imaxAbsNeg = -imax; - return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin, - SignExtendedNumber(0)); - } + SignExtendedNumber imaxAbsNeg = -imax; + return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin, + SignExtendedNumber(0)); } IntRange unionWith(const ref IntRange other) const @safe @@ -504,7 +498,7 @@ struct IntRange union_ = true; } - ref const(IntRange) dump(const(char)* funcName, Expression e) const return + ref const(IntRange) dump(Exp)(const(char)* funcName, Exp e) const return { printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n", imin.negative?'-':'+', cast(ulong)imin.value, @@ -574,13 +568,13 @@ struct IntRange swap(l, r); // r spans [-1,0] } - auto minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); - auto minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax)); - auto maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); - auto maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax)); + const minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); + const minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax)); + const maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1))); + const maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax)); - auto min = minAndNeg < minAndPos ? minAndNeg : minAndPos; - auto max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos; + const min = minAndNeg < minAndPos ? minAndNeg : minAndPos; + const max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos; auto range = IntRange(min, max); return range; @@ -668,7 +662,7 @@ struct IntRange return widest(); // Don't treat the whole range as divide by 0 if only one end of a range is 0. - // Issue 15289 + // https://issues.dlang.org/show_bug.cgi?id=15289 if (rhs.imax.value == 0) { rhs.imax.value--; @@ -682,17 +676,19 @@ struct IntRange { return IntRange(imin / rhs.imax, imax / rhs.imin); } - else + if (rhs.imin.negative && !rhs.imax.negative) // divisor spans [-1, 0, 1] { - // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] - SignExtendedNumber[4] bdy; - bdy[0] = imin / rhs.imin; - bdy[1] = imin / rhs.imax; - bdy[2] = imax / rhs.imin; - bdy[3] = imax / rhs.imax; - + SignExtendedNumber[4] bdy = [-imin, imin, -imax, imax]; return IntRange.fromNumbers4(bdy.ptr); } + // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] + SignExtendedNumber[4] bdy; + bdy[0] = imin / rhs.imin; + bdy[1] = imin / rhs.imax; + bdy[2] = imax / rhs.imin; + bdy[3] = imax / rhs.imax; + + return IntRange.fromNumbers4(bdy.ptr); } IntRange opBinary(string op : "%")(IntRange rhs) diff --git a/gcc/d/dmd/json.d b/gcc/d/dmd/json.d index f20b3d4..080870a 100644 --- a/gcc/d/dmd/json.d +++ b/gcc/d/dmd/json.d @@ -1,12 +1,12 @@ /** * Code for generating .json descriptions of the module when passing the `-X` flag to dmd. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/json.d, _json.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/json.d, _json.d) * Documentation: https://dlang.org/phobos/dmd_json.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/json.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/json.d */ module dmd.json; @@ -24,6 +24,7 @@ import dmd.denum; import dmd.dimport; import dmd.dmodule; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -40,9 +41,13 @@ import dmd.root.string; import dmd.target; import dmd.visitor; -version(Windows) { - extern (C) char* getcwd(char* buffer, size_t maxlen); -} else { +version(Windows) +{ + extern (C) char* _getcwd(char* buffer, size_t maxlen); + alias getcwd = _getcwd; +} +else +{ import core.sys.posix.unistd : getcwd; } @@ -328,7 +333,7 @@ public: } } - extern(D) void propertyStorageClass(const char[] name, StorageClass stc) + extern(D) void propertyStorageClass(const char[] name, STC stc) { stc &= STC.visibleStorageClasses; if (stc) @@ -345,23 +350,22 @@ public: } } - extern(D) void property(const char[] linename, const char[] charname, const ref Loc loc) + extern(D) void property(const char[] linename, const char[] charname, Loc loc) { if (loc.isValid()) { - if (auto filename = loc.filename.toDString) + SourceLoc sl = SourceLoc(loc); + if (sl.filename.length > 0 && sl.filename != this.filename) { - if (filename != this.filename) - { - this.filename = filename; - property("file", filename); - } + this.filename = sl.filename; + property("file", sl.filename); } - if (loc.linnum) + + if (sl.linnum) { - property(linename, loc.linnum); - if (loc.charnum) - property(charname, loc.charnum); + property(linename, sl.linnum); + if (sl.charnum) + property(charname, sl.charnum); } } } @@ -904,7 +908,7 @@ public: arrayStart(); foreach (importPath; global.params.imppath[]) { - item(importPath.toDString); + item(importPath.path.toDString); } arrayEnd(); @@ -990,35 +994,34 @@ void json_generate(ref Modules modules, ref OutBuffer buf) // of modules representing their syntax. json.generateModules(modules); json.removeComma(); + return; } - else - { - // Generate the new format which is an object where each - // output option is its own field. - json.objectStart(); - if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo) - { - json.propertyStart("compilerInfo"); - json.generateCompilerInfo(); - } - if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo) - { - json.propertyStart("buildInfo"); - json.generateBuildInfo(); - } - if (global.params.jsonFieldFlags & JsonFieldFlags.modules) - { - json.propertyStart("modules"); - json.generateModules(modules); - } - if (global.params.jsonFieldFlags & JsonFieldFlags.semantics) - { - json.propertyStart("semantics"); - json.generateSemantics(); - } - json.objectEnd(); + // Generate the new format which is an object where each + // output option is its own field. + + json.objectStart(); + if (global.params.jsonFieldFlags & JsonFieldFlags.compilerInfo) + { + json.propertyStart("compilerInfo"); + json.generateCompilerInfo(); + } + if (global.params.jsonFieldFlags & JsonFieldFlags.buildInfo) + { + json.propertyStart("buildInfo"); + json.generateBuildInfo(); + } + if (global.params.jsonFieldFlags & JsonFieldFlags.modules) + { + json.propertyStart("modules"); + json.generateModules(modules); + } + if (global.params.jsonFieldFlags & JsonFieldFlags.semantics) + { + json.propertyStart("semantics"); + json.generateSemantics(); } + json.objectEnd(); } /** diff --git a/gcc/d/dmd/json.h b/gcc/d/dmd/json.h index b119c9e..8211509 100644 --- a/gcc/d/dmd/json.h +++ b/gcc/d/dmd/json.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/lambdacomp.d b/gcc/d/dmd/lambdacomp.d index a1db8d5..9f9fd77 100644 --- a/gcc/d/dmd/lambdacomp.d +++ b/gcc/d/dmd/lambdacomp.d @@ -5,12 +5,12 @@ * The serialization is a string which contains the type of the parameters and the string * represantation of the lambda expression. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/lamdbacomp.d, _lambdacomp.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lambdacomp.d, _lambdacomp.d) * Documentation: https://dlang.org/phobos/dmd_lambdacomp.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lambdacomp.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/lambdacomp.d */ module dmd.lambdacomp; @@ -26,8 +26,8 @@ import dmd.dsymbolsem; import dmd.dtemplate; import dmd.expression; import dmd.func; -import dmd.dmangle; import dmd.hdrgen; +import dmd.mangle; import dmd.mtype; import dmd.common.outbuffer; import dmd.root.rmem; @@ -240,39 +240,39 @@ public: auto id = exp.ident.toChars(); // If it's not an argument - if (!checkArgument(id)) + if (checkArgument(id)) + return; + + // we must check what the identifier expression is. + Dsymbol scopesym; + Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); + + // If it's an unknown symbol, consider the function incomparable + if (!s) { - // we must check what the identifier expression is. - Dsymbol scopesym; - Dsymbol s = sc.search(exp.loc, exp.ident, scopesym); - if (s) - { - auto v = s.isVarDeclaration(); - // If it's a VarDeclaration, it must be a manifest constant - if (v && (v.storage_class & STC.manifest)) - { - v.getConstInitializer.accept(this); - } - else if (auto em = s.isEnumDeclaration()) - { - d = em; - et = ExpType.EnumDecl; - } - else if (auto fd = s.isFuncDeclaration()) - { - writeMangledName(fd); - } - // For anything else, the function is deemed uncomparable - else - { - buf.setsize(0); - } - } - // If it's an unknown symbol, consider the function incomparable - else - { - buf.setsize(0); - } + buf.setsize(0); + return; + } + + auto v = s.isVarDeclaration(); + // If it's a VarDeclaration, it must be a manifest constant + if (v && (v.storage_class & STC.manifest)) + { + v.getConstInitializer.accept(this); + } + else if (auto em = s.isEnumDeclaration()) + { + d = em; + et = ExpType.EnumDecl; + } + else if (auto fd = s.isFuncDeclaration()) + { + writeMangledName(fd); + } + // For anything else, the function is deemed uncomparable + else + { + buf.setsize(0); } } @@ -445,26 +445,28 @@ public: visitType(p.type); } - override void visit(StructLiteralExp e) { + override void visit(StructLiteralExp e) + { static if (LOG) printf("StructLiteralExp: %s\n", e.toChars); auto ty = cast(TypeStruct)e.stype; - if (ty) + if (!ty) { - writeMangledName(ty.sym); - auto dim = e.elements.length; - foreach (i; 0..dim) - { - auto elem = (*e.elements)[i]; - if (elem) - elem.accept(this); - else - buf.writestring("null_"); - } - } - else buf.setsize(0); + return; + } + + writeMangledName(ty.sym); + auto dim = e.elements.length; + foreach (i; 0..dim) + { + auto elem = (*e.elements)[i]; + if (elem) + elem.accept(this); + else + buf.writestring("null_"); + } } override void visit(ArrayLiteralExp) { buf.setsize(0); } diff --git a/gcc/d/dmd/lexer.d b/gcc/d/dmd/lexer.d index c9c506e..ed9f7f1 100644 --- a/gcc/d/dmd/lexer.d +++ b/gcc/d/dmd/lexer.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/lex.html, Lexical) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/lexer.d, _lexer.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/lexer.d, _lexer.d) * Documentation: https://dlang.org/phobos/dmd_lexer.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/lexer.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/lexer.d */ module dmd.lexer; @@ -22,9 +22,11 @@ import dmd.errorsink; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.common.smallbuffer; +import dmd.common.outbuffer; +import dmd.common.charactertables; import dmd.root.array; import dmd.root.ctfloat; -import dmd.common.outbuffer; import dmd.root.port; import dmd.root.rmem; import dmd.root.utf; @@ -32,16 +34,13 @@ import dmd.tokens; nothrow: -version (DMDLIB) -{ - version = LocOffset; -} - /*********************************************************** * Values to use for various magic identifiers */ struct CompileEnv { + import dmd.common.charactertables; + uint versionNumber; /// __VERSION__ const(char)[] date; /// __DATE__ const(char)[] time; /// __TIME__ @@ -49,8 +48,13 @@ struct CompileEnv const(char)[] timestamp; /// __TIMESTAMP__ bool previewIn; /// `in` means `[ref] scope const`, accepts rvalues + bool transitionIn; /// `-transition=in` is active, `in` parameters are listed bool ddocOutput; /// collect embedded documentation comments bool masm; /// use MASM inline asm syntax + + // these need a default otherwise tests won't work. + IdentifierCharLookup cCharLookupTable; /// C identifier table (set to the lexer by the C parser) + IdentifierCharLookup dCharLookupTable; /// D identifier table } /*********************************************************** @@ -59,13 +63,17 @@ class Lexer { private __gshared OutBuffer stringbuffer; + BaseLoc* baseLoc; // Used to generate `scanloc`, which is just an index into this data structure Loc scanloc; // for error messages Loc prevloc; // location of token before current + int linnum; // current line number const(char)* p; // current character Token token; + IdentifierCharLookup charLookup; /// Character table for identifiers + // For ImportC bool Ccompile; /// true if compiling ImportC @@ -121,10 +129,11 @@ class Lexer ErrorSink errorSink, const CompileEnv* compileEnv) scope { - scanloc = Loc(filename, 1, 1); // debug printf("Lexer::Lexer(%p)\n", base); // debug printf("lexer.filename = %s\n", filename); token = Token.init; + this.baseLoc = newBaseLoc(filename, base[0 .. endoffset]); + this.linnum = 1; this.base = base; this.end = base + endoffset; p = base + begoffset; @@ -142,6 +151,8 @@ class Lexer { this.compileEnv.versionNumber = 1; this.compileEnv.vendor = "DLF"; + this.compileEnv.cCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.LR); + this.compileEnv.dCharLookupTable = IdentifierCharLookup.forTable(IdentifierTable.LR); } //initKeywords(); /* If first line starts with '#!', ignore the line @@ -175,6 +186,11 @@ class Lexer } endOfLine(); } + + // setup the identifier table lookup functions + // C tables are setup in its parser constructor + // Due to us not knowing if we're in C at this point in time. + charLookup = this.compileEnv.dCharLookupTable; } /*********************** @@ -207,7 +223,9 @@ class Lexer tokenizeNewlines = true; inTokenStringConstant = 0; lastDocLine = 0; - scanloc = Loc("#defines", 1, 1); + + baseLoc = newBaseLoc("#defines", slice); + scanloc = baseLoc.getLoc(0); } /********************************** @@ -301,11 +319,13 @@ class Lexer */ final void scan(Token* t) { - const lastLine = scanloc.linnum; + const lastLine = linnum; Loc startLoc; t.blockComment = null; t.lineComment = null; + size_t universalCharacterName4, universalCharacterName8; + while (1) { t.ptr = p; @@ -395,10 +415,35 @@ class Lexer continue; // skip white space case '\\': - if (Ccompile && (p[1] == '\r' || p[1] == '\n')) + if (Ccompile) { - ++p; // ignore \ followed by new line, like VC does - continue; + if (p[1] == '\r' || p[1] == '\n') + { + ++p; // ignore \ followed by new line, like VC does + continue; + } + else if (p[1] == 'u') + { + // Universal Character Name (C) 2 byte + // \uXXXX + // let the main case handling for identifiers process this + + // case_indent will always increment, so subtract to prevent branching on the fast path + p--; + + goto case_ident; + } + else if (p[1] == 'U') + { + // Universal Character Name (C) 4 byte + // \UXXXXXXXX + // let the main case handling for identifiers process this + + // case_indent will always increment, so subtract to prevent branching on the fast path + p--; + + goto case_ident; + } } goto default; @@ -455,7 +500,7 @@ class Lexer clexerCharConstant(*t, c); return; } - else if (p[1] == '\"') // C wide string literal + if (p[1] == '\"') // C wide string literal { const c = *p; ++p; @@ -465,7 +510,7 @@ class Lexer 'd'; return; } - else if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal + if (p[1] == '8' && p[2] == '\"') // C UTF-8 string literal { p += 2; escapeStringConstant(t); @@ -498,14 +543,13 @@ class Lexer delimitedStringConstant(t); return; } - else if (p[1] == '{') + if (p[1] == '{') { p++; tokenStringConstant(t); return; } - else - goto case_ident; + goto case_ident; case 'i': if (Ccompile) goto case_ident; @@ -515,20 +559,19 @@ class Lexer escapeStringConstant(t, true); return; } - else if (p[1] == '`') + if (p[1] == '`') { p++; // skip the i wysiwygStringConstant(t, true); return; } - else if (p[1] == 'q' && p[2] == '{') + if (p[1] == 'q' && p[2] == '{') { p += 2; // skip the i and q tokenStringConstant(t, true); return; } - else - goto case_ident; + goto case_ident; case '"': escapeStringConstant(t); return; @@ -586,23 +629,161 @@ class Lexer case '_': case_ident: { - while (1) + IdentLoop: while (1) { + // If this is changed, change the decrement in C's universal character name code above + // For syntax \uXXXX and \UXXXXXXXX const c = *++p; + + // Is this the first character of the identifier + // For the universal character name this will line up, + // for the main switch it won't since it wasn't the first, + // for the default it won't either because a decode increments. + const isStartCharacter = t.ptr is p; + if (isidchar(c)) continue; - else if (c & 0x80) + if (c & 0x80) { const s = p; const u = decodeUTF(); - if (isUniAlpha(u)) - continue; - error(t.loc, "char 0x%04x not allowed in identifier", u); + + if (isStartCharacter) + { + if (charLookup.isStart(u)) + continue; + error(t.loc, "character 0x%04x is not allowed as a start character in an identifier", u); + } + else + { + if (charLookup.isContinue(u)) + continue; + error(t.loc, "character 0x%04x is not allowed as a continue character in an identifier", u); + } + p = s; } + else if (Ccompile && c == '\\') + { + uint times; + const s = p; + p++; + + if (*p == 'u') + { + // Universal Character Name (C) 2 byte + // \uXXXX + p++; + times = 4; + } + else if (*p == 'U') + { + // Universal Character Name (C) 4 byte + // \UXXXXXXXX + p++; + times = 8; + } + else + { + error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid u/U", *p); + p = s; + break; + } + + foreach(_; 0 .. times) + { + const hc = *p; + p++; + + if ((hc >= '0' && hc <= '9') || (hc >= 'a' && hc <= 'f') || (hc >= 'A' && hc <= 'F')) + continue; + + error(t.loc, "char 0x%x is not allowed to follow '\\' expecting a C universal character name in format \\uXXXX or \\UXXXXXXXX with hex digits instead of X with invalid hex digit", hc); + p = s; + break IdentLoop; + } + + continue; + } break; } - Identifier id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false); + + Identifier id; + + if (universalCharacterName4 > 0 || universalCharacterName8 > 0) + { + auto priorValidation = t.ptr[0 .. p - t.ptr]; + const(char)* priorVPtr = priorValidation.ptr; + const possibleLength = ( + priorValidation.length - ( + (universalCharacterName4 * 6) + + (universalCharacterName8 * 10) + )) + ( + (universalCharacterName4 * 3) + + (universalCharacterName8 * 4) + ); + + char[64] buffer = void; + SmallBuffer!char sb = SmallBuffer!char(possibleLength, buffer[]); + + char[] storage = sb.extent; + size_t offset; + + while(priorVPtr < &priorValidation[$-1] + 1) + { + if (*priorVPtr == '\\') + { + dchar tempDchar = 0; + uint times; + + // universal character name (C) + if (priorVPtr[1] == 'u') + times = 4; + else if (priorVPtr[1] == 'U') + times = 8; + else + assert(0, "ICE: Universal character name is 2 or 4 bytes only"); + priorVPtr += 2; + + foreach(_; 0 .. times) + { + char c = *++priorVPtr; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'a' && c <= 'f') + c -= 'a' - 10; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + + tempDchar <<= 4; + tempDchar |= c; + } + + utf_encodeChar(&storage[offset], tempDchar); + offset += utf_codeLengthChar(tempDchar); + + // Could be an error instead of a warning, + // but hey it was written specifically so why worry? + if (priorVPtr is priorValidation.ptr) + { + if (!charLookup.isStart(tempDchar)) + warning(t.loc, "char 0x%x is not allowed start character for an identifier", tempDchar); + } + else + { + if (!charLookup.isContinue(tempDchar)) + warning(t.loc, "char 0x%x is not allowed continue character for an identifier", tempDchar); + } + } + else + storage[offset++] = *++priorVPtr; + } + + id = Identifier.idPool(storage[0 .. offset], false); + } + else + id = Identifier.idPool((cast(char*)t.ptr)[0 .. p - t.ptr], false); + t.ident = id; t.value = cast(TOK)id.getValue(); @@ -686,7 +867,7 @@ class Lexer case 0: case 0x1A: error(t.loc, "unterminated /* */ comment"); - p = end; + //p = end; t.loc = loc(); t.value = TOK.endOfFile; return; @@ -712,11 +893,11 @@ class Lexer t.value = TOK.comment; return; } - else if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr) + if (doDocComment && t.ptr[2] == '*' && p - 4 != t.ptr) { // if /** but not /**/ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } continue; case '/': // do // style comments @@ -744,9 +925,9 @@ class Lexer if (doDocComment && t.ptr[2] == '/') { getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } - p = end; + //p = end; t.loc = loc(); t.value = TOK.endOfFile; return; @@ -776,7 +957,7 @@ class Lexer if (doDocComment && t.ptr[2] == '/') { getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } p++; endOfLine(); @@ -822,7 +1003,7 @@ class Lexer case 0: case 0x1A: error(t.loc, "unterminated /+ +/ comment"); - p = end; + //p = end; t.loc = loc(); t.value = TOK.endOfFile; return; @@ -848,7 +1029,7 @@ class Lexer { // if /++ but not /++/ getDocComment(t, lastLine == startLoc.linnum, startLoc.linnum - lastDocLine > 1); - lastDocLine = scanloc.linnum; + lastDocLine = linnum; } continue; } @@ -1174,9 +1355,11 @@ class Lexer if (c & 0x80) { c = decodeUTF(); - // Check for start of unicode identifier - if (isUniAlpha(c)) + + // Check for start of an identifier + if (charLookup.isStart(c)) goto case_ident; + if (c == PS || c == LS) { endOfLine(); @@ -1281,7 +1464,7 @@ class Lexer * Returns: * the escape sequence as a single character */ - private dchar escapeSequence(const ref Loc loc, ref const(char)* sequence, bool Ccompile, out dchar c2) + private dchar escapeSequence(Loc loc, ref const(char)* sequence, bool Ccompile, out dchar c2) { const(char)* p = sequence; // cache sequence reference on stack scope(exit) sequence = p; @@ -1372,6 +1555,8 @@ class Lexer if (ndigits != 2 && !utf_isValidDchar(v)) { error(loc, "invalid UTF character \\U%08x", v); + if (v >= 0xD800 && v <= 0xDFFF) + errorSupplemental("The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?"); v = '?'; // recover with valid UTF character } } @@ -1688,7 +1873,7 @@ class Lexer delimright = ']'; else if (c == '<') delimright = '>'; - else if (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c))) + else if (isalpha(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c))) { // Start of identifier; must be a heredoc Token tok; @@ -1736,7 +1921,9 @@ class Lexer } else if (c == delimright) goto Ldone; - if (startline && (isalpha(c) || c == '_' || (c >= 0x80 && isUniAlpha(c))) && hereid) + + // we're looking for a new identifier token + if (startline && (isalpha(c) || c == '_' || (c >= 0x80 && charLookup.isStart(c))) && hereid) { Token tok; auto psave = p; @@ -2384,19 +2571,19 @@ class Lexer Ldone: if (errorDigit) { - error(token.loc, "%s digit expected, not `%c`", base == 2 ? "binary".ptr : + error(scanloc, "%s digit expected, not `%c`", base == 2 ? "binary".ptr : base == 8 ? "octal".ptr : "decimal".ptr, errorDigit); err = true; } if (overflow && !err) { - error("integer overflow"); + error(scanloc, "integer overflow"); err = true; } if ((base == 2 && !anyBinaryDigitsNoSingleUS) || (base == 16 && !anyHexDigitsNoSingleUS)) - error(token.loc, "`%.*s` isn't a valid integer literal, use `%.*s0` instead", cast(int)(p - start), start, 2, start); + error(scanloc, "`%.*s` isn't a valid integer literal, use `%.*s0` instead", cast(int)(p - start), start, 2, start); t.unsvalue = n; @@ -2425,7 +2612,7 @@ class Lexer goto L1; case 'l': f = FLAGS.long_; - error("lower case integer suffix 'l' is not allowed. Please use 'L' instead"); + error(scanloc, "lower case integer suffix 'l' is not allowed. Please use 'L' instead"); goto L1; case 'L': f = FLAGS.long_; @@ -2433,7 +2620,7 @@ class Lexer p++; if ((flags & f) && !err) { - error("repeated integer suffix `%c`", p[-1]); + error(scanloc, "repeated integer suffix `%c`", p[-1]); err = true; } flags = cast(FLAGS)(flags | f); @@ -2447,9 +2634,9 @@ class Lexer { if (err) // can't translate invalid octal value, just show a generic message - error("octal literals larger than 7 are no longer supported"); + error(scanloc, "octal literals larger than 7 are no longer supported"); else - error(token.loc, "octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!\"%llo%.*s\"` instead", + error(scanloc, "octal literals `0%llo%.*s` are no longer supported, use `std.conv.octal!\"%llo%.*s\"` instead", n, cast(int)(p - psuffix), psuffix, n, cast(int)(p - psuffix), psuffix); } TOK result; @@ -2967,9 +3154,7 @@ class Lexer final Loc loc() @nogc { - scanloc.charnum = cast(ushort)(1 + p - line); - version (LocOffset) - scanloc.fileOffset = cast(uint)(p - base); + scanloc = baseLoc.getLoc(cast(uint) (p - base)); return scanloc; } @@ -2978,16 +3163,26 @@ class Lexer eSink.error(token.loc, format, args); } - void error(T...)(const ref Loc loc, const(char)* format, T args) + void error(T...)(Loc loc, const(char)* format, T args) { eSink.error(loc, format, args); } - void deprecation(T...)(const ref Loc loc, const(char)* format, T args) + void errorSupplemental(T...)(const(char)* format, T args) + { + eSink.errorSupplemental(token.loc, format, args); + } + + void deprecation(T...)(Loc loc, const(char)* format, T args) { eSink.deprecation(loc, format, args); } + void warning(T...)(Loc loc, const(char)* format, T args) + { + eSink.warning(loc, format, args); + } + void deprecation(T...)(const(char)* format, T args) { eSink.deprecation(token.loc, format, args); @@ -3054,7 +3249,6 @@ class Lexer */ final void poundLine(ref Token tok, bool linemarker) { - auto linnum = this.scanloc.linnum; const(char)* filespec = null; bool flags; @@ -3091,9 +3285,7 @@ class Lexer case TOK.endOfLine: if (!inTokenStringConstant) { - this.scanloc.linnum = linnum; - if (filespec) - this.scanloc.filename = filespec; + baseLoc.addSubstitution(cast(uint) (p - base), filespec, linnum); } return; case TOK.file: @@ -3244,12 +3436,12 @@ class Lexer if (*q != ct) break; } - /* Remove leading spaces until start of the comment + /* Remove leading line feed or space */ int linestart = 0; if (ct == '/') { - while (q < qend && (*q == ' ' || *q == '\t')) + if (q < qend && *q == ' ') ++q; } else if (q < qend) @@ -3391,10 +3583,11 @@ class Lexer /************************** * `p` should be at start of next line */ - private void endOfLine() @nogc @safe + private void endOfLine() @safe { - scanloc.linnum = scanloc.linnum + 1; + linnum += 1; line = p; + baseLoc.newLine(cast(uint)(p - base)); } /**************************** @@ -3416,124 +3609,6 @@ class Lexer } } - -/******************************* Private *****************************************/ - -private: - -private enum LS = 0x2028; // UTF line separator -private enum PS = 0x2029; // UTF paragraph separator - -/******************************************** - * Do our own char maps - */ -private static immutable cmtable = () -{ - ubyte[256] table; - foreach (const c; 0 .. table.length) - { - if ('0' <= c && c <= '7') - table[c] |= CMoctal; - if (c_isxdigit(c)) - table[c] |= CMhex; - if (c_isalnum(c) || c == '_') - table[c] |= CMidchar; - - switch (c) - { - case 'x': case 'X': - case 'b': case 'B': - table[c] |= CMzerosecond; - break; - - case '0': .. case '9': - case 'e': case 'E': - case 'f': case 'F': - case 'l': case 'L': - case 'p': case 'P': - case 'u': case 'U': - case 'i': - case '.': - case '_': - table[c] |= CMzerosecond | CMdigitsecond; - break; - - default: - break; - } - - switch (c) - { - case '\\': - case '\n': - case '\r': - case 0: - case 0x1A: - case '\'': - break; - default: - if (!(c & 0x80)) - table[c] |= CMsinglechar; - break; - } - } - return table; -}(); - -private -{ - enum CMoctal = 0x1; - enum CMhex = 0x2; - enum CMidchar = 0x4; - enum CMzerosecond = 0x8; - enum CMdigitsecond = 0x10; - enum CMsinglechar = 0x20; -} - -private bool isoctal(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMoctal) != 0; -} - -private bool ishex(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMhex) != 0; -} - -private bool isidchar(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMidchar) != 0; -} - -private bool isZeroSecond(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMzerosecond) != 0; -} - -private bool isDigitSecond(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMdigitsecond) != 0; -} - -private bool issinglechar(const char c) pure @nogc @safe -{ - return (cmtable[c] & CMsinglechar) != 0; -} - -private bool c_isxdigit(const int c) pure @nogc @safe -{ - return (( c >= '0' && c <= '9') || - ( c >= 'a' && c <= 'f') || - ( c >= 'A' && c <= 'F')); -} - -private bool c_isalnum(const int c) pure @nogc @safe -{ - return (( c >= '0' && c <= '9') || - ( c >= 'a' && c <= 'z') || - ( c >= 'A' && c <= 'Z')); -} - /******************************* Unittest *****************************************/ unittest @@ -3599,25 +3674,35 @@ unittest import core.stdc.stdarg; string expected; + string expectedSupplemental; bool gotError; - void error(const ref Loc loc, const(char)* format, ...) + void verror(Loc loc, const(char)* format, va_list ap) { gotError = true; char[100] buffer = void; + auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)]; + assert(expected == actual); + } + + void errorSupplemental(Loc loc, const(char)* format, ...) + { + gotError = true; + char[128] buffer = void; va_list ap; va_start(ap, format); auto actual = buffer[0 .. vsnprintf(buffer.ptr, buffer.length, format, ap)]; va_end(ap); - assert(expected == actual); + assert(expectedSupplemental == actual); } } ErrorSinkTest errorSink = new ErrorSinkTest; - void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false) + void test2(string sequence, string[2] expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false) { - errorSink.expected = expectedError; + errorSink.expected = expectedError[0]; + errorSink.expectedSupplemental = expectedError[1]; errorSink.gotError = false; auto p = cast(const(char)*)sequence.ptr; Lexer lexer = new Lexer(errorSink); @@ -3630,6 +3715,11 @@ unittest assert(expectedScanLength == actualScanLength); } + void test(string sequence, string expectedError, dchar expectedReturnValue, uint expectedScanLength, bool Ccompile = false) + { + test2(sequence, [expectedError, null], expectedReturnValue, expectedScanLength, Ccompile); + } + test("c", `undefined escape sequence \c`, 'c', 1); test("!", `undefined escape sequence \!`, '!', 1); test(""", `undefined escape sequence \&`, '&', 1, true); @@ -3648,8 +3738,6 @@ unittest test("U0001f6" , `escape hex sequence has 6 hex digits instead of 8`, 0x0001f6, 7); test("U0001f60", `escape hex sequence has 7 hex digits instead of 8`, 0x0001f60, 8); - test("ud800" , `invalid UTF character \U0000d800`, '?', 5); - test("udfff" , `invalid UTF character \U0000dfff`, '?', 5); test("U00110000", `invalid UTF character \U00110000`, '?', 9); test("xg0" , `undefined escape hex sequence \xg`, 'g', 2); @@ -3661,6 +3749,9 @@ unittest test(""", `unterminated named entity "`, '?', 5); test("400", `escape octal sequence \400 is larger than \377`, 0x100, 3); + + test2("uD800", [`invalid UTF character \U0000d800`, `The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?`], '?', 5); + test2("uDFFF", [`invalid UTF character \U0000dfff`, `The code unit is a UTF-16 surrogate, is the escape UTF-16 not a Unicode code point?`], '?', 5); } unittest diff --git a/gcc/d/dmd/location.d b/gcc/d/dmd/location.d index ca6805e..393ffb8 100644 --- a/gcc/d/dmd/location.d +++ b/gcc/d/dmd/location.d @@ -1,12 +1,12 @@ /** * Encapsulates file/line/column locations. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/location.d, _location.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/location.d, _location.d) * Documentation: https://dlang.org/phobos/dmd_location.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/location.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/location.d */ module dmd.location; @@ -16,19 +16,15 @@ import core.stdc.stdio; import dmd.common.outbuffer; import dmd.root.array; import dmd.root.filename; - -version (DMDLIB) -{ - version = LocOffset; -} +import dmd.root.string: toDString; /// How code locations are formatted for diagnostic reporting enum MessageStyle : ubyte { digitalmars, /// filename.d(line): message gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html + sarif /// JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html } - /** A source code location @@ -37,19 +33,19 @@ debug info etc. */ struct Loc { - private uint _linnum; - private uint _charnum; - private uint fileIndex; // index into filenames[], starting from 1 (0 means no filename) - version (LocOffset) - uint fileOffset; /// utf8 code unit index relative to start of file, starting from 0 + private uint index = 0; // offset into lineTable[] + + // FIXME: This arbitrary size increase is needed to prevent segfault in + // runnable/test42.d on Ubuntu x86 when DMD was built with DMD 2.105 .. 2.110 + // https://github.com/dlang/dmd/pull/20777#issuecomment-2614128849 + version (DigitalMars) version (linux) version (X86) + private uint dummy; - static immutable Loc initial; /// use for default initialization of const ref Loc's + static immutable Loc initial; /// use for default initialization of Loc's extern (C++) __gshared bool showColumns; extern (C++) __gshared MessageStyle messageStyle; - __gshared Array!(const(char)*) filenames; - nothrow: /******************************* @@ -64,35 +60,34 @@ nothrow: this.messageStyle = messageStyle; } - extern (C++) this(const(char)* filename, uint linnum, uint charnum) @safe + /// Returns: a Loc that simply holds a filename, with no line / column info + extern (C++) static Loc singleFilename(const char* filename) { - this._linnum = linnum; - this._charnum = charnum; - this.filename = filename; + Loc result; + locFileTable ~= new BaseLoc(filename.toDString, null, locIndex, 0, [0]); + result.index = locIndex++; + return result; } /// utf8 code unit index relative to start of line, starting from 1 extern (C++) uint charnum() const @nogc @safe { - return _charnum; - } - - /// ditto - extern (C++) uint charnum(uint num) @nogc @safe - { - return _charnum = num; + return SourceLoc(this).column; } /// line number, starting from 1 - extern (C++) uint linnum() const @nogc @safe + extern (C++) uint linnum() const @nogc @trusted { - return _linnum; + return SourceLoc(this).line; } - /// ditto - extern (C++) uint linnum(uint num) @nogc @safe + /// Advance this location to the first column of the next line + void nextLine() { - return _linnum = num; + const i = fileTableIndex(this.index); + const j = locFileTable[i].getLineIndex(this.index - locFileTable[i].startIndex); + if (j + 1 < locFileTable[i].lines.length) + index = locFileTable[i].startIndex + locFileTable[i].lines[j + 1]; } /*** @@ -100,62 +95,39 @@ nothrow: */ extern (C++) const(char)* filename() const @nogc { - return fileIndex ? filenames[fileIndex - 1] : null; - } + if (this.index == 0) + return null; - /*** - * Set file name for this location - * Params: - * name = file name for location, null for no file name - */ - extern (C++) void filename(const(char)* name) @trusted - { - if (name) + const i = fileTableIndex(this.index); + if (locFileTable[i].substitutions.length > 0) { - //printf("setting %s\n", name); - filenames.push(name); - fileIndex = cast(uint)filenames.length; - assert(fileIndex, "internal compiler error: file name index overflow"); + const si = locFileTable[i].getSubstitutionIndex(this.index - locFileTable[i].startIndex); + const fname = locFileTable[i].substitutions[si].filename; + if (fname.length > 0) + return fname.ptr; } - else - fileIndex = 0; + + return locFileTable[i].filename.ptr; } extern (C++) const(char)* toChars( bool showColumns = Loc.showColumns, MessageStyle messageStyle = Loc.messageStyle) const nothrow { - OutBuffer buf; - if (fileIndex) - { - buf.writestring(filename); - } - if (linnum) - { - final switch (messageStyle) - { - case MessageStyle.digitalmars: - buf.writeByte('('); - buf.print(linnum); - if (showColumns && charnum) - { - buf.writeByte(','); - buf.print(charnum); - } - buf.writeByte(')'); - break; - case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html - buf.writeByte(':'); - buf.print(linnum); - if (showColumns && charnum) - { - buf.writeByte(':'); - buf.print(charnum); - } - break; - } - } - return buf.extractChars(); + return SourceLoc(this).toChars(showColumns, messageStyle); + } + + /// Returns: byte offset into source file + uint fileOffset() const + { + const i = fileTableIndex(this.index); + return this.index - locFileTable[i].startIndex; + } + + /// Returns: this location as a SourceLoc + extern (C++) SourceLoc toSourceLoc() const @nogc @safe + { + return SourceLoc(this); } /** @@ -165,11 +137,13 @@ nothrow: * - Uses case-insensitive comparison on Windows * - Ignores `charnum` if `Columns` is false. */ - extern (C++) bool equals(ref const(Loc) loc) const + extern (C++) bool equals(Loc loc) const { - return (!showColumns || charnum == loc.charnum) && - linnum == loc.linnum && - FileName.equals(filename, loc.filename); + SourceLoc lhs = SourceLoc(this); + SourceLoc rhs = SourceLoc(loc); + return (!showColumns || lhs.column == rhs.column) && + lhs.line == rhs.line && + FileName.equals(lhs.filename, rhs.filename); } /** @@ -182,23 +156,13 @@ nothrow: */ extern (D) bool opEquals(ref const(Loc) loc) const @trusted nothrow @nogc { - import core.stdc.string : strcmp; - - return charnum == loc.charnum && - linnum == loc.linnum && - (filename == loc.filename || - (filename && loc.filename && strcmp(filename, loc.filename) == 0)); + return this.index == loc.index; } /// ditto extern (D) size_t toHash() const @trusted nothrow { - import dmd.root.string : toDString; - - auto hash = hashOf(linnum); - hash = hashOf(charnum, hash); - hash = hashOf(filename.toDString, hash); - return hash; + return hashOf(this.index); } /****************** @@ -207,6 +171,302 @@ nothrow: */ bool isValid() const pure @safe { - return fileIndex != 0; + return this.index != 0; + } +} + +/** + * Format a source location for error messages + * + * Params: + * buf = buffer to write string into + * loc = source location to write + * showColumns = include column number in message + * messageStyle = select error message format + */ +void writeSourceLoc(ref OutBuffer buf, + SourceLoc loc, + bool showColumns, + MessageStyle messageStyle) nothrow +{ + if (loc.filename.length == 0) + return; + buf.writestring(loc.filename); + if (loc.line == 0) + return; + + final switch (messageStyle) + { + case MessageStyle.digitalmars: + buf.writeByte('('); + buf.print(loc.line); + if (showColumns && loc.column) + { + buf.writeByte(','); + buf.print(loc.column); + } + buf.writeByte(')'); + break; + case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html + buf.writeByte(':'); + buf.print(loc.line); + if (showColumns && loc.column) + { + buf.writeByte(':'); + buf.print(loc.column); + } + break; + case MessageStyle.sarif: // https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + // No formatting needed here for SARIF + break; } } + +/** + * Describes a location in the source code as a file + line number + column number + * + * While `Loc` is a compact opaque location meant to be stored in the AST, + * this struct has simple modifiable fields and is used for printing. + */ +struct SourceLoc +{ + const(char)[] filename; /// name of source file + uint line; /// line number (starts at 1) + uint column; /// column number (starts at 1) + uint fileOffset; /// byte index into file + + /// Index `fileOffset` into this to to obtain source code context of this location + const(char)[] fileContent; + + // aliases for backwards compatibility + alias linnum = line; + alias charnum = column; + + this(const(char)[] filename, uint line, uint column, uint fileOffset = 0, const(char)[] fileContent = null) nothrow @nogc pure @safe + { + this.filename = filename; + this.line = line; + this.column = column; + this.fileOffset = fileOffset; + this.fileContent = fileContent; + } + + this(Loc loc) nothrow @nogc @trusted + { + if (loc.index == 0 || locFileTable.length == 0) + return; + + const i = fileTableIndex(loc.index); + this = locFileTable[i].getSourceLoc(loc.index - locFileTable[i].startIndex); + } + + extern (C++) const(char)* toChars( + bool showColumns = Loc.showColumns, + MessageStyle messageStyle = Loc.messageStyle) const nothrow + { + OutBuffer buf; + writeSourceLoc(buf, this, showColumns, messageStyle); + return buf.extractChars(); + } + + bool opEquals(SourceLoc other) const nothrow + { + return this.filename == other.filename && this.line == other.line && this.column == other.column; + } + +} + +/// Given the `index` of a `Loc`, find the index in `locFileTable` of the corresponding `BaseLoc` +private size_t fileTableIndex(uint index) nothrow @nogc +{ + // To speed up linear find, we cache the last hit and compare that first, + // since usually we stay in the same file for some time when resolving source locations. + // If it's a different file now, either scan forwards / backwards + __gshared size_t lastI = 0; // index of last found hit + + size_t i = lastI; + if (index >= locFileTable[i].startIndex) + { + while (i + 1 < locFileTable.length && index >= locFileTable[i+1].startIndex) + i++; + } + else + { + while (index < locFileTable[i].startIndex) + i--; + } + + lastI = i; + return i; +} + +/** + * Create a new source location map for a file + * Params: + * filename = source file name + * fileContent = content of source file + * Returns: new BaseLoc + */ +BaseLoc* newBaseLoc(const(char)* filename, const(char)[] fileContent) nothrow +{ + locFileTable ~= new BaseLoc(filename.toDString, fileContent, locIndex, 1, [0]); + // Careful: the endloc of a FuncDeclaration can + // point to 1 past the very last byte in the file, so account for that + locIndex += fileContent.length + 1; + return locFileTable[$ - 1]; +} + +/** +Mapping from byte offset into source file to line/column numbers + +Consider this 4-line 24 byte source file: + +--- +app.d +1 struct S +2 { +3 int y; +4 } +--- + +Loc(0) is reserved for null locations, so the first `BaseLoc` gets `startIndex = 1` +and reserves 25 possible positions. Loc(1) represents the very start of this source +file, and every next byte gets the next `Loc`, up to Loc(25) which represents the +location right past the very last `}` character (hence it's 1 more than the file +size of 24, classic fence post problem!). + +The next source file will get `Loc(26) .. Loc(26 + fileSize + 1)` etc. + +Now say we know that `int y` has a `Loc(20)` and we want to know the line and column number. + +First we find the corresponding `BaseLoc` in `locFileTable`. Since 20 < 26, the first `BaseLoc` +contains this location. Since `startIndex = 1`, we subtract that to get a file offset 19. + +To get the line number from the file offset, we binary search into the `lines` array, +which contains file offsets where each line starts: + +`locFileTable[0].lines == [0, 9, 11, 22, 24]` + +We see 14 would be inserted right after `11` at `lines[2]`, so it's line 3 (+1 for 1-indexing). +Since line 3 starts at file offset 11, and `14 - 11 = 3`, it's column 4 (again, accounting for 1-indexing) + +#line and #file directives are handled with a separate array `substitutions` because they're rare, +and we don't want to penalize memory usage in their absence. +*/ +struct BaseLoc +{ +@safe nothrow: + + const(char)[] filename; /// Source file name + const(char)[] fileContents; /// Source file contents + uint startIndex; /// Subtract this from Loc.index to get file offset + int startLine = 1; /// Line number at index 0 + uint[] lines; /// For each line, the file offset at which it starts. At index 0 there's always a 0 entry. + BaseLoc[] substitutions; /// Substitutions from #line / #file directives + + /// Register that a new line starts at `offset` bytes from the start of the source file + void newLine(uint offset) + { + lines ~= offset; + } + + /// Construct a `Loc` entry for the start of the source file + `offset` bytes + Loc getLoc(uint offset) @nogc + { + Loc result; + result.index = startIndex + offset; + return result; + } + + /** + * Register a new file/line mapping from #file and #line directives + * Params: + * offset = byte offset in the source file at which the substitution starts + * filename = new filename from this point on (null = unchanged) + * line = line number from this point on + */ + void addSubstitution(uint offset, const(char)* filename, uint line) @system + { + auto fname = filename.toDString; + if (substitutions.length == 0) + substitutions ~= BaseLoc(this.filename, null, 0, 0); + + if (fname.length == 0) + fname = substitutions[$ - 1].filename; + substitutions ~= BaseLoc(fname, null, offset, cast(int) (line - lines.length + startLine - 2)); + } + + /// Returns: `loc` modified by substitutions from #file / #line directives + SourceLoc substitute(SourceLoc loc) @nogc + { + if (substitutions.length == 0) + return loc; + + const i = getSubstitutionIndex(loc.fileOffset); + if (substitutions[i].filename.length > 0) + loc.filename = substitutions[i].filename; + loc.linnum += substitutions[i].startLine; + return loc; + } + + /// Resolve an offset into this file to a filename + line + column + private SourceLoc getSourceLoc(uint offset) @nogc + { + const i = getLineIndex(offset); + const sl = SourceLoc(filename, cast(int) (i + startLine), cast(int) (1 + offset - lines[i]), offset, fileContents); + return substitute(sl); + } + + private size_t getSubstitutionIndex(uint offset) @nogc + { + size_t lo = 0; + size_t hi = substitutions.length + -1; + size_t mid = 0; + while (lo <= hi) + { + mid = lo + (hi - lo) / 2; + if (substitutions[mid].startIndex <= offset) + { + if (mid == substitutions.length - 1 || substitutions[mid + 1].startIndex > offset) + return mid; + + lo = mid + 1; + } + else + { + hi = mid - 1; + } + } + assert(0); + } + + /// Binary search the index in `this.lines` corresponding to `offset` + private size_t getLineIndex(uint offset) @nogc + { + size_t lo = 0; + size_t hi = lines.length + -1; + size_t mid = 0; + while (lo <= hi) + { + mid = lo + (hi - lo) / 2; + if (lines[mid] <= offset) + { + if (mid == lines.length - 1 || lines[mid + 1] > offset) + return mid; + + lo = mid + 1; + } + else + { + hi = mid - 1; + } + } + assert(0); + } +} + +// Whenever a new source file is parsed, start the `Loc` from this index (0 is reserved for Loc.init) +private __gshared uint locIndex = 1; + +// Global mapping of Loc indices to source file offset/line/column, see `BaseLoc` +private __gshared BaseLoc*[] locFileTable; diff --git a/gcc/d/dmd/mangle.h b/gcc/d/dmd/mangle.h index de6fa55..97875c5 100644 --- a/gcc/d/dmd/mangle.h +++ b/gcc/d/dmd/mangle.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/basicmangle.d b/gcc/d/dmd/mangle/basic.d index 52534fa..263dd5e 100644 --- a/gcc/d/dmd/basicmangle.d +++ b/gcc/d/dmd/mangle/basic.d @@ -1,13 +1,13 @@ /** * Defines the building blocks for creating the mangled names for basic types. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/basicmangle.d, _basicmangle.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/basic.d, _basicmangle.d) * Documentation: https://dlang.org/phobos/dmd_basicmangle.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/basicmangle.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/basic.d */ -module dmd.basicmangle; +module dmd.mangle.basic; import dmd.astenums; import dmd.common.outbuffer : OutBuffer; diff --git a/gcc/d/dmd/cppmangle.d b/gcc/d/dmd/mangle/cpp.d index 0116aa3..7e9f020 100644 --- a/gcc/d/dmd/cppmangle.d +++ b/gcc/d/dmd/mangle/cpp.d @@ -4,24 +4,20 @@ * This is the POSIX side of the implementation. * It exports two functions to C++, `toCppMangleItanium` and `cppTypeInfoMangleItanium`. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d, _cppmangle.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/cpp.d, _cppmangle.d) * Documentation: https://dlang.org/phobos/dmd_cppmangle.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cppmangle.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/cpp.d * * References: * Follows Itanium C++ ABI 1.86 section 5.1 * http://refspecs.linux-foundation.org/cxxabi-1.86.html#mangling * which is where the grammar comments come from. - * - * Bugs: - * https://issues.dlang.org/query.cgi - * enter `C++, mangling` as the keywords. */ -module dmd.cppmangle; +module dmd.mangle.cpp; import core.stdc.stdio; @@ -30,6 +26,7 @@ import dmd.astenums; import dmd.attrib; import dmd.declaration; import dmd.dsymbol; +import dmd.dsymbolsem : isGNUABITag; import dmd.dtemplate; import dmd.errors; import dmd.expression; @@ -49,19 +46,30 @@ import dmd.typesem; import dmd.visitor; -// helper to check if an identifier is a C++ operator -enum CppOperator { Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign, Unknown } -package CppOperator isCppOperator(Identifier id) +// C++ operators +enum CppOperator { Unknown, Cast, Assign, Eq, Index, Call, Unary, Binary, OpAssign } + +/************** + * Check if id is a C++ operator + * Params: + * id = identifier to be checked + * Returns: + * CppOperator, or Unknown if not a C++ operator + */ +package CppOperator isCppOperator(const scope Identifier id) { - __gshared const(Identifier)[] operators = null; - if (!operators) - operators = [Id._cast, Id.assign, Id.eq, Id.index, Id.call, Id.opUnary, Id.opBinary, Id.opOpAssign]; - foreach (i, op; operators) - { - if (op == id) - return cast(CppOperator)i; + with (Id) with (CppOperator) + { + return (id == opCast) ? Cast : + (id == opAssign) ? Assign : + (id == opEquals) ? Eq : + (id == opIndex) ? Index : + (id == opCall) ? Call : + (id == opUnary) ? Unary : + (id == opBinary) ? Binary : + (id == opOpAssign) ? OpAssign : + Unknown ; } - return CppOperator.Unknown; } /// @@ -71,6 +79,8 @@ const(char)* toCppMangleItanium(Dsymbol s) OutBuffer buf; scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); v.mangleOf(s); + if (v.errors) + fatal(); return buf.extractChars(); } @@ -82,6 +92,8 @@ const(char)* cppTypeInfoMangleItanium(Dsymbol s) buf.writestring("_ZTI"); // "TI" means typeinfo structure scope CppMangleVisitor v = new CppMangleVisitor(&buf, s.loc); v.cpp_mangle_name(s, false); + if (v.errors) + fatal(); return buf.extractChars(); } @@ -93,6 +105,8 @@ const(char)* cppThunkMangleItanium(FuncDeclaration fd, int offset) buf.printf("_ZThn%u_", offset); // "Th" means thunk, "n%u" is the call offset scope CppMangleVisitor v = new CppMangleVisitor(&buf, fd.loc); v.mangle_function_encoding(fd); + if (v.errors) + fatal(); return buf.extractChars(); } @@ -163,6 +177,7 @@ private final class CppMangleVisitor : Visitor Objects components; /// array of components available for substitution OutBuffer* buf; /// append the mangling to buf[] Loc loc; /// location for use in error messages + bool errors; /// failed to mangle properly /** * Constructor @@ -216,7 +231,7 @@ private final class CppMangleVisitor : Visitor // if so, just pick up the type from the instance if (!rt) rt = tf.nextOf(); - if (tf.isref) + if (tf.isRef) rt = rt.referenceTo(); auto prev = this.context.push(tf.nextOf()); scope (exit) this.context.pop(prev); @@ -420,10 +435,10 @@ private final class CppMangleVisitor : Visitor // 4. null pointer: std::nullptr_t (since C++11) if (t.ty == Tvoid || t.ty == Tbool) return true; - else if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) + if (t.ty == Tnull && global.params.cplusplus >= CppStdRevision.cpp11) return true; - else - return t.isTypeBasic() && (t.isintegral() || t.isreal()); + + return t.isTypeBasic() && (t.isIntegral() || t.isReal()); } /****************************** @@ -466,14 +481,14 @@ private final class CppMangleVisitor : Visitor else if (TemplateValueParameter tv = tp.isTemplateValueParameter()) { // <expr-primary> ::= L <type> <value number> E # integer literal - if (tv.valType.isintegral()) + if (tv.valType.isIntegral()) { Expression e = isExpression(o); assert(e); buf.writeByte('L'); tv.valType.accept(this); auto val = e.toUInteger(); - if (!tv.valType.isunsigned() && cast(sinteger_t)val < 0) + if (!tv.valType.isUnsigned() && cast(sinteger_t)val < 0) { val = -val; buf.writeByte('n'); @@ -484,7 +499,8 @@ private final class CppMangleVisitor : Visitor else { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti.kind, ti.toPrettyChars, tv.valType.toChars()); - fatal(); + errors = true; + return; } } else if (tp.isTemplateAliasParameter()) @@ -519,13 +535,13 @@ private final class CppMangleVisitor : Visitor else { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template alias parameter is not supported", ti.kind, ti.toPrettyChars, o.toChars()); - fatal(); + errors = true; } } else if (tp.isTemplateThisParameter()) { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template this parameter is not supported", ti.kind, ti.toPrettyChars, o.toChars()); - fatal(); + errors = true; } else { @@ -574,7 +590,8 @@ private final class CppMangleVisitor : Visitor if (t is null) { .error(ti.loc, "%s `%s` internal compiler error: C++ `%s` template value parameter is not supported", ti.kind, ti.toPrettyChars, (*ti.tiargs)[j].toChars()); - fatal(); + errors = true; + return false; } t.accept(this); } @@ -705,8 +722,7 @@ private final class CppMangleVisitor : Visitor */ static Dsymbol getInstance(Dsymbol s) { - Dsymbol p = s.toParent(); - if (p) + if (Dsymbol p = s.toParent()) { if (TemplateInstance ti = p.isTemplateInstance()) return ti; @@ -807,8 +823,7 @@ private final class CppMangleVisitor : Visitor return buf.writestring("St"); auto si = getInstance(s); - Dsymbol p = getQualifier(si); - if (p) + if (Dsymbol p = getQualifier(si)) { if (isStd(p)) { @@ -1012,7 +1027,8 @@ private final class CppMangleVisitor : Visitor if (!(d.storage_class & (STC.extern_ | STC.field | STC.gshared))) { .error(d.loc, "%s `%s` internal compiler error: C++ static non-`__gshared` non-`extern` variables not supported", d.kind, d.toPrettyChars); - fatal(); + errors = true; + return; } Dsymbol p = d.toParent(); if (p && !p.isModule()) //for example: char Namespace1::beta[6] should be mangled as "_ZN10Namespace14betaE" @@ -1086,13 +1102,13 @@ private final class CppMangleVisitor : Visitor buf.writestring(ctor.isCpCtor ? "C2" : "C1"); else if (d.isAggregateDtor()) buf.writestring("D1"); - else if (d.ident && d.ident == Id.assign) + else if (d.ident && d.ident == Id.opAssign) buf.writestring("aS"); - else if (d.ident && d.ident == Id.eq) + else if (d.ident && d.ident == Id.opEquals) buf.writestring("eq"); - else if (d.ident && d.ident == Id.index) + else if (d.ident && d.ident == Id.opIndex) buf.writestring("ix"); - else if (d.ident && d.ident == Id.call) + else if (d.ident && d.ident == Id.opCall) buf.writestring("cl"); else source_name(d, true); @@ -1348,7 +1364,8 @@ private final class CppMangleVisitor : Visitor // Static arrays in D are passed by value; no counterpart in C++ .error(loc, "internal compiler error: unable to pass static array `%s` to extern(C++) function, use pointer instead", t.toChars()); - fatal(); + errors = true; + return; } auto prev = this.context.push({ TypeFunction tf; @@ -1386,7 +1403,7 @@ private final class CppMangleVisitor : Visitor else p = ""; .error(loc, "internal compiler error: %stype `%s` cannot be mapped to C++\n", p, t.toChars()); - fatal(); //Fatal, because this error should be handled in frontend + errors = true; //Fatal, because this error should be handled in frontend } /**************************** @@ -1729,7 +1746,7 @@ extern(C++): * Ds char16_t * u <source-name> # vendor extended type */ - if (t.isimaginary() || t.iscomplex()) + if (t.isImaginary() || t.isComplex()) { // https://issues.dlang.org/show_bug.cgi?id=22806 // Complex and imaginary types are represented in the same way as @@ -1740,7 +1757,7 @@ extern(C++): append(t); CV_qualifiers(t); - if (t.isimaginary()) + if (t.isImaginary()) buf.writeByte('G'); // 'G' means imaginary else buf.writeByte('C'); // 'C' means complex @@ -1895,7 +1912,7 @@ extern(C++): if (t.linkage == LINK.c) buf.writeByte('Y'); Type tn = t.next; - if (t.isref) + if (t.isRef) tn = tn.referenceTo(); tn.accept(this); mangleFunctionParameters(t.parameterList); @@ -1922,21 +1939,21 @@ extern(C++): //printf("enum id = '%s'\n", id.toChars()); if (id == Id.__c_long) return writeBasicType(t, 0, 'l'); - else if (id == Id.__c_ulong) + if (id == Id.__c_ulong) return writeBasicType(t, 0, 'm'); - else if (id == Id.__c_char) + if (id == Id.__c_char) return writeBasicType(t, 0, 'c'); - else if (id == Id.__c_wchar_t) + if (id == Id.__c_wchar_t) return writeBasicType(t, 0, 'w'); - else if (id == Id.__c_longlong) + if (id == Id.__c_longlong) return writeBasicType(t, 0, 'x'); - else if (id == Id.__c_ulonglong) + if (id == Id.__c_ulonglong) return writeBasicType(t, 0, 'y'); - else if (id == Id.__c_complex_float) + if (id == Id.__c_complex_float) return Type.tcomplex32.accept(this); - else if (id == Id.__c_complex_double) + if (id == Id.__c_complex_double) return Type.tcomplex64.accept(this); - else if (id == Id.__c_complex_real) + if (id == Id.__c_complex_real) return Type.tcomplex80.accept(this); doSymbol(t); @@ -2175,7 +2192,7 @@ private extern(C++) final class ComponentVisitor : Visitor /// Set to the result of the comparison private bool result; - public this(RootObject base) @safe + public this(RootObject base) @trusted { switch (base.dyncast()) { @@ -2345,8 +2362,7 @@ private bool isNamespaceEqual (CPPNamespaceDeclaration a, Nspace b, size_t idx = // We need to see if there's more ident enclosing if (auto pb = b.toParent().isNspace()) return isNamespaceEqual(a.cppnamespace, pb); - else - return a.cppnamespace is null; + return a.cppnamespace is null; } /// Returns: @@ -2413,7 +2429,7 @@ private struct ABITagContainer foreach (exp; *s.userAttribDecl.atts) { - if (UserAttributeDeclaration.isGNUABITag(exp)) + if (isGNUABITag(exp)) return (*exp.isStructLiteralExp().elements)[0] .isArrayLiteralExp(); } diff --git a/gcc/d/dmd/dmangle.d b/gcc/d/dmd/mangle/package.d index 33428de..3ad2c7d 100644 --- a/gcc/d/dmd/dmangle.d +++ b/gcc/d/dmd/mangle/package.d @@ -3,16 +3,16 @@ * * Specification: $(LINK2 https://dlang.org/spec/abi.html#name_mangling, Name Mangling) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d, _dmangle.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mangle/package.d, _dmangle.d) * Documentation: https://dlang.org/phobos/dmd_dmangle.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmangle.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mangle/package.d * References: https://dlang.org/blog/2017/12/20/ds-newfangled-name-mangling/ */ -module dmd.dmangle; +module dmd.mangle; /****************************************************************************** @@ -70,14 +70,16 @@ void mangleToBuffer(TemplateInstance ti, ref OutBuffer buf) } /// Returns: `true` if the given character is a valid mangled character -package bool isValidMangling(dchar c) nothrow +package(dmd) bool isValidMangling(dchar c) nothrow { + import dmd.common.charactertables; + return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c != 0 && strchr("$%().:?@[]_", c) || - isUniAlpha(c); + isAnyIdentifierCharacter(c); } // valid mangled characters @@ -137,7 +139,7 @@ import core.stdc.string; import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; -import dmd.basicmangle; +import dmd.mangle.basic; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; @@ -147,6 +149,7 @@ import dmd.dtemplate; import dmd.errors; import dmd.expression; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; @@ -355,31 +358,31 @@ void mangleFuncType(TypeFunction t, TypeFunction ta, ubyte modMask, Type tret, r if (ta.purity) buf.writestring("Na"); - if (ta.isnothrow) + if (ta.isNothrow) buf.writestring("Nb"); - if (ta.isref) + if (ta.isRef) buf.writestring("Nc"); - if (ta.isproperty) + if (ta.isProperty) buf.writestring("Nd"); - if (ta.isnogc) + if (ta.isNogc) buf.writestring("Ni"); // `return scope` must be in that order - if (ta.isreturnscope && !ta.isreturninferred) + if (ta.isReturnScope && !ta.isReturnInferred) { buf.writestring("NjNl"); } else { // when return ref, the order is `scope return` - if (ta.isScopeQual && !ta.isscopeinferred) + if (ta.isScopeQual && !ta.isScopeInferred) buf.writestring("Nl"); - if (ta.isreturn && !ta.isreturninferred) + if (ta.isReturn && !ta.isReturnInferred) buf.writestring("Nj"); } - if (ta.islive) + if (ta.isLive) buf.writestring("Nm"); switch (ta.trust) @@ -448,7 +451,7 @@ void mangleParameter(Parameter p, ref OutBuffer buf, ref Backref backref) switch (stc & ((STC.IOR | STC.lazy_) & ~STC.constscoperef)) { - case 0: + case STC.none: break; case STC.in_: buf.writeByte('I'); @@ -827,7 +830,7 @@ public: continue; } // Now that we know it is not an alias, we MUST obtain a value - uint olderr = global.errors; + const olderr = global.errors; ea = ea.ctfeInterpret(); if (ea.op == EXP.error || olderr != global.errors) continue; diff --git a/gcc/d/dmd/module.h b/gcc/d/dmd/module.h index 379e8e6..e178014 100644 --- a/gcc/d/dmd/module.h +++ b/gcc/d/dmd/module.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -28,6 +28,14 @@ enum PKG PKGpackage // already determined that's an actual package }; +enum class Edition : unsigned char +{ + none = 0u, + legacy = 1u, + v2024 = 2u, + latest = 2u, +}; + class Package : public ScopeDsymbol { public: @@ -39,8 +47,6 @@ public: bool equals(const RootObject * const o) const override; - Package *isPackage() override final { return this; } - bool isAncestorPackageOf(const Package * const pkg) const; void accept(Visitor *v) override { v->visit(this); } @@ -75,6 +81,7 @@ public: FileType filetype; // source file type d_bool hasAlwaysInlines; // contains references to functions that must be inlined d_bool isPackageFile; // if it is a package.d + Edition edition; // language edition that this module is compiled with Package *pkg; // if isPackageFile is true, the Package that contains this package.d Strings contentImportedFiles; // array of files whose content was imported int needmoduleinfo; @@ -100,11 +107,9 @@ public: Modules aimports; // all imported modules - unsigned debuglevel; // debug level Identifiers *debugids; // debug identifiers Identifiers *debugidsNot; // forward referenced debug identifiers - unsigned versionlevel; // version level Identifiers *versionids; // version identifiers Identifiers *versionidsNot; // forward referenced version identifiers @@ -116,10 +121,10 @@ public: static Module* create(const char *arg, Identifier *ident, int doDocComment, int doHdrGen); static const char *find(const char *filename); - static Module *load(const Loc &loc, Identifiers *packages, Identifier *ident); + static Module *load(Loc loc, Identifiers *packages, Identifier *ident); const char *kind() const override; - bool read(const Loc &loc); // read file, returns 'true' if succeed, 'false' otherwise. + bool read(Loc loc); // read file, returns 'true' if succeed, 'false' otherwise. Module *parse(); // syntactic parse int needModuleInfo(); bool isPackageAccessible(Package *p, Visibility visibility, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all) override; @@ -151,7 +156,6 @@ public: void *ctfe_cov; // stores coverage information from ctfe - Module *isModule() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/mtype.d b/gcc/d/dmd/mtype.d index 715ee12..a3e1a8c 100644 --- a/gcc/d/dmd/mtype.d +++ b/gcc/d/dmd/mtype.d @@ -1,12 +1,12 @@ /** * Defines a D type. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mtype.d, _mtype.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mtype.d, _mtype.d) * Documentation: https://dlang.org/phobos/dmd_mtype.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mtype.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mtype.d */ module dmd.mtype; @@ -30,7 +30,7 @@ import dmd.dtemplate; import dmd.enumsem; import dmd.errors; import dmd.expression; -import dmd.funcsem; +import dmd.dsymbolsem : determineSize; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -58,6 +58,11 @@ static if (__VERSION__ < 2095) private alias StringValueType = StringValue!Type; } +private auto X(T, U)(T m, U n) +{ + return (m << 4) | n; +} + /*************************** * Return !=0 if modfrom can be implicitly converted to modto */ @@ -67,10 +72,6 @@ bool MODimplicitConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe return true; //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto); - auto X(T, U)(T m, U n) - { - return ((m << 4) | n); - } switch (X(modfrom & ~MODFlags.shared_, modto & ~MODFlags.shared_)) { @@ -98,11 +99,6 @@ MATCH MODmethodConv(MOD modfrom, MOD modto) pure nothrow @nogc @safe if (MODimplicitConv(modfrom, modto)) return MATCH.constant; - auto X(T, U)(T m, U n) - { - return ((m << 4) | n); - } - switch (X(modfrom, modto)) { case X(0, MODFlags.wild): @@ -236,9 +232,9 @@ unittest /************************************ * Convert MODxxxx to STCxxx */ -StorageClass ModToStc(uint mod) pure nothrow @nogc @safe +STC ModToStc(uint mod) pure nothrow @nogc @safe { - StorageClass stc = 0; + STC stc = STC.none; if (mod & MODFlags.immutable_) stc |= STC.immutable_; if (mod & MODFlags.const_) @@ -303,13 +299,16 @@ extern (C++) abstract class Type : ASTNode Type wcto; // MODFlags.wildconst Type swto; // MODFlags.shared_ | MODFlags.wild Type swcto; // MODFlags.shared_ | MODFlags.wildconst + Type pto; // merged pointer to this type + Type rto; // reference to this type + Type arrayof; // array of this type + + // ImportC: store the name of the typedef resolving to this type + // So `uint8_t x;` will be printed as `uint8_t x;` and not as the resolved `ubyte x;` + Identifier typedefIdent; } Mcache* mcache; - Type pto; // merged pointer to this type - Type rto; // reference to this type - Type arrayof; // array of this type - TypeInfoDeclaration vtinfo; // TypeInfo object for this Type void* ctype; // for back end @@ -520,7 +519,7 @@ extern (C++) abstract class Type : ASTNode static Type merge(Type t) { - import dmd.basicmangle : tyToDecoBuffer; + import dmd.mangle.basic : tyToDecoBuffer; OutBuffer buf; buf.reserve(3); @@ -533,12 +532,9 @@ extern (C++) abstract class Type : ASTNode auto sv = t.stringtable.update(buf[]); if (sv.value) return sv.value; - else - { - t.deco = cast(char*)sv.toDchars(); - sv.value = t; - return t; - } + t.deco = cast(char*)sv.toDchars(); + sv.value = t; + return t; } for (size_t i = 0; basetab[i] != Terror; i++) @@ -597,6 +593,14 @@ extern (C++) abstract class Type : ASTNode tsize_t = basic[isLP64 ? Tuns64 : Tuns32]; tptrdiff_t = basic[isLP64 ? Tint64 : Tint32]; thash_t = tsize_t; + + static if (__VERSION__ == 2081) + { + // Related issue: https://issues.dlang.org/show_bug.cgi?id=19134 + // D 2.081.x regressed initializing class objects at compile time. + // As a workaround initialize this global at run-time instead. + TypeTuple.empty = new TypeTuple(); + } } /** @@ -610,20 +614,9 @@ extern (C++) abstract class Type : ASTNode stringtable = stringtable.init; } - final uinteger_t size() - { - return size(Loc.initial); - } - - uinteger_t size(const ref Loc loc) - { - error(loc, "no size for type `%s`", toChars()); - return SIZE_INVALID; - } - uint alignsize() { - return cast(uint)size(Loc.initial); + return cast(uint)size(this, Loc.initial); } /********************************* @@ -649,43 +642,43 @@ extern (C++) abstract class Type : ASTNode return buf.extractChars(); } - bool isintegral() + bool isIntegral() { return false; } // real, imaginary, or complex - bool isfloating() + bool isFloating() { return false; } - bool isreal() + bool isReal() { return false; } - bool isimaginary() + bool isImaginary() { return false; } - bool iscomplex() + bool isComplex() { return false; } - bool isscalar() + bool isScalar() { return false; } - bool isunsigned() + bool isUnsigned() { return false; } - bool isscope() + bool isScopeClass() { return false; } @@ -713,7 +706,7 @@ extern (C++) abstract class Type : ASTNode */ bool isBoolean() { - return isscalar(); + return isScalar(); } final bool isConst() const nothrow pure @nogc @safe @@ -772,9 +765,6 @@ extern (C++) abstract class Type : ASTNode memcpy(cast(void*)t, cast(void*)this, sz); // t.mod = NULL; // leave mod unchanged t.deco = null; - t.arrayof = null; - t.pto = null; - t.rto = null; t.vtinfo = null; t.ctype = null; t.mcache = null; @@ -1118,70 +1108,71 @@ extern (C++) abstract class Type : ASTNode * Apply STCxxxx bits to existing type. * Use *before* semantic analysis is run. */ - extern (D) final Type addSTC(StorageClass stc) + extern (D) final Type addSTC(STC stc) { Type t = this; if (t.isImmutable()) { + return t; } else if (stc & STC.immutable_) { t = t.makeImmutable(); + return t; } - else + + if ((stc & STC.shared_) && !t.isShared()) { - if ((stc & STC.shared_) && !t.isShared()) + if (t.isWild()) + { + if (t.isConst()) + t = t.makeSharedWildConst(); + else + t = t.makeSharedWild(); + } + else + { + if (t.isConst()) + t = t.makeSharedConst(); + else + t = t.makeShared(); + } + } + if ((stc & STC.const_) && !t.isConst()) + { + if (t.isShared()) { if (t.isWild()) - { - if (t.isConst()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedWild(); - } + t = t.makeSharedWildConst(); else - { - if (t.isConst()) - t = t.makeSharedConst(); - else - t = t.makeShared(); - } + t = t.makeSharedConst(); } - if ((stc & STC.const_) && !t.isConst()) + else { - if (t.isShared()) - { - if (t.isWild()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedConst(); - } + if (t.isWild()) + t = t.makeWildConst(); else - { - if (t.isWild()) - t = t.makeWildConst(); - else - t = t.makeConst(); - } + t = t.makeConst(); } - if ((stc & STC.wild) && !t.isWild()) + } + if ((stc & STC.wild) && !t.isWild()) + { + if (t.isShared()) { - if (t.isShared()) - { - if (t.isConst()) - t = t.makeSharedWildConst(); - else - t = t.makeSharedWild(); - } + if (t.isConst()) + t = t.makeSharedWildConst(); else - { - if (t.isConst()) - t = t.makeWildConst(); - else - t = t.makeWild(); - } + t = t.makeSharedWild(); + } + else + { + if (t.isConst()) + t = t.makeWildConst(); + else + t = t.makeWild(); } } + return t; } @@ -1285,40 +1276,6 @@ extern (C++) abstract class Type : ASTNode return ((te = isTypeEnum()) !is null) ? te.toBasetype2() : this; } - /******************************** - * Determine if 'this' can be implicitly converted - * to type 'to'. - * Returns: - * MATCH.nomatch, MATCH.convert, MATCH.constant, MATCH.exact - */ - MATCH implicitConvTo(Type to) - { - //printf("Type::implicitConvTo(this=%p, to=%p)\n", this, to); - //printf("from: %s\n", toChars()); - //printf("to : %s\n", to.toChars()); - if (this.equals(to)) - return MATCH.exact; - return MATCH.nomatch; - } - - /******************************* - * Determine if converting 'this' to 'to' is an identity operation, - * a conversion to const operation, or the types aren't the same. - * Returns: - * MATCH.exact 'this' == 'to' - * MATCH.constant 'to' is const - * MATCH.nomatch conversion to mutable or invariant - */ - MATCH constConv(Type to) - { - //printf("Type::constConv(this = %s, to = %s)\n", toChars(), to.toChars()); - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - /*************************************** * Compute MOD bits matching `this` argument type to wild parameter type. * Params: @@ -1334,21 +1291,19 @@ extern (C++) abstract class Type : ASTNode { if (isImmutable()) return MODFlags.immutable_; - else if (isWildConst()) + if (isWildConst()) { if (t.isWildConst()) return MODFlags.wild; - else - return MODFlags.wildconst; + return MODFlags.wildconst; } - else if (isWild()) + if (isWild()) return MODFlags.wild; - else if (isConst()) + if (isConst()) return MODFlags.const_; - else if (isMutable()) + if (isMutable()) return MODFlags.mutable; - else - assert(0); + assert(0); } return 0; } @@ -1372,7 +1327,7 @@ extern (C++) abstract class Type : ASTNode * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. */ - Expression defaultInitLiteral(const ref Loc loc) + Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { @@ -1381,12 +1336,6 @@ extern (C++) abstract class Type : ASTNode return defaultInit(this, loc); } - // if initializer is 0 - bool isZeroInit(const ref Loc loc) - { - return false; // assume not - } - /*************************************** * Return !=0 if the type or any of its subtypes is wild. */ @@ -1412,7 +1361,7 @@ extern (C++) abstract class Type : ASTNode * Returns: * true if so */ - bool hasSystemFields() + bool hasUnsafeBitpatterns() { return false; } @@ -1446,33 +1395,6 @@ extern (C++) abstract class Type : ASTNode return t; } - /******************************************* - * Compute number of elements for a (possibly multidimensional) static array, - * or 1 for other types. - * Params: - * loc = for error message - * Returns: - * number of elements, uint.max on overflow - */ - final uint numberOfElems(const ref Loc loc) - { - //printf("Type::numberOfElems()\n"); - uinteger_t n = 1; - Type tb = this; - while ((tb = tb.toBasetype()).ty == Tsarray) - { - bool overflow = false; - n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); - if (overflow || n >= uint.max) - { - error(loc, "static array `%s` size overflowed to %llu", toChars(), cast(ulong)n); - return uint.max; - } - tb = (cast(TypeSArray)tb).next; - } - return cast(uint)n; - } - /**************************************** * Return the mask that an integral type will * fit into. @@ -1574,7 +1496,7 @@ extern (C++) abstract class Type : ASTNode } } - final pure inout nothrow @nogc @safe + final pure inout nothrow @nogc @trusted { inout(TypeError) isTypeError() { return ty == Terror ? cast(typeof(return))this : null; } inout(TypeVector) isTypeVector() { return ty == Tvector ? cast(typeof(return))this : null; } @@ -1599,6 +1521,8 @@ extern (C++) abstract class Type : ASTNode inout(TypeTraits) isTypeTraits() { return ty == Ttraits ? cast(typeof(return))this : null; } inout(TypeNoreturn) isTypeNoreturn() { return ty == Tnoreturn ? cast(typeof(return))this : null; } inout(TypeTag) isTypeTag() { return ty == Ttag ? cast(typeof(return))this : null; } + + extern (D) bool isStaticOrDynamicArray() const { return ty == Tarray || ty == Tsarray; } } override void accept(Visitor v) @@ -1648,12 +1572,7 @@ extern (C++) final class TypeError : Type return this; } - override uinteger_t size(const ref Loc loc) - { - return SIZE_INVALID; - } - - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { return ErrorExp.get(); } @@ -1687,10 +1606,10 @@ extern (C++) abstract class TypeNext : Type /******************************* * For TypeFunction, nextOf() can return NULL if the function return - * type is meant to be inferred, and semantic() hasn't yet ben run + * type is meant to be inferred, and semantic() hasn't yet been run * on the function. After semantic(), it must no longer be NULL. */ - override final Type nextOf() + override final Type nextOf() @safe { return next; } @@ -1890,35 +1809,6 @@ extern (C++) abstract class TypeNext : Type return t; } - override MATCH constConv(Type to) - { - //printf("TypeNext::constConv from = %s, to = %s\n", toChars(), to.toChars()); - if (equals(to)) - return MATCH.exact; - - if (!(ty == to.ty && MODimplicitConv(mod, to.mod))) - return MATCH.nomatch; - - Type tn = to.nextOf(); - if (!(tn && next.ty == tn.ty)) - return MATCH.nomatch; - - MATCH m; - if (to.isConst()) // whole tail const conversion - { - // Recursive shared level check - m = next.constConv(tn); - if (m == MATCH.exact) - m = MATCH.constant; - } - else - { - //printf("\tnext => %s, to.next => %s\n", next.toChars(), tn.toChars()); - m = next.equals(tn) ? MATCH.constant : MATCH.nomatch; - } - return m; - } - override final MOD deduceWild(Type t, bool isRef) { if (ty == Tfunction) @@ -2108,236 +1998,50 @@ extern (C++) final class TypeBasic : Type return this; } - override uinteger_t size(const ref Loc loc) - { - uint size; - //printf("TypeBasic::size()\n"); - switch (ty) - { - case Tint8: - case Tuns8: - size = 1; - break; - - case Tint16: - case Tuns16: - size = 2; - break; - - case Tint32: - case Tuns32: - case Tfloat32: - case Timaginary32: - size = 4; - break; - - case Tint64: - case Tuns64: - case Tfloat64: - case Timaginary64: - size = 8; - break; - - case Tfloat80: - case Timaginary80: - size = target.realsize; - break; - - case Tcomplex32: - size = 8; - break; - - case Tcomplex64: - case Tint128: - case Tuns128: - size = 16; - break; - - case Tcomplex80: - size = target.realsize * 2; - break; - - case Tvoid: - //size = Type::size(); // error message - size = 1; - break; - - case Tbool: - size = 1; - break; - - case Tchar: - size = 1; - break; - - case Twchar: - size = 2; - break; - - case Tdchar: - size = 4; - break; - - default: - assert(0); - } - //printf("TypeBasic::size() = %d\n", size); - return size; - } - override uint alignsize() { return target.alignsize(this); } - override bool isintegral() + override bool isIntegral() { - //printf("TypeBasic::isintegral('%s') x%x\n", toChars(), flags); + //printf("TypeBasic::isIntegral('%s') x%x\n", toChars(), flags); return (flags & TFlags.integral) != 0; } - override bool isfloating() + override bool isFloating() { return (flags & TFlags.floating) != 0; } - override bool isreal() + override bool isReal() { return (flags & TFlags.real_) != 0; } - override bool isimaginary() + override bool isImaginary() { return (flags & TFlags.imaginary) != 0; } - override bool iscomplex() + override bool isComplex() { return (flags & TFlags.complex) != 0; } - override bool isscalar() + override bool isScalar() { return (flags & (TFlags.integral | TFlags.floating)) != 0; } - override bool isunsigned() + override bool isUnsigned() { return (flags & TFlags.unsigned) != 0; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeBasic::implicitConvTo(%s) from %s\n", to.toChars(), toChars()); - if (this == to) - return MATCH.exact; - - if (ty == to.ty) - { - if (mod == to.mod) - return MATCH.exact; - else if (MODimplicitConv(mod, to.mod)) - return MATCH.constant; - else if (!((mod ^ to.mod) & MODFlags.shared_)) // for wild matching - return MATCH.constant; - else - return MATCH.convert; - } - - if (ty == Tvoid || to.ty == Tvoid) - return MATCH.nomatch; - if (to.ty == Tbool) - return MATCH.nomatch; - - TypeBasic tob; - if (to.ty == Tvector && to.deco) - { - TypeVector tv = cast(TypeVector)to; - tob = tv.elementType(); - } - else if (auto te = to.isTypeEnum()) - { - EnumDeclaration ed = te.sym; - if (ed.isSpecial()) - { - /* Special enums that allow implicit conversions to them - * with a MATCH.convert - */ - tob = to.toBasetype().isTypeBasic(); - } - else - return MATCH.nomatch; - } - else - tob = to.isTypeBasic(); - if (!tob) - return MATCH.nomatch; - - if (flags & TFlags.integral) - { - // Disallow implicit conversion of integers to imaginary or complex - if (tob.flags & (TFlags.imaginary | TFlags.complex)) - return MATCH.nomatch; - - // If converting from integral to integral - if (tob.flags & TFlags.integral) - { - const sz = size(Loc.initial); - const tosz = tob.size(Loc.initial); - - /* Can't convert to smaller size - */ - if (sz > tosz) - return MATCH.nomatch; - /* Can't change sign if same size - */ - //if (sz == tosz && (flags ^ tob.flags) & TFlags.unsigned) - // return MATCH.nomatch; - } - } - else if (flags & TFlags.floating) - { - // Disallow implicit conversion of floating point to integer - if (tob.flags & TFlags.integral) - return MATCH.nomatch; - - assert(tob.flags & TFlags.floating || to.ty == Tvector); - - // Disallow implicit conversion from complex to non-complex - if (flags & TFlags.complex && !(tob.flags & TFlags.complex)) - return MATCH.nomatch; - - // Disallow implicit conversion of real or imaginary to complex - if (flags & (TFlags.real_ | TFlags.imaginary) && tob.flags & TFlags.complex) - return MATCH.nomatch; - - // Disallow implicit conversion to-from real and imaginary - if ((flags & (TFlags.real_ | TFlags.imaginary)) != (tob.flags & (TFlags.real_ | TFlags.imaginary))) - return MATCH.nomatch; - } - return MATCH.convert; - } - - override bool isZeroInit(const ref Loc loc) + override bool hasUnsafeBitpatterns() { - switch (ty) - { - case Tchar: - case Twchar: - case Tdchar: - case Timaginary32: - case Timaginary64: - case Timaginary80: - case Tfloat32: - case Tfloat64: - case Tfloat80: - case Tcomplex32: - case Tcomplex64: - case Tcomplex80: - return false; // no - default: - return true; // yes - } + return ty == Tbool; } // For eliminating dynamic_cast @@ -2383,35 +2087,30 @@ extern (C++) final class TypeVector : Type return new TypeVector(basetype.syntaxCopy()); } - override uinteger_t size(const ref Loc loc) - { - return basetype.size(); - } - override uint alignsize() { return cast(uint)basetype.size(); } - override bool isintegral() + override bool isIntegral() { - //printf("TypeVector::isintegral('%s') x%x\n", toChars(), flags); - return basetype.nextOf().isintegral(); + //printf("TypeVector::isIntegral('%s') x%x\n", toChars(), flags); + return basetype.nextOf().isIntegral(); } - override bool isfloating() + override bool isFloating() { - return basetype.nextOf().isfloating(); + return basetype.nextOf().isFloating(); } - override bool isscalar() + override bool isScalar() { - return basetype.nextOf().isscalar(); + return basetype.nextOf().isScalar(); } - override bool isunsigned() + override bool isUnsigned() { - return basetype.nextOf().isunsigned(); + return basetype.nextOf().isUnsigned(); } override bool isBoolean() @@ -2419,30 +2118,7 @@ extern (C++) final class TypeVector : Type return false; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeVector::implicitConvTo(%s) from %s\n", to.toChars(), toChars()); - if (this == to) - return MATCH.exact; - if (to.ty != Tvector) - return MATCH.nomatch; - - TypeVector tv = cast(TypeVector)to; - assert(basetype.ty == Tsarray && tv.basetype.ty == Tsarray); - - // Can't convert to a vector which has different size. - if (basetype.size() != tv.basetype.size()) - return MATCH.nomatch; - - // Allow conversion to void[] - if (tv.basetype.nextOf().ty == Tvoid) - return MATCH.convert; - - // Otherwise implicitly convertible only if basetypes are. - return basetype.implicitConvTo(tv.basetype); - } - - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { //printf("TypeVector::defaultInitLiteral()\n"); assert(basetype.ty == Tsarray); @@ -2462,11 +2138,6 @@ extern (C++) final class TypeVector : Type return tb; } - override bool isZeroInit(const ref Loc loc) - { - return basetype.isZeroInit(loc); - } - override void accept(Visitor v) { v.visit(this); @@ -2532,22 +2203,6 @@ extern (C++) final class TypeSArray : TypeArray return dim.isIntegerExp() && dim.isIntegerExp().getInteger() == 0; } - override uinteger_t size(const ref Loc loc) - { - //printf("TypeSArray::size()\n"); - const n = numberOfElems(loc); - const elemsize = baseElemOf().size(loc); - bool overflow = false; - const sz = mulu(n, elemsize, overflow); - if (overflow || sz >= uint.max) - { - if (elemsize != SIZE_INVALID && n != uint.max) - error(loc, "static array `%s` size overflowed to %lld", toChars(), cast(long)sz); - return SIZE_INVALID; - } - return sz; - } - override uint alignsize() { return next.alignsize(); @@ -2559,86 +2214,12 @@ extern (C++) final class TypeSArray : TypeArray return nty.isSomeChar; } - override bool isZeroInit(const ref Loc loc) - { - return next.isZeroInit(loc); - } - override structalign_t alignment() { return next.alignment(); } - override MATCH constConv(Type to) - { - if (auto tsa = to.isTypeSArray()) - { - if (!dim.equals(tsa.dim)) - return MATCH.nomatch; - } - return TypeNext.constConv(to); - } - - override MATCH implicitConvTo(Type to) - { - //printf("TypeSArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); - if (auto ta = to.isTypeDArray()) - { - if (!MODimplicitConv(next.mod, ta.next.mod)) - return MATCH.nomatch; - - /* Allow conversion to void[] - */ - if (ta.next.ty == Tvoid) - { - return MATCH.convert; - } - - MATCH m = next.constConv(ta.next); - if (m > MATCH.nomatch) - { - return MATCH.convert; - } - return MATCH.nomatch; - } - if (auto tsa = to.isTypeSArray()) - { - if (this == to) - return MATCH.exact; - - if (dim.equals(tsa.dim)) - { - MATCH m = next.implicitConvTo(tsa.next); - - /* Allow conversion to non-interface base class. - */ - if (m == MATCH.convert && - next.ty == Tclass) - { - if (auto toc = tsa.next.isTypeClass) - { - if (!toc.sym.isInterfaceDeclaration) - return MATCH.convert; - } - } - - /* Since static arrays are value types, allow - * conversions from const elements to non-const - * ones, just like we allow conversion from const int - * to int. - */ - if (m >= MATCH.constant) - { - if (mod != to.mod) - m = MATCH.constant; - return m; - } - } - } - return MATCH.nomatch; - } - - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { @@ -2657,9 +2238,9 @@ extern (C++) final class TypeSArray : TypeArray return ae; } - override bool hasSystemFields() + override bool hasUnsafeBitpatterns() { - return next.hasSystemFields(); + return next.hasUnsafeBitpatterns(); } override bool hasVoidInitPointers() @@ -2723,12 +2304,6 @@ extern (C++) final class TypeDArray : TypeArray return result; } - override uinteger_t size(const ref Loc loc) - { - //printf("TypeDArray::size()\n"); - return target.ptrsize * 2; - } - override uint alignsize() { // A DArray consists of two ptr-sized values, so align it on pointer size @@ -2742,45 +2317,11 @@ extern (C++) final class TypeDArray : TypeArray return nty.isSomeChar; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeDArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); - if (equals(to)) - return MATCH.exact; - - if (auto ta = to.isTypeDArray()) - { - if (!MODimplicitConv(next.mod, ta.next.mod)) - return MATCH.nomatch; // not const-compatible - - /* Allow conversion to void[] - */ - if (next.ty != Tvoid && ta.next.ty == Tvoid) - { - return MATCH.convert; - } - - MATCH m = next.constConv(ta.next); - if (m > MATCH.nomatch) - { - if (m == MATCH.exact && mod != to.mod) - m = MATCH.constant; - return m; - } - } - return Type.implicitConvTo(to); - } - override void accept(Visitor v) { v.visit(this); @@ -2822,57 +2363,11 @@ extern (C++) final class TypeAArray : TypeArray return result; } - override uinteger_t size(const ref Loc loc) - { - return target.ptrsize; - } - - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeAArray::implicitConvTo(to = %s) this = %s\n", to.toChars(), toChars()); - if (equals(to)) - return MATCH.exact; - - if (auto ta = to.isTypeAArray()) - { - if (!MODimplicitConv(next.mod, ta.next.mod)) - return MATCH.nomatch; // not const-compatible - - if (!MODimplicitConv(index.mod, ta.index.mod)) - return MATCH.nomatch; // not const-compatible - - MATCH m = next.constConv(ta.next); - MATCH mi = index.constConv(ta.index); - if (m > MATCH.nomatch && mi > MATCH.nomatch) - { - return MODimplicitConv(mod, to.mod) ? MATCH.constant : MATCH.nomatch; - } - } - return Type.implicitConvTo(to); - } - - override MATCH constConv(Type to) - { - if (auto taa = to.isTypeAArray()) - { - MATCH mindex = index.constConv(taa.index); - MATCH mkey = next.constConv(taa.next); - // Pick the worst match - return mkey < mindex ? mkey : mindex; - } - return Type.constConv(to); - } - override void accept(Visitor v) { v.visit(this); @@ -2909,68 +2404,7 @@ extern (C++) final class TypePointer : TypeNext return result; } - override uinteger_t size(const ref Loc loc) - { - return target.ptrsize; - } - - override MATCH implicitConvTo(Type to) - { - //printf("TypePointer::implicitConvTo(to = %s) %s\n", to.toChars(), toChars()); - if (equals(to)) - return MATCH.exact; - - // Only convert between pointers - auto tp = to.isTypePointer(); - if (!tp) - return MATCH.nomatch; - - assert(this.next); - assert(tp.next); - - // Conversion to void* - if (tp.next.ty == Tvoid) - { - // Function pointer conversion doesn't check constness? - if (this.next.ty == Tfunction) - return MATCH.convert; - - if (!MODimplicitConv(next.mod, tp.next.mod)) - return MATCH.nomatch; // not const-compatible - - return this.next.ty == Tvoid ? MATCH.constant : MATCH.convert; - } - - // Conversion between function pointers - if (auto thisTf = this.next.isTypeFunction()) - return thisTf.implicitPointerConv(tp.next); - - // Default, no implicit conversion between the pointer targets - MATCH m = next.constConv(tp.next); - - if (m == MATCH.exact && mod != to.mod) - m = MATCH.constant; - return m; - } - - override MATCH constConv(Type to) - { - if (next.ty == Tfunction) - { - if (to.nextOf() && next.equals((cast(TypeNext)to).next)) - return Type.constConv(to); - else - return MATCH.nomatch; - } - return TypeNext.constConv(to); - } - - override bool isscalar() - { - return true; - } - - override bool isZeroInit(const ref Loc loc) + override bool isScalar() { return true; } @@ -3007,16 +2441,6 @@ extern (C++) final class TypeReference : TypeNext return result; } - override uinteger_t size(const ref Loc loc) - { - return target.ptrsize; - } - - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override void accept(Visitor v) { v.visit(this); @@ -3050,20 +2474,21 @@ extern (C++) final class TypeFunction : TypeNext // getters and setters are generated for them private extern (D) static struct BitFields { - bool isnothrow; /// nothrow - bool isnogc; /// is @nogc - bool isproperty; /// can be called without parentheses - bool isref; /// returns a reference - bool isreturn; /// 'this' is returned by ref + bool isNothrow; /// nothrow + bool isNogc; /// is @nogc + bool isProperty; /// can be called without parentheses + bool isRef; /// returns a reference + bool isReturn; /// 'this' is returned by ref bool isScopeQual; /// 'this' is scope - bool isreturninferred; /// 'this' is return from inference - bool isscopeinferred; /// 'this' is scope from inference - bool islive; /// is @live + bool isReturnInferred; /// 'this' is return from inference + bool isScopeInferred; /// 'this' is scope from inference + bool isLive; /// is @live bool incomplete; /// return type or default arguments removed bool isInOutParam; /// inout on the parameters bool isInOutQual; /// inout on the qualifier - bool isctor; /// the function is a constructor - bool isreturnscope; /// `this` is returned by value + bool isCtor; /// the function is a constructor + bool isReturnScope; /// `this` is returned by value + bool isRvalue; /// returned reference should be treated as rvalue } import dmd.common.bitfields : generateBitFields; @@ -3075,7 +2500,7 @@ extern (C++) final class TypeFunction : TypeNext byte inuse; ArgumentList inferenceArguments; // function arguments to determine `auto ref` in type semantic - extern (D) this(ParameterList pl, Type treturn, LINK linkage, StorageClass stc = 0) @safe + extern (D) this(ParameterList pl, Type treturn, LINK linkage, STC stc = STC.none) @safe { super(Tfunction, treturn); //if (!treturn) *(char*)0=0; @@ -3087,26 +2512,28 @@ extern (C++) final class TypeFunction : TypeNext if (stc & STC.pure_) this.purity = PURE.fwdref; if (stc & STC.nothrow_) - this.isnothrow = true; + this.isNothrow = true; if (stc & STC.nogc) - this.isnogc = true; + this.isNogc = true; if (stc & STC.property) - this.isproperty = true; + this.isProperty = true; if (stc & STC.live) - this.islive = true; + this.isLive = true; if (stc & STC.ref_) - this.isref = true; + this.isRef = true; if (stc & STC.return_) - this.isreturn = true; + this.isReturn = true; if (stc & STC.returnScope) - this.isreturnscope = true; + this.isReturnScope = true; if (stc & STC.returninferred) - this.isreturninferred = true; + this.isReturnInferred = true; if (stc & STC.scope_) this.isScopeQual = true; if (stc & STC.scopeinferred) - this.isscopeinferred = true; + this.isScopeInferred = true; + if (stc & STC.rvalue) + this.isRvalue = true; this.trust = TRUST.default_; if (stc & STC.safe) @@ -3117,9 +2544,9 @@ extern (C++) final class TypeFunction : TypeNext this.trust = TRUST.trusted; } - static TypeFunction create(Parameters* parameters, Type treturn, ubyte varargs, LINK linkage, StorageClass stc = 0) @safe + static TypeFunction create(Parameters* parameters, Type treturn, ubyte varargs, LINK linkage, StorageClass stc = STC.none) @safe { - return new TypeFunction(ParameterList(parameters, cast(VarArg)varargs), treturn, linkage, stc); + return new TypeFunction(ParameterList(parameters, cast(VarArg)varargs), treturn, linkage, cast(STC) stc); } override const(char)* kind() const @@ -3132,22 +2559,23 @@ extern (C++) final class TypeFunction : TypeNext Type treturn = next ? next.syntaxCopy() : null; auto t = new TypeFunction(parameterList.syntaxCopy(), treturn, linkage); t.mod = mod; - t.isnothrow = isnothrow; - t.isnogc = isnogc; - t.islive = islive; + t.isNothrow = isNothrow; + t.isNogc = isNogc; + t.isLive = isLive; t.purity = purity; - t.isproperty = isproperty; - t.isref = isref; - t.isreturn = isreturn; - t.isreturnscope = isreturnscope; + t.isProperty = isProperty; + t.isRef = isRef; + t.isReturn = isReturn; + t.isReturnScope = isReturnScope; t.isScopeQual = isScopeQual; - t.isreturninferred = isreturninferred; - t.isscopeinferred = isscopeinferred; + t.isReturnInferred = isReturnInferred; + t.isScopeInferred = isScopeInferred; + t.isRvalue = isRvalue; t.isInOutParam = isInOutParam; t.isInOutQual = isInOutQual; t.trust = trust; t.inferenceArguments = inferenceArguments; - t.isctor = isctor; + t.isCtor = isCtor; return t; } @@ -3170,18 +2598,25 @@ extern (C++) final class TypeFunction : TypeNext * Returns: * true if D-style variadic */ - bool isDstyleVariadic() const pure nothrow + bool isDstyleVariadic() const pure nothrow @safe { return linkage == LINK.d && parameterList.varargs == VarArg.variadic; } - extern(D) static const(char)* getMatchError(A...)(const(char)* format, A args) + /********************************* + * Append error message to buf. + * Input: + * buf = message sink + * format = printf format + */ + extern(C) static void getMatchError(ref OutBuffer buf, const(char)* format, ...) { if (global.gag && !global.params.v.showGaggedErrors) - return null; - OutBuffer buf; - buf.printf(format, args); - return buf.extractChars(); + return; + va_list ap; + va_start(ap, format); + buf.vprintf(format, ap); + va_end(ap); } /******************************** @@ -3190,10 +2625,10 @@ extern (C++) final class TypeFunction : TypeNext * * Params: * argumentList = array of function arguments - * pMessage = address to store error message, or `null` + * buf = if not null, append error message to it * Returns: re-ordered argument list, or `null` on error */ - extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, const(char)** pMessage) + extern(D) Expressions* resolveNamedArgs(ArgumentList argumentList, OutBuffer* buf) { Expression[] args = argumentList.arguments ? (*argumentList.arguments)[] : null; Identifier[] names = argumentList.names ? (*argumentList.names)[] : null; @@ -3217,8 +2652,8 @@ extern (C++) final class TypeFunction : TypeNext const pi = findParameterIndex(name); if (pi == -1) { - if (pMessage) - *pMessage = getMatchError("no parameter named `%s`", name.toChars()); + if (buf) + getMatchError(*buf, "no parameter named `%s`", name.toChars()); return null; } ci = pi; @@ -3228,8 +2663,8 @@ extern (C++) final class TypeFunction : TypeNext if (!isVariadic) { // Without named args, let the caller diagnose argument overflow - if (hasNamedArgs && pMessage) - *pMessage = getMatchError("argument `%s` goes past end of parameter list", arg.toChars()); + if (hasNamedArgs && buf) + getMatchError(*buf, "argument `%s` goes past end of parameter list", arg.toChars()); return null; } while (ci >= newArgs.length) @@ -3238,8 +2673,8 @@ extern (C++) final class TypeFunction : TypeNext if ((*newArgs)[ci]) { - if (pMessage) - *pMessage = getMatchError("parameter `%s` assigned twice", parameterList[ci].toChars()); + if (buf) + getMatchError(*buf, "parameter `%s` assigned twice", parameterList[ci].toChars()); return null; } (*newArgs)[ci++] = arg; @@ -3257,8 +2692,8 @@ extern (C++) final class TypeFunction : TypeNext if (this.incomplete) continue; - if (pMessage) - *pMessage = getMatchError("missing argument for parameter #%d: `%s`", + if (buf) + getMatchError(*buf, "missing argument for parameter #%d: `%s`", i + 1, parameterToChars(parameterList[i], this, false)); return null; } @@ -3272,87 +2707,6 @@ extern (C++) final class TypeFunction : TypeNext return newArgs; } - /+ - + Checks whether this function type is convertible to ` to` - + when used in a function pointer / delegate. - + - + Params: - + to = target type - + - + Returns: - + MATCH.nomatch: `to` is not a covaraint function - + MATCH.convert: `to` is a covaraint function - + MATCH.exact: `to` is identical to this function - +/ - private MATCH implicitPointerConv(Type to) - { - assert(to); - - if (this.equals(to)) - return MATCH.constant; - - if (this.covariant(to) == Covariant.yes) - { - Type tret = this.nextOf(); - Type toret = to.nextOf(); - if (tret.ty == Tclass && toret.ty == Tclass) - { - /* https://issues.dlang.org/show_bug.cgi?id=10219 - * Check covariant interface return with offset tweaking. - * interface I {} - * class C : Object, I {} - * I function() dg = function C() {} // should be error - */ - int offset = 0; - if (toret.isBaseOf(tret, &offset) && offset != 0) - return MATCH.nomatch; - } - return MATCH.convert; - } - - return MATCH.nomatch; - } - - /** Extends TypeNext.constConv by also checking for matching attributes **/ - override MATCH constConv(Type to) - { - // Attributes need to match exactly, otherwise it's an implicit conversion - if (this.ty != to.ty || !this.attributesEqual(cast(TypeFunction) to)) - return MATCH.nomatch; - - return super.constConv(to); - } - - extern (D) bool checkRetType(const ref Loc loc) - { - Type tb = next.toBasetype(); - if (tb.ty == Tfunction) - { - error(loc, "functions cannot return a function"); - next = Type.terror; - } - if (tb.ty == Ttuple) - { - error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)"); - next = Type.terror; - } - if (!isref && (tb.ty == Tstruct || tb.ty == Tsarray)) - { - if (auto ts = tb.baseElemOf().isTypeStruct()) - { - if (!ts.sym.members) - { - error(loc, "functions cannot return opaque type `%s` by value", tb.toChars()); - next = Type.terror; - } - } - } - if (tb.ty == Terror) - return true; - return false; - } - - /// Returns: `true` the function is `isInOutQual` or `isInOutParam` ,`false` otherwise. bool iswild() const pure nothrow @safe @nogc { @@ -3373,9 +2727,9 @@ extern (C++) final class TypeFunction : TypeNext return (this.trust == other.trust || (trustSystemEqualsDefault && this.trust <= TRUST.system && other.trust <= TRUST.system)) && this.purity == other.purity && - this.isnothrow == other.isnothrow && - this.isnogc == other.isnogc && - this.islive == other.islive; + this.isNothrow == other.isNothrow && + this.isNogc == other.isNogc && + this.isLive == other.isLive; } override void accept(Visitor v) @@ -3434,44 +2788,11 @@ extern (C++) final class TypeDelegate : TypeNext return result; } - override uinteger_t size(const ref Loc loc) - { - return target.ptrsize * 2; - } - override uint alignsize() { return target.ptrsize; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeDelegate.implicitConvTo(this=%p, to=%p)\n", this, to); - //printf("from: %s\n", toChars()); - //printf("to : %s\n", to.toChars()); - if (this.equals(to)) - return MATCH.exact; - - if (auto toDg = to.isTypeDelegate()) - { - MATCH m = this.next.isTypeFunction().implicitPointerConv(toDg.next); - - // Retain the old behaviour for this refactoring - // Should probably be changed to constant to match function pointers - if (m > MATCH.convert) - m = MATCH.convert; - - return m; - } - - return MATCH.nomatch; - } - - override bool isZeroInit(const ref Loc loc) - { - return true; - } - override bool isBoolean() { return true; @@ -3487,7 +2808,7 @@ extern (C++) final class TypeDelegate : TypeNext * This is a shell containing a TraitsExp that can be * either resolved to a type or to a symbol. * - * The point is to allow AliasDeclarationY to use `__traits()`, see https://issues.dlang.org/show_bug.cgi?id=7804. + * The point is to allow AliasDeclarationY to use `__traits()`, see $(LINK https://issues.dlang.org/show_bug.cgi?id=7804). */ extern (C++) final class TypeTraits : Type { @@ -3497,7 +2818,7 @@ extern (C++) final class TypeTraits : Type /// Cached type/symbol after semantic analysis. RootObject obj; - final extern (D) this(const ref Loc loc, TraitsExp exp) @safe + final extern (D) this(Loc loc, TraitsExp exp) @safe { super(Ttraits); this.loc = loc; @@ -3521,11 +2842,6 @@ extern (C++) final class TypeTraits : Type { v.visit(this); } - - override uinteger_t size(const ref Loc loc) - { - return SIZE_INVALID; - } } /****** @@ -3539,7 +2855,7 @@ extern (C++) final class TypeMixin : Type Expressions* exps; RootObject obj; // cached result of semantic analysis. - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(Tmixin); this.loc = loc; @@ -3635,12 +2951,6 @@ extern (C++) abstract class TypeQualified : Type idents.push(e); } - override uinteger_t size(const ref Loc loc) - { - error(this.loc, "size of type `%s` is not known", toChars()); - return SIZE_INVALID; - } - override void accept(Visitor v) { v.visit(this); @@ -3653,16 +2963,13 @@ extern (C++) final class TypeIdentifier : TypeQualified { Identifier ident; - // The symbol representing this identifier, before alias resolution - Dsymbol originalSymbol; - - extern (D) this(const ref Loc loc, Identifier ident) + extern (D) this(Loc loc, Identifier ident) { super(Tident, loc); this.ident = ident; } - static TypeIdentifier create(const ref Loc loc, Identifier ident) + static TypeIdentifier create(Loc loc, Identifier ident) { return new TypeIdentifier(loc, ident); } @@ -3693,7 +3000,7 @@ extern (C++) final class TypeInstance : TypeQualified { TemplateInstance tempinst; - extern (D) this(const ref Loc loc, TemplateInstance tempinst) + extern (D) this(Loc loc, TemplateInstance tempinst) { super(Tinstance, loc); this.tempinst = tempinst; @@ -3726,7 +3033,7 @@ extern (C++) final class TypeTypeof : TypeQualified Expression exp; int inuse; - extern (D) this(const ref Loc loc, Expression exp) + extern (D) this(Loc loc, Expression exp) { super(Ttypeof, loc); this.exp = exp; @@ -3746,14 +3053,6 @@ extern (C++) final class TypeTypeof : TypeQualified return t; } - override uinteger_t size(const ref Loc loc) - { - if (exp.type) - return exp.type.size(loc); - else - return TypeQualified.size(loc); - } - override void accept(Visitor v) { v.visit(this); @@ -3764,7 +3063,7 @@ extern (C++) final class TypeTypeof : TypeQualified */ extern (C++) final class TypeReturn : TypeQualified { - extern (D) this(const ref Loc loc) + extern (D) this(Loc loc) { super(Treturn, loc); } @@ -3812,11 +3111,6 @@ extern (C++) final class TypeStruct : Type return "struct"; } - override uinteger_t size(const ref Loc loc) - { - return sym.size(loc); - } - override uint alignsize() { sym.size(Loc.initial); // give error for forward references @@ -3839,7 +3133,7 @@ extern (C++) final class TypeStruct : Type * Use when we prefer the default initializer to be a literal, * rather than a global immutable variable. */ - override Expression defaultInitLiteral(const ref Loc loc) + override Expression defaultInitLiteral(Loc loc) { static if (LOGDEFAULTINIT) { @@ -3882,20 +3176,13 @@ extern (C++) final class TypeStruct : Type /* Copy from the initializer symbol for larger symbols, * otherwise the literals expressed as code get excessively large. */ - if (size(loc) > target.ptrsize * 4 && !needsNested()) + if (size(this, loc) > target.ptrsize * 4 && !needsNested()) structinit.useStaticInit = true; structinit.type = this; return structinit; } - override bool isZeroInit(const ref Loc loc) - { - // Determine zeroInit here, as this can be called before semantic2 - sym.determineSize(sym.loc); - return sym.zeroInit; - } - override bool isAssignable() { bool assignable = true; @@ -3976,11 +3263,11 @@ extern (C++) final class TypeStruct : Type return sym.hasVoidInitPointers; } - override bool hasSystemFields() + override bool hasUnsafeBitpatterns() { sym.size(Loc.initial); // give error for forward references sym.determineTypeProperties(); - return sym.hasSystemFields; + return sym.hasUnsafeBitpatterns; } override bool hasInvariant() @@ -3990,97 +3277,6 @@ extern (C++) final class TypeStruct : Type return sym.hasInvariant() || sym.hasFieldWithInvariant; } - extern (D) MATCH implicitConvToWithoutAliasThis(Type to) - { - MATCH m; - - if (ty == to.ty && sym == (cast(TypeStruct)to).sym) - { - m = MATCH.exact; // exact match - if (mod != to.mod) - { - m = MATCH.constant; - if (MODimplicitConv(mod, to.mod)) - { - } - else - { - /* Check all the fields. If they can all be converted, - * allow the conversion. - */ - uint offset = ~0; // dead-store to prevent spurious warning - for (size_t i = 0; i < sym.fields.length; i++) - { - VarDeclaration v = sym.fields[i]; - if (i == 0) - { - } - else if (v.offset == offset) - { - if (m > MATCH.nomatch) - continue; - } - else - { - if (m == MATCH.nomatch) - return m; - } - - // 'from' type - Type tvf = v.type.addMod(mod); - - // 'to' type - Type tv = v.type.addMod(to.mod); - - // field match - MATCH mf = tvf.implicitConvTo(tv); - //printf("\t%s => %s, match = %d\n", v.type.toChars(), tv.toChars(), mf); - - if (mf == MATCH.nomatch) - return mf; - if (mf < m) // if field match is worse - m = mf; - offset = v.offset; - } - } - } - } - return m; - } - - extern (D) MATCH implicitConvToThroughAliasThis(Type to) - { - MATCH m; - if (!(ty == to.ty && sym == (cast(TypeStruct)to).sym) && sym.aliasthis && !(att & AliasThisRec.tracing)) - { - if (auto ato = aliasthisOf(this)) - { - att = cast(AliasThisRec)(att | AliasThisRec.tracing); - m = ato.implicitConvTo(to); - att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); - } - else - m = MATCH.nomatch; // no match - } - return m; - } - - override MATCH implicitConvTo(Type to) - { - //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to.toChars()); - MATCH m = implicitConvToWithoutAliasThis(to); - return m ? m : implicitConvToThroughAliasThis(to); - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeStruct)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - override MOD deduceWild(Type t, bool isRef) { if (ty == t.ty && sym == (cast(TypeStruct)t).sym) @@ -4129,11 +3325,6 @@ extern (C++) final class TypeEnum : Type return this; } - override uinteger_t size(const ref Loc loc) - { - return sym.getMemtype(loc).size(loc); - } - Type memType() { return sym.getMemtype(Loc.initial); @@ -4147,39 +3338,39 @@ extern (C++) final class TypeEnum : Type return t.alignsize(); } - override bool isintegral() + override bool isIntegral() { - return memType().isintegral(); + return memType().isIntegral(); } - override bool isfloating() + override bool isFloating() { - return memType().isfloating(); + return memType().isFloating(); } - override bool isreal() + override bool isReal() { - return memType().isreal(); + return memType().isReal(); } - override bool isimaginary() + override bool isImaginary() { - return memType().isimaginary(); + return memType().isImaginary(); } - override bool iscomplex() + override bool isComplex() { - return memType().iscomplex(); + return memType().isComplex(); } - override bool isscalar() + override bool isScalar() { - return memType().isscalar(); + return memType().isScalar(); } - override bool isunsigned() + override bool isUnsigned() { - return memType().isunsigned(); + return memType().isUnsigned(); } override bool isBoolean() @@ -4212,28 +3403,6 @@ extern (C++) final class TypeEnum : Type return memType().needsNested(); } - override MATCH implicitConvTo(Type to) - { - MATCH m; - //printf("TypeEnum::implicitConvTo() %s to %s\n", toChars(), to.toChars()); - if (ty == to.ty && sym == (cast(TypeEnum)to).sym) - m = (mod == to.mod) ? MATCH.exact : MATCH.constant; - else if (sym.getMemtype(Loc.initial).implicitConvTo(to)) - m = MATCH.convert; // match with conversions - else - m = MATCH.nomatch; // no match - return m; - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeEnum)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - return MATCH.nomatch; - } - extern (D) Type toBasetype2() { if (!sym.members && !sym.memtype) @@ -4242,19 +3411,14 @@ extern (C++) final class TypeEnum : Type return tb.castMod(mod); // retain modifier bits from 'this' } - override bool isZeroInit(const ref Loc loc) - { - return sym.getDefaultValue(loc).toBool().hasValue(false); - } - override bool hasVoidInitPointers() { return memType().hasVoidInitPointers(); } - override bool hasSystemFields() + override bool hasUnsafeBitpatterns() { - return memType().hasSystemFields(); + return memType().hasUnsafeBitpatterns(); } override bool hasInvariant() @@ -4292,11 +3456,6 @@ extern (C++) final class TypeClass : Type return "class"; } - override uinteger_t size(const ref Loc loc) - { - return target.ptrsize; - } - override TypeClass syntaxCopy() { return this; @@ -4307,65 +3466,6 @@ extern (C++) final class TypeClass : Type return sym; } - extern (D) MATCH implicitConvToWithoutAliasThis(Type to) - { - ClassDeclaration cdto = to.isClassHandle(); - MATCH m = constConv(to); - if (m > MATCH.nomatch) - return m; - - if (cdto && cdto.isBaseOf(sym, null) && MODimplicitConv(mod, to.mod)) - { - //printf("'to' is base\n"); - return MATCH.convert; - } - return MATCH.nomatch; - } - - extern (D) MATCH implicitConvToThroughAliasThis(Type to) - { - MATCH m; - if (sym.aliasthis && !(att & AliasThisRec.tracing)) - { - if (auto ato = aliasthisOf(this)) - { - att = cast(AliasThisRec)(att | AliasThisRec.tracing); - m = ato.implicitConvTo(to); - att = cast(AliasThisRec)(att & ~AliasThisRec.tracing); - } - } - return m; - } - - override MATCH implicitConvTo(Type to) - { - //printf("TypeClass::implicitConvTo(to = '%s') %s\n", to.toChars(), toChars()); - MATCH m = implicitConvToWithoutAliasThis(to); - return m ? m : implicitConvToThroughAliasThis(to); - } - - override MATCH constConv(Type to) - { - if (equals(to)) - return MATCH.exact; - if (ty == to.ty && sym == (cast(TypeClass)to).sym && MODimplicitConv(mod, to.mod)) - return MATCH.constant; - - /* Conversion derived to const(base) - */ - int offset = 0; - if (to.isBaseOf(this, &offset) && offset == 0 && MODimplicitConv(mod, to.mod)) - { - // Disallow: - // derived to base - // inout(derived) to inout(base) - if (!to.isMutable() && !to.isWild()) - return MATCH.convert; - } - - return MATCH.nomatch; - } - override MOD deduceWild(Type t, bool isRef) { ClassDeclaration cd = t.isClassHandle(); @@ -4387,12 +3487,7 @@ extern (C++) final class TypeClass : Type return wm; } - override bool isZeroInit(const ref Loc loc) - { - return true; - } - - override bool isscope() + override bool isScopeClass() { return sym.stack; } @@ -4413,7 +3508,10 @@ extern (C++) final class TypeClass : Type extern (C++) final class TypeTuple : Type { // 'logically immutable' cached global - don't modify! - __gshared TypeTuple empty = new TypeTuple(); + static if (__VERSION__ == 2081) + __gshared TypeTuple empty; // See comment in Type._init + else + __gshared TypeTuple empty = new TypeTuple(); Parameters* arguments; // types making up the tuple @@ -4443,17 +3541,19 @@ extern (C++) final class TypeTuple : Type extern (D) this(Expressions* exps) { super(Ttuple); - auto arguments = new Parameters(exps ? exps.length : 0); - if (exps) + if (!exps) { - for (size_t i = 0; i < exps.length; i++) - { - Expression e = (*exps)[i]; - if (e.type.ty == Ttuple) - error(e.loc, "cannot form sequence of sequences"); - auto arg = new Parameter(e.loc, STC.undefined_, e.type, null, null, null); - (*arguments)[i] = arg; - } + this.arguments = new Parameters(0); + return; + } + auto arguments = new Parameters(exps.length); + + for (size_t i = 0; i < exps.length; i++) + { + Expression e = (*exps)[i]; + assert(e.type.ty != Ttuple); + auto arg = new Parameter(e.loc, STC.none, e.type, null, null, null); + (*arguments)[i] = arg; } this.arguments = arguments; //printf("TypeTuple() %p, %s\n", this, toChars()); @@ -4477,15 +3577,15 @@ extern (C++) final class TypeTuple : Type { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(Loc.initial, 0, t1, null, null, null)); + arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null)); } extern (D) this(Type t1, Type t2) { super(Ttuple); arguments = new Parameters(); - arguments.push(new Parameter(Loc.initial, 0, t1, null, null, null)); - arguments.push(new Parameter(Loc.initial, 0, t2, null, null, null)); + arguments.push(new Parameter(Loc.initial, STC.none, t1, null, null, null)); + arguments.push(new Parameter(Loc.initial, STC.none, t2, null, null, null)); } static TypeTuple create() @safe @@ -4539,29 +3639,6 @@ extern (C++) final class TypeTuple : Type return false; } - override MATCH implicitConvTo(Type to) - { - if (this == to) - return MATCH.exact; - if (auto tt = to.isTypeTuple()) - { - if (arguments.length == tt.arguments.length) - { - MATCH m = MATCH.exact; - for (size_t i = 0; i < tt.arguments.length; i++) - { - Parameter arg1 = (*arguments)[i]; - Parameter arg2 = (*tt.arguments)[i]; - MATCH mi = arg1.type.implicitConvTo(arg2.type); - if (mi < m) - m = mi; - } - return m; - } - } - return MATCH.nomatch; - } - override void accept(Visitor v) { v.visit(this); @@ -4623,36 +3700,11 @@ extern (C++) final class TypeNull : Type return this; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeNull::implicitConvTo(this=%p, to=%p)\n", this, to); - //printf("from: %s\n", toChars()); - //printf("to : %s\n", to.toChars()); - MATCH m = Type.implicitConvTo(to); - if (m != MATCH.nomatch) - return m; - - // NULL implicitly converts to any pointer type or dynamic array - //if (type.ty == Tpointer && type.nextOf().ty == Tvoid) - { - Type tb = to.toBasetype(); - if (tb.ty == Tnull || tb.ty == Tpointer || tb.ty == Tarray || tb.ty == Taarray || tb.ty == Tclass || tb.ty == Tdelegate) - return MATCH.constant; - } - - return MATCH.nomatch; - } - override bool isBoolean() { return true; } - override uinteger_t size(const ref Loc loc) - { - return tvoidptr.size(loc); - } - override void accept(Visitor v) { v.visit(this); @@ -4680,38 +3732,11 @@ extern (C++) final class TypeNoreturn : Type return this; } - override MATCH implicitConvTo(Type to) - { - //printf("TypeNoreturn::implicitConvTo(this=%p, to=%p)\n", this, to); - //printf("from: %s\n", toChars()); - //printf("to : %s\n", to.toChars()); - if (this.equals(to)) - return MATCH.exact; - - // Different qualifiers? - if (to.ty == Tnoreturn) - return MATCH.constant; - - // Implicitly convertible to any type - return MATCH.convert; - } - - override MATCH constConv(Type to) - { - // Either another noreturn or conversion to any type - return this.implicitConvTo(to); - } - override bool isBoolean() { return true; // bottom type can be implicitly converted to any other type } - override uinteger_t size(const ref Loc loc) - { - return 0; - } - override uint alignsize() { return 0; @@ -4745,7 +3770,7 @@ extern (C++) final class TypeTag : Type /// struct S { int a; } s1, *s2; MOD mod; /// modifiers to apply after type is resolved (only MODFlags.const_ at the moment) - extern (D) this(const ref Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) @safe + extern (D) this(Loc loc, TOK tok, Identifier id, structalign_t packalign, Type base, Dsymbols* members) @safe { //printf("TypeTag ctor %s %p\n", id ? id.toChars() : "null".ptr, this); super(Ttag); @@ -4785,11 +3810,11 @@ extern (C++) struct ParameterList { /// The raw (unexpanded) formal parameters, possibly containing tuples. Parameters* parameters; - StorageClass stc; // storage class of ... + STC stc; // storage class of ... VarArg varargs = VarArg.none; bool hasIdentifierList; // true if C identifier-list style - this(Parameters* parameters, VarArg varargs = VarArg.none, StorageClass stc = 0) @safe + this(Parameters* parameters, VarArg varargs = VarArg.none, STC stc = STC.none) @safe { this.parameters = parameters; this.varargs = varargs; @@ -4846,7 +3871,8 @@ extern (C++) struct ParameterList foreach (_, p1; cast() this) { auto p2 = other[idx++]; - if (!p2 || p1 != p2) { + if (!p2 || p1 != p2) + { diff = true; break; } @@ -4887,13 +3913,13 @@ extern (C++) final class Parameter : ASTNode import dmd.attrib : UserAttributeDeclaration; Loc loc; - StorageClass storageClass; + STC storageClass; Type type; Identifier ident; Expression defaultArg; UserAttributeDeclaration userAttribDecl; // user defined attributes - extern (D) this(const ref Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe + extern (D) this(Loc loc, STC storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe { this.loc = loc; this.type = type; @@ -4903,9 +3929,9 @@ extern (C++) final class Parameter : ASTNode this.userAttribDecl = userAttribDecl; } - static Parameter create(const ref Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe + static Parameter create(Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl) @safe { - return new Parameter(loc, storageClass, type, ident, defaultArg, userAttribDecl); + return new Parameter(loc, cast(STC) storageClass, type, ident, defaultArg, userAttribDecl); } Parameter syntaxCopy() @@ -4925,7 +3951,7 @@ extern (C++) final class Parameter : ASTNode Type isLazyArray() { Type tb = type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { Type tel = (cast(TypeArray)tb).next.toBasetype(); if (auto td = tel.isTypeDelegate()) @@ -5035,7 +4061,7 @@ extern (C++) final class Parameter : ASTNode /*************************************** * Expands tuples in args in depth first order. Calls - * dg(void *ctx, size_t argidx, Parameter *arg) for each Parameter. + * dg(void* ctx, size_t argidx, Parameter* arg) for each Parameter. * If dg returns !=0, stops and returns that value else returns 0. * Use this function to avoid the O(N + N^2/2) complexity of * calculating dim and calling N times getNth. @@ -5117,21 +4143,21 @@ extern (C++) final class Parameter : ASTNode bool isCovariant(bool returnByRef, const Parameter p) const pure nothrow @nogc @safe { - ulong thisSTC = this.storageClass; - ulong otherSTC = p.storageClass; + STC thisSTC = this.storageClass; + STC otherSTC = p.storageClass; if (thisSTC & STC.constscoperef) thisSTC |= STC.scope_; if (otherSTC & STC.constscoperef) otherSTC |= STC.scope_; - const mask = STC.ref_ | STC.out_ | STC.lazy_ | (((thisSTC | otherSTC) & STC.constscoperef) ? STC.in_ : 0); + const mask = STC.ref_ | STC.out_ | STC.lazy_ | (((thisSTC | otherSTC) & STC.constscoperef) ? STC.in_ : STC.none); if ((thisSTC & mask) != (otherSTC & mask)) return false; return isCovariantScope(returnByRef, thisSTC, otherSTC); } - extern (D) static bool isCovariantScope(bool returnByRef, StorageClass from, StorageClass to) pure nothrow @nogc @safe + extern (D) static bool isCovariantScope(bool returnByRef, STC from, STC to) pure nothrow @nogc @safe { // Workaround for failing covariance when finding a common type of delegates, // some of which have parameters with inferred scope @@ -5260,19 +4286,19 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma { if (tf.purity) dg("pure"); - if (tf.isnothrow) + if (tf.isNothrow) dg("nothrow"); - if (tf.isnogc) + if (tf.isNogc) dg("@nogc"); - if (tf.isproperty) + if (tf.isProperty) dg("@property"); - if (tf.isref) + if (tf.isRef) dg("ref"); - if (tf.isreturn && !tf.isreturninferred) + if (tf.isReturn && !tf.isReturnInferred) dg("return"); - if (tf.isScopeQual && !tf.isscopeinferred) + if (tf.isScopeQual && !tf.isScopeInferred) dg("scope"); - if (tf.islive) + if (tf.isLive) dg("@live"); TRUST trustAttrib = tf.trust; @@ -5294,10 +4320,10 @@ void attributesApply(const TypeFunction tf, void delegate(string) dg, TRUSTforma AggregateDeclaration isAggregate(Type t) { t = t.toBasetype(); - if (t.ty == Tclass) - return (cast(TypeClass)t).sym; - if (t.ty == Tstruct) - return (cast(TypeStruct)t).sym; + if (auto tc = t.isTypeClass()) + return tc.sym; + if (auto ts = t.isTypeStruct()) + return ts.sym; return null; } @@ -5312,27 +4338,27 @@ AggregateDeclaration isAggregate(Type t) bool isIndexableNonAggregate(Type t) { t = t.toBasetype(); - return (t.ty == Tpointer || t.ty == Tsarray || t.ty == Tarray || t.ty == Taarray || + return (t.ty == Tpointer || t.isStaticOrDynamicArray() || t.ty == Taarray || t.ty == Ttuple || t.ty == Tvector); } /*************************************** * Computes how a parameter may be returned. - * Shrinking the representation is necessary because StorageClass is so wide + * Shrinking the representation is necessary because STC is so wide * Params: * stc = storage class of parameter * Returns: * value from enum ScopeRef */ -ScopeRef buildScopeRef(StorageClass stc) pure nothrow @nogc @safe +ScopeRef buildScopeRef(STC stc) pure nothrow @nogc @safe { if (stc & STC.out_) stc |= STC.ref_; // treat `out` and `ref` the same ScopeRef result; - final switch (stc & (STC.ref_ | STC.scope_ | STC.return_)) + switch (stc & (STC.ref_ | STC.scope_ | STC.return_)) { - case 0: result = ScopeRef.None; break; + case STC.none: result = ScopeRef.None; break; /* can occur in case test/compilable/testsctreturn.d * related to https://issues.dlang.org/show_bug.cgi?id=20149 @@ -5350,6 +4376,8 @@ ScopeRef buildScopeRef(StorageClass stc) pure nothrow @nogc @safe result = stc & STC.returnScope ? ScopeRef.Ref_ReturnScope : ScopeRef.ReturnRef_Scope; break; + default: + assert(0); } return result; } diff --git a/gcc/d/dmd/mtype.h b/gcc/d/dmd/mtype.h index ad64b12..4d1938f 100644 --- a/gcc/d/dmd/mtype.h +++ b/gcc/d/dmd/mtype.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -42,7 +42,7 @@ typedef struct TYPE type; namespace dmd { - Type *typeSemantic(Type *t, const Loc &loc, Scope *sc); + Type *typeSemantic(Type *t, Loc loc, Scope *sc); Type *merge(Type *type); } @@ -146,9 +146,6 @@ public: MOD mod; // modifiers MODxxxx char *deco; void* mcache; - Type *pto; // merged pointer to this type - Type *rto; // reference to this type - Type *arrayof; // array of this type TypeInfoDeclaration *vtinfo; // TypeInfo object for this Type type *ctype; // for back end @@ -228,20 +225,18 @@ public: char *toPrettyChars(bool QualifyTypes = false); static void _init(); - uinteger_t size(); - virtual uinteger_t size(const Loc &loc); virtual unsigned alignsize(); void modToBuffer(OutBuffer& buf) const; char *modToChars() const; - virtual bool isintegral(); - virtual bool isfloating(); // real, imaginary, or complex - virtual bool isreal(); - virtual bool isimaginary(); - virtual bool iscomplex(); - virtual bool isscalar(); - virtual bool isunsigned(); - virtual bool isscope(); + virtual bool isIntegral(); + virtual bool isFloating(); // real, imaginary, or complex + virtual bool isReal(); + virtual bool isImaginary(); + virtual bool isComplex(); + virtual bool isScalar(); + virtual bool isUnsigned(); + virtual bool isScopeClass(); virtual bool isString(); virtual bool isAssignable(); virtual bool isBoolean(); @@ -266,17 +261,14 @@ public: virtual Type *makeSharedWildConst(); virtual Type *makeMutable(); Type *toBasetype(); - virtual MATCH implicitConvTo(Type *to); - virtual MATCH constConv(Type *to); virtual unsigned char deduceWild(Type *t, bool isRef); virtual ClassDeclaration *isClassHandle(); virtual structalign_t alignment(); - virtual Expression *defaultInitLiteral(const Loc &loc); - virtual bool isZeroInit(const Loc &loc = Loc()); // if initializer is 0 + virtual Expression *defaultInitLiteral(Loc loc); virtual int hasWild() const; virtual bool hasVoidInitPointers(); - virtual bool hasSystemFields(); + virtual bool hasUnsafeBitpatterns(); virtual bool hasInvariant(); virtual Type *nextOf(); Type *baseElemOf(); @@ -323,8 +315,7 @@ public: const char *kind() override; TypeError *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; - Expression *defaultInitLiteral(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -344,7 +335,6 @@ public: Type *makeSharedWild() override final; Type *makeSharedWildConst() override final; Type *makeMutable() override final; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override final; void transitive(); void accept(Visitor *v) override { v->visit(this); } @@ -358,17 +348,14 @@ public: const char *kind() override; TypeBasic *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; - bool isintegral() override; - bool isfloating() override; - bool isreal() override; - bool isimaginary() override; - bool iscomplex() override; - bool isscalar() override; - bool isunsigned() override; - MATCH implicitConvTo(Type *to) override; - bool isZeroInit(const Loc &loc) override; + bool isIntegral() override; + bool isFloating() override; + bool isReal() override; + bool isImaginary() override; + bool isComplex() override; + bool isScalar() override; + bool isUnsigned() override; // For eliminating dynamic_cast TypeBasic *isTypeBasic() override; @@ -383,17 +370,14 @@ public: static TypeVector *create(Type *basetype); const char *kind() override; TypeVector *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; - bool isintegral() override; - bool isfloating() override; - bool isscalar() override; - bool isunsigned() override; + bool isIntegral() override; + bool isFloating() override; + bool isScalar() override; + bool isUnsigned() override; bool isBoolean() override; - MATCH implicitConvTo(Type *to) override; - Expression *defaultInitLiteral(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; TypeBasic *elementType(); - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -413,15 +397,11 @@ public: const char *kind() override; TypeSArray *syntaxCopy() override; bool isIncomplete(); - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; bool isString() override; - bool isZeroInit(const Loc &loc) override; structalign_t alignment() override; - MATCH constConv(Type *to) override; - MATCH implicitConvTo(Type *to) override; - Expression *defaultInitLiteral(const Loc &loc) override; - bool hasSystemFields() override; + Expression *defaultInitLiteral(Loc loc) override; + bool hasUnsafeBitpatterns() override; bool hasVoidInitPointers() override; bool hasInvariant() override; bool needsDestruction() override; @@ -437,12 +417,9 @@ class TypeDArray final : public TypeArray public: const char *kind() override; TypeDArray *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; bool isString() override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; - MATCH implicitConvTo(Type *to) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -456,11 +433,7 @@ public: static TypeAArray *create(Type *t, Type *index); const char *kind() override; TypeAArray *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; - MATCH implicitConvTo(Type *to) override; - MATCH constConv(Type *to) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -471,11 +444,7 @@ public: static TypePointer *create(Type *t); const char *kind() override; TypePointer *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; - MATCH implicitConvTo(Type *to) override; - MATCH constConv(Type *to) override; - bool isscalar() override; - bool isZeroInit(const Loc &loc) override; + bool isScalar() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -485,8 +454,6 @@ class TypeReference final : public TypeNext public: const char *kind() override; TypeReference *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; - bool isZeroInit(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -499,7 +466,7 @@ enum RET enum class TRUST : unsigned char { default_ = 0, - system = 1, // @system (same as TRUSTdefault) + system = 1, // @system (same as TRUST.default_ unless feature "safer" is enabled) trusted = 2, // @trusted safe = 3 // @safe }; @@ -528,7 +495,7 @@ public: Expression *defaultArg; UserAttributeDeclaration *userAttribDecl; // user defined attributes - static Parameter *create(const Loc &loc, StorageClass storageClass, Type *type, Identifier *ident, + static Parameter *create(Loc loc, StorageClass storageClass, Type *type, Identifier *ident, Expression *defaultArg, UserAttributeDeclaration *userAttribDecl); Parameter *syntaxCopy(); Type *isLazyArray(); @@ -574,28 +541,27 @@ public: bool hasLazyParameters(); bool isDstyleVariadic() const; - MATCH constConv(Type *to) override; - - bool isnothrow() const; - void isnothrow(bool v); - bool isnogc() const; - void isnogc(bool v); - bool isproperty() const; - void isproperty(bool v); - bool isref() const; - void isref(bool v); - bool isreturn() const; - void isreturn(bool v); - bool isreturnscope() const; - void isreturnscope(bool v); + + bool isNothrow() const; + void isNothrow(bool v); + bool isNogc() const; + void isNogc(bool v); + bool isProperty() const; + void isProperty(bool v); + bool isRef() const; + void isRef(bool v); + bool isReturn() const; + void isReturn(bool v); + bool isReturnScope() const; + void isReturnScope(bool v); bool isScopeQual() const; void isScopeQual(bool v); - bool isreturninferred() const; - void isreturninferred(bool v); - bool isscopeinferred() const; - void isscopeinferred(bool v); - bool islive() const; - void islive(bool v); + bool isReturnInferred() const; + void isReturnInferred(bool v); + bool isScopeInferred() const; + void isScopeInferred(bool v); + bool isLive() const; + void isLive(bool v); bool incomplete() const; void incomplete(bool v); bool isInOutParam() const; @@ -615,10 +581,7 @@ public: static TypeDelegate *create(TypeFunction *t); const char *kind() override; TypeDelegate *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; - MATCH implicitConvTo(Type *to) override; - bool isZeroInit(const Loc &loc) override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -634,7 +597,6 @@ class TypeTraits final : public Type const char *kind() override; TypeTraits *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -657,7 +619,6 @@ public: // representing ident.ident!tiargs.ident. ... etc. Objects idents; - uinteger_t size(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -666,9 +627,8 @@ class TypeIdentifier final : public TypeQualified { public: Identifier *ident; - Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution - static TypeIdentifier *create(const Loc &loc, Identifier *ident); + static TypeIdentifier *create(Loc loc, Identifier *ident); const char *kind() override; TypeIdentifier *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -694,7 +654,6 @@ public: const char *kind() override; TypeTypeof *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -727,22 +686,18 @@ public: static TypeStruct *create(StructDeclaration *sym); const char *kind() override; - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; TypeStruct *syntaxCopy() override; structalign_t alignment() override; - Expression *defaultInitLiteral(const Loc &loc) override; - bool isZeroInit(const Loc &loc) override; + Expression *defaultInitLiteral(Loc loc) override; bool isAssignable() override; bool isBoolean() override; bool needsDestruction() override; bool needsCopyOrPostblit() override; bool needsNested() override; bool hasVoidInitPointers() override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasInvariant() override; - MATCH implicitConvTo(Type *to) override; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override; void accept(Visitor *v) override { v->visit(this); } @@ -755,27 +710,23 @@ public: const char *kind() override; TypeEnum *syntaxCopy() override; - uinteger_t size(const Loc &loc) override; unsigned alignsize() override; - Type *memType(const Loc &loc); - bool isintegral() override; - bool isfloating() override; - bool isreal() override; - bool isimaginary() override; - bool iscomplex() override; - bool isscalar() override; - bool isunsigned() override; + Type *memType(Loc loc); + bool isIntegral() override; + bool isFloating() override; + bool isReal() override; + bool isImaginary() override; + bool isComplex() override; + bool isScalar() override; + bool isUnsigned() override; bool isBoolean() override; bool isString() override; bool isAssignable() override; bool needsDestruction() override; bool needsCopyOrPostblit() override; bool needsNested() override; - MATCH implicitConvTo(Type *to) override; - MATCH constConv(Type *to) override; - bool isZeroInit(const Loc &loc) override; bool hasVoidInitPointers() override; - bool hasSystemFields() override; + bool hasUnsafeBitpatterns() override; bool hasInvariant() override; Type *nextOf() override; @@ -790,14 +741,10 @@ public: CPPMANGLE cppmangle; const char *kind() override; - uinteger_t size(const Loc &loc) override; TypeClass *syntaxCopy() override; ClassDeclaration *isClassHandle() override; - MATCH implicitConvTo(Type *to) override; - MATCH constConv(Type *to) override; unsigned char deduceWild(Type *t, bool isRef) override; - bool isZeroInit(const Loc &loc) override; - bool isscope() override; + bool isScopeClass() override; bool isBoolean() override; void accept(Visitor *v) override { v->visit(this); } @@ -838,10 +785,8 @@ public: const char *kind() override; TypeNull *syntaxCopy() override; - MATCH implicitConvTo(Type *to) override; bool isBoolean() override; - uinteger_t size(const Loc &loc) override; void accept(Visitor *v) override { v->visit(this); } }; @@ -850,10 +795,7 @@ class TypeNoreturn final : public Type public: const char *kind() override; TypeNoreturn *syntaxCopy() override; - MATCH implicitConvTo(Type* to) override; - MATCH constConv(Type* to) override; bool isBoolean() override; - uinteger_t size(const Loc& loc) override; unsigned alignsize() override; void accept(Visitor *v) override { v->visit(this); } @@ -877,9 +819,10 @@ namespace dmd // return the symbol to which type t resolves Dsymbol *toDsymbol(Type *t, Scope *sc); bool equivalent(Type *src, Type *t); - Covariant covariant(Type *, Type *, StorageClass * = NULL, bool = false); + Covariant covariant(Type *, Type *, StorageClass * = nullptr, bool = false); bool isBaseOf(Type *tthis, Type *t, int *poffset); - Type *trySemantic(Type *type, const Loc &loc, Scope *sc); + bool isZeroInit(Type *t, Loc loc = Loc()); + Type *trySemantic(Type *type, Loc loc, Scope *sc); Type *pointerTo(Type *type); Type *referenceTo(Type *type); Type *merge2(Type *type); @@ -902,4 +845,8 @@ namespace dmd Type *addMod(Type *type, MOD mod); Type *addStorageClass(Type *type, StorageClass stc); Type *substWildTo(Type *type, unsigned mod); + uinteger_t size(Type *type); + uinteger_t size(Type *type, Loc loc); + MATCH implicitConvTo(Type* from, Type* to); + MATCH constConv(Type* from, Type* to); } diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d index c2fa5fb..fab9723 100644 --- a/gcc/d/dmd/mustuse.d +++ b/gcc/d/dmd/mustuse.d @@ -1,11 +1,11 @@ /** * Compile-time checks associated with the @mustuse attribute. * - * Copyright: Copyright (C) 2022-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2022-2025 by The D Language Foundation, All Rights Reserved * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d, _mustuse.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/mustuse.d, _mustuse.d) * Documentation: https://dlang.org/phobos/dmd_mustuse.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mustuse.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/mustuse.d */ module dmd.mustuse; @@ -111,26 +111,13 @@ private bool isAssignmentOpId(Identifier id) { import dmd.id : Id; - return id == Id.assign - || id == Id.addass - || id == Id.subass - || id == Id.mulass - || id == Id.divass - || id == Id.modass - || id == Id.andass - || id == Id.orass - || id == Id.xorass - || id == Id.shlass - || id == Id.shrass - || id == Id.ushrass - || id == Id.catass - || id == Id.indexass - || id == Id.slice - || id == Id.sliceass + return id == Id.opAssign + || id == Id.opIndexAssign + || id == Id.opSlice + || id == Id.opSliceAssign || id == Id.opOpAssign || id == Id.opIndexOpAssign - || id == Id.opSliceOpAssign - || id == Id.powass; + || id == Id.opSliceOpAssign; } /** @@ -202,7 +189,7 @@ private bool isIncrementOrDecrement(Expression e) */ private bool hasMustUseAttribute(Dsymbol sym, Scope* sc) { - import dmd.attrib : foreachUda; + import dmd.attribsem : foreachUda; bool result = false; diff --git a/gcc/d/dmd/nogc.d b/gcc/d/dmd/nogc.d index 9e45e45..f1494a4 100644 --- a/gcc/d/dmd/nogc.d +++ b/gcc/d/dmd/nogc.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#nogc-functions, No-GC Functions) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d, _nogc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nogc.d, _nogc.d) * Documentation: https://dlang.org/phobos/dmd_nogc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nogc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/nogc.d */ module dmd.nogc; @@ -18,17 +18,24 @@ import core.stdc.stdio; import dmd.aggregate; import dmd.astenums; import dmd.declaration; +import dmd.common.outbuffer; +import dmd.dmodule; import dmd.dscope; import dmd.dtemplate : isDsymbol; +import dmd.dsymbol : PASS; import dmd.errors; import dmd.expression; import dmd.func; import dmd.globals; import dmd.init; +import dmd.location; import dmd.mtype; -import dmd.postordervisitor; +import dmd.rootobject : RootObject, DYNCAST; +import dmd.semantic2; +import dmd.semantic3; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /************************************** * Look for GC-allocations @@ -40,6 +47,7 @@ public: FuncDeclaration f; bool checkOnly; // don't print errors bool err; + bool nogcExceptions; // -preview=dip1008 enabled extern (D) this(FuncDeclaration f) scope @safe { @@ -73,20 +81,19 @@ public: * Register that expression `e` requires the GC * Params: * e = expression that uses GC - * format = error message when `e` is used in a `@nogc` function. - * Must contain format strings "`@nogc` %s `%s`" referring to the function. + * msg = error message when `e` is used in a `@nogc` function. * Returns: `true` if `err` was set, `false` if it's not in a `@nogc` and not checkonly (-betterC) */ - private bool setGC(Expression e, const(char)* format) + private bool setGC(Expression e, const(char)* msg) { if (checkOnly) { err = true; return true; } - if (f.setGC(e.loc, format)) + if (f.setGC(e.loc, msg)) { - error(e.loc, format, f.kind(), f.toPrettyChars()); + error(e.loc, "%s causes a GC allocation in `@nogc` %s `%s`", msg, f.kind(), f.toChars()); err = true; return true; } @@ -104,7 +111,7 @@ public: auto fd = stripHookTraceImpl(e.f); if (fd.ident == Id._d_arraysetlengthT) { - if (setGC(e, "setting `length` in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "setting this array's `length`")) return; f.printGCUsage(e.loc, "setting `length` may cause a GC allocation"); } @@ -114,7 +121,7 @@ public: { if (e.type.ty != Tarray || !e.elements || !e.elements.length || e.onstack) return; - if (setGC(e, "array literal in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "this array literal")) return; f.printGCUsage(e.loc, "array literal may cause a GC allocation"); } @@ -123,13 +130,15 @@ public: { if (!e.keys.length) return; - if (setGC(e, "associative array literal in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "this associative array literal")) return; f.printGCUsage(e.loc, "associative array literal may cause a GC allocation"); } override void visit(NewExp e) { + if (e.placement) + return; // placement new doesn't use the GC if (e.member && !e.member.isNogc() && f.setGC(e.loc, null)) { // @nogc-ness is already checked in NewExp::semantic @@ -137,10 +146,10 @@ public: } if (e.onstack) return; - if (global.params.ehnogc && e.thrownew) + if (nogcExceptions && e.thrownew) return; // separate allocator is called for this, not the GC - if (setGC(e, "cannot use `new` in `@nogc` %s `%s`")) + if (setGC(e, "allocating with `new`")) return; f.printGCUsage(e.loc, "`new` causes a GC allocation"); } @@ -163,7 +172,7 @@ public: Type t1b = e.e1.type.toBasetype(); if (e.modifiable && t1b.ty == Taarray) { - if (setGC(e, "assigning an associative array element in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "assigning this associative array element")) return; f.printGCUsage(e.loc, "assigning an associative array element may cause a GC allocation"); } @@ -173,7 +182,7 @@ public: { if (e.e1.op == EXP.arrayLength) { - if (setGC(e, "setting `length` in `@nogc` %s `%s` may cause a GC allocation")) + if (setGC(e, "setting this array's `length`")) return; f.printGCUsage(e.loc, "setting `length` may cause a GC allocation"); } @@ -186,14 +195,14 @@ public: err = true; return; } - if (setGC(e, "cannot use operator `~=` in `@nogc` %s `%s`")) + if (setGC(e, "appending to this array with operator `~=`")) return; f.printGCUsage(e.loc, "operator `~=` may cause a GC allocation"); } override void visit(CatExp e) { - if (setGC(e, "cannot use operator `~` in `@nogc` %s `%s`")) + if (setGC(e, "concatenating with operator `~`")) return; f.printGCUsage(e.loc, "operator `~` may cause a GC allocation"); } @@ -201,7 +210,7 @@ public: Expression checkGC(Scope* sc, Expression e) { - if (sc.flags & SCOPE.ctfeBlock) // ignore GC in ctfe blocks + if (sc.ctfeBlock) // ignore GC in ctfe blocks return e; /* If betterC, allow GC to happen in non-CTFE code. @@ -211,13 +220,14 @@ Expression checkGC(Scope* sc, Expression e) const betterC = !global.params.useGC; FuncDeclaration f = sc.func; if (e && e.op != EXP.error && f && sc.intypeof != 1 && - (!(sc.flags & SCOPE.ctfe) || betterC) && + (!sc.ctfe || betterC) && (f.type.ty == Tfunction && - (cast(TypeFunction)f.type).isnogc || f.nogcInprocess || global.params.v.gc) && - !(sc.flags & SCOPE.debug_)) + (cast(TypeFunction)f.type).isNogc || f.nogcInprocess || global.params.v.gc) && + !sc.debug_) { scope NOGCVisitor gcv = new NOGCVisitor(f); gcv.checkOnly = betterC; + gcv.nogcExceptions = sc.previews.dip1008; walkPostorder(e, gcv); if (gcv.err) { @@ -234,6 +244,18 @@ Expression checkGC(Scope* sc, Expression e) return e; } +extern (D) void printGCUsage(FuncDeclaration fd, Loc loc, const(char)* warn) +{ + if (!global.params.v.gc) + return; + + Module m = fd.getModule(); + if (m && m.isRoot() && !fd.inUnittest()) + { + message(loc, "vgc: %s", warn); + } +} + /** * Removes `_d_HookTraceImpl` if found from `fd`. * This is needed to be able to find hooks that are called though the hook's `*Trace` wrapper. @@ -244,7 +266,6 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd) { import dmd.id : Id; import dmd.dsymbol : Dsymbol; - import dmd.rootobject : RootObject, DYNCAST; if (fd.ident != Id._d_HookTraceImpl) return fd; @@ -256,3 +277,76 @@ private FuncDeclaration stripHookTraceImpl(FuncDeclaration fd) assert(s, "Expected _d_HookTraceImpl's second template parameter to be an alias to the hook!"); return s.isFuncDeclaration; } + +/************************************** + * The function is doing something that may allocate with the GC, + * so mark it as not nogc (not no-how). + * + * Params: + * fd = function + * loc = location of GC action + * fmt = format string for error message. Must include "%s `%s`" for the function kind and name. + * args = arguments to format string + * + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ +extern (D) bool setGC(FuncDeclaration fd, Loc loc, const(char)* fmt, RootObject[] args...) +{ + //printf("setGC() %s\n", toChars()); + if (fd.nogcInprocess && fd.semanticRun < PASS.semantic3 && fd._scope) + { + fd.semantic2(fd._scope); + fd.semantic3(fd._scope); + } + + if (fd.nogcInprocess) + { + fd.nogcInprocess = false; + if (fmt) + fd.nogcViolation = new AttributeViolation(loc, fmt, args); // action that requires GC + else if (args.length > 0) + { + if (auto sa = args[0].isDsymbol()) + { + if (FuncDeclaration fd2 = sa.isFuncDeclaration()) + { + fd.nogcViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function + } + } + } + + fd.type.toTypeFunction().isNogc = false; + if (fd.fes) + fd.fes.func.setGC(Loc.init, null, null); + } + else if (fd.isNogc()) + return true; + return false; +} + +/************************************** + * The function calls non-`@nogc` function f, mark it as not nogc. + * Params: + * fd = function doin the call + * f = function being called + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ +extern (D) bool setGCCall(FuncDeclaration fd, FuncDeclaration f) +{ + return fd.setGC(fd.loc, null, f); +} + + bool isNogc(FuncDeclaration fd) +{ + //printf("isNogc() %s, inprocess: %d\n", toChars(), !!(flags & FUNCFLAG.nogcInprocess)); + if (fd.nogcInprocess) + fd.setGC(fd.loc, null); + return fd.type.toTypeFunction().isNogc; +} + +extern (D) bool isNogcBypassingInference(FuncDeclaration fd) +{ + return !fd.nogcInprocess && fd.isNogc(); +} diff --git a/gcc/d/dmd/nspace.d b/gcc/d/dmd/nspace.d index 52c2b79..0c93f0e 100644 --- a/gcc/d/dmd/nspace.d +++ b/gcc/d/dmd/nspace.d @@ -36,12 +36,12 @@ * are valid D identifier. * * See_Also: https://github.com/dlang/dmd/pull/10031 - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/nspace.d, _nspace.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/nspace.d, _nspace.d) * Documentation: https://dlang.org/phobos/dmd_nspace.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/nspace.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/nspace.d */ module dmd.nspace; @@ -62,10 +62,11 @@ extern (C++) final class Nspace : ScopeDsymbol */ Expression identExp; - extern (D) this(const ref Loc loc, Identifier ident, Expression identExp, Dsymbols* members) + extern (D) this(Loc loc, Identifier ident, Expression identExp, Dsymbols* members) { super(loc, ident); //printf("Nspace::Nspace(ident = %s)\n", ident.toChars()); + this.dsym = DSYM.nspace; this.members = members; this.identExp = identExp; } @@ -88,11 +89,6 @@ extern (C++) final class Nspace : ScopeDsymbol return "namespace"; } - override inout(Nspace) isNspace() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/nspace.h b/gcc/d/dmd/nspace.h index cbee2fb..7b4c302 100644 --- a/gcc/d/dmd/nspace.h +++ b/gcc/d/dmd/nspace.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -23,6 +23,5 @@ class Nspace final : public ScopeDsymbol Nspace *syntaxCopy(Dsymbol *s) override; bool hasPointers() override; const char *kind() const override; - Nspace *isNspace() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/ob.d b/gcc/d/dmd/ob.d index 0a59815..099f811 100644 --- a/gcc/d/dmd/ob.d +++ b/gcc/d/dmd/ob.d @@ -1,12 +1,13 @@ /** * Flow analysis for Ownership/Borrowing * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ob.d, _ob.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/ob.d, _ob.d) * Documentation: https://dlang.org/phobos/dmd_escape.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ob.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/ob.d + * References: https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md Argument Ownership and Function Calls */ module dmd.ob; @@ -29,9 +30,10 @@ import dmd.dtemplate; import dmd.errors; import dmd.escape; import dmd.expression; -import dmd.foreachvar; + import dmd.func; import dmd.globals; +import dmd.hdrgen; import dmd.identifier; import dmd.init; import dmd.location; @@ -40,7 +42,9 @@ import dmd.printast; import dmd.statement; import dmd.stmtstate; import dmd.tokens; +import dmd.typesem; import dmd.visitor; +import dmd.visitor.foreachvar; import dmd.root.bitarray; import dmd.common.outbuffer; @@ -226,6 +230,8 @@ struct PtrVarState * are being merged * Params: * pvs = path to be merged with `this` + * vi = variable's index into gen[] + * gen = array of variable states */ void combine(ref PtrVarState pvs, size_t vi, PtrVarState[] gen) { @@ -280,6 +286,9 @@ struct PtrVarState } /*********************** + * Print a bracketed list of all the variables that depend on 'this' + * Params: + * vars = variables that depend on 'this' */ void print(VarDeclaration[] vars) { @@ -370,7 +379,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) return ob; } - // block_goto(blx, BCgoto, null) + // block_goto(blx, BC.goto_, null) ObNode* gotoNextNode() { return gotoNextNodeIs(newNode()); @@ -846,7 +855,7 @@ void toObNodes(ref ObNodes obnodes, Statement s) case STMT.Mixin: case STMT.Peel: case STMT.Synchronized: - debug printf("s: %s\n", s.toChars()); + debug printf("s: %s\n", toChars(s)); assert(0); // should have been rewritten } } @@ -1113,8 +1122,8 @@ bool isTrackableVar(VarDeclaration v) /* Assume types with a destructor are doing their own tracking, * such as being a ref counted type */ - if (v.needsScopeDtor()) - return false; +// if (v.needsScopeDtor()) +// return false; /* Not tracking function parameters that are not mutable */ @@ -1231,7 +1240,8 @@ void allocStates(ref ObState obstate) */ bool isBorrowedPtr(VarDeclaration v) { - return v.isScope() && !v.isowner && v.type.nextOf().isMutable(); + return v.isScope() && !v.isowner && + v.type.hasPointersToMutableFields(); } /****************************** @@ -1241,7 +1251,7 @@ bool isBorrowedPtr(VarDeclaration v) */ bool isReadonlyPtr(VarDeclaration v) { - return v.isScope() && !v.type.nextOf().isMutable(); + return v.isScope() && !v.type.hasPointersToMutableFields(); } /*************************************** @@ -1251,7 +1261,7 @@ void genKill(ref ObState obstate, ObNode* ob) { enum log = false; if (log) - printf("-----------computeGenKill()-----------\n"); + printf("-----------computeGenKill() %d -----------\n", ob.index); /*************** * Assigning result of expression `e` to variable `v`. @@ -1274,8 +1284,6 @@ void genKill(ref ObState obstate, ObNode* ob) pvs.state = PtrState.Owner; pvs.deps.zero(); - EscapeByResults er; - escapeByValue(e, &er, true); bool any = false; // if any variables are assigned to v void by(VarDeclaration r) @@ -1305,10 +1313,7 @@ void genKill(ref ObState obstate, ObNode* ob) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(e, &by); /* Make v an Owner for initializations like: * scope v = malloc(); @@ -1331,7 +1336,7 @@ void genKill(ref ObState obstate, ObNode* ob) } } - void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) + void dgReadVar(Loc loc, ObNode* ob, VarDeclaration v, bool mutable) { if (log) printf("dgReadVar() %s %d\n", v.toChars(), mutable); @@ -1346,12 +1351,12 @@ void genKill(ref ObState obstate, ObNode* ob) { alias visit = typeof(super).visit; extern (D) void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar; - extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar; + extern (D) void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar; ObNode* ob; ObState* obstate; extern (D) this(void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar, - void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar, + void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar, ObNode* ob, ref ObState obstate) scope { this.dgWriteVar = dgWriteVar; @@ -1435,6 +1440,41 @@ void genKill(ref ObState obstate, ObNode* ob) assert(t.ty == Tdelegate); tf = t.nextOf().isTypeFunction(); assert(tf); + + } + + if (auto dve = ce.e1.isDotVarExp()) + { + if (!t.isTypeDelegate() && dve.e1.isVarExp()) + { + //printf("dve: %s\n", dve.toChars()); + + void byf(VarDeclaration v) + { + //printf("byf v: %s\n", v.ident.toChars()); + if (!isTrackableVar(v)) + return; + + const vi = obstate.vars.find(v); + if (vi == size_t.max) + return; + + auto fd = dve.var.isFuncDeclaration(); + if (fd && fd.storage_class & STC.scope_) + { + // borrow + obstate.varStack.push(vi); + obstate.mutableStack.push(isMutableRef(dve.e1.type.toBasetype())); + } + else + { + // move (i.e. consume arg) + makeUndefined(vi, ob.gen); + } + } + + escapeLive(dve.e1, &byf); + } } // j=1 if _arguments[] is first argument @@ -1450,14 +1490,13 @@ void genKill(ref ObState obstate, ObNode* ob) Parameter p = tf.parameterList[i - j]; auto pt = p.type.toBasetype(); - EscapeByResults er; - escapeByValue(arg, &er, true); if (!(p.storageClass & STC.out_ && arg.isVarExp())) arg.accept(this); void by(VarDeclaration v) { + //printf("by v: %s\n", v.ident.toChars()); if (!isTrackableVar(v)) return; @@ -1484,18 +1523,12 @@ void genKill(ref ObState obstate, ObNode* ob) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(arg, &by); } else // variadic args { arg.accept(this); - EscapeByResults er; - escapeByValue(arg, &er, true); - void byv(VarDeclaration v) { if (!isTrackableVar(v)) @@ -1517,10 +1550,7 @@ void genKill(ref ObState obstate, ObNode* ob) makeUndefined(vi, ob.gen); } - foreach (VarDeclaration v2; er.byvalue) - byv(v2); - foreach (VarDeclaration v2; er.byref) - byv(v2); + escapeLive(arg, &byv); } } @@ -1653,7 +1683,7 @@ void genKill(ref ObState obstate, ObNode* ob) override void visit(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { if (e.basis) e.basis.accept(this); @@ -1693,6 +1723,8 @@ void genKill(ref ObState obstate, ObNode* ob) override void visit(NewExp e) { + if (e.placement) + e.placement.accept(this); if (e.arguments) { foreach (ex; *e.arguments) @@ -1721,6 +1753,15 @@ void genKill(ref ObState obstate, ObNode* ob) } foreachExp(ob, ob.exp); + + if (log) + { + printf(" gen:\n"); + foreach (i, ref pvs2; ob.gen[]) + { + printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]); + } + } } /*************************************** @@ -1946,6 +1987,25 @@ void doDataFlowAnalysis(ref ObState obstate) /*************************************** + * Check for escaping variables using DIP1000's `escapeByValue`, with `live` set to `true` + * Params: + * e = expression to check + * onVar = gets called for each variable escaped through `e`, either by value or by ref + */ +void escapeLive(Expression e, scope void delegate(VarDeclaration) onVar) +{ + scope EscapeByResults er = EscapeByResults( + (VarDeclaration v, bool) => onVar(v), + onVar, + (FuncDeclaration f, bool) {}, + (Expression e, bool) {}, + true, + ); + + escapeByValue(e, er); +} + +/*************************************** * Check for Ownership/Borrowing errors. */ void checkObErrors(ref ObState obstate) @@ -1976,8 +2036,6 @@ void checkObErrors(ref ObState obstate) } pvs.deps.zero(); - EscapeByResults er; - escapeByValue(e, &er, true); void by(VarDeclaration r) // `v` = `r` { @@ -2015,10 +2073,7 @@ void checkObErrors(ref ObState obstate) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(e, &by); } else { @@ -2032,7 +2087,7 @@ void checkObErrors(ref ObState obstate) } } - void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen) + void dgReadVar(Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen) { if (log) printf("dgReadVar() %s\n", v.toChars()); const vi = obstate.vars.find(v); @@ -2050,12 +2105,12 @@ void checkObErrors(ref ObState obstate) { alias visit = typeof(super).visit; extern (D) void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar; - extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar; + extern (D) void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar; PtrVarState[] cpvs; ObNode* ob; ObState* obstate; - extern (D) this(void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar, + extern (D) this(void delegate(Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar, void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar, PtrVarState[] cpvs, ObNode* ob, ref ObState obstate) scope { @@ -2157,8 +2212,6 @@ void checkObErrors(ref ObState obstate) if (!(p.storageClass & STC.out_ && arg.isVarExp())) arg.accept(this); - EscapeByResults er; - escapeByValue(arg, &er, true); void by(VarDeclaration v) { @@ -2192,18 +2245,11 @@ void checkObErrors(ref ObState obstate) } } - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(arg, &by); } else // variadic args { arg.accept(this); - - EscapeByResults er; - escapeByValue(arg, &er, true); - void byv(VarDeclaration v) { if (!isTrackableVar(v)) @@ -2231,10 +2277,7 @@ void checkObErrors(ref ObState obstate) } } - foreach (VarDeclaration v2; er.byvalue) - byv(v2); - foreach (VarDeclaration v2; er.byref) - byv(v2); + escapeLive(arg, &byv); } } @@ -2383,7 +2426,7 @@ void checkObErrors(ref ObState obstate) override void visit(ArrayLiteralExp e) { Type tb = e.type.toBasetype(); - if (tb.ty == Tsarray || tb.ty == Tarray) + if (tb.isStaticOrDynamicArray()) { if (e.basis) e.basis.accept(this); @@ -2423,6 +2466,9 @@ void checkObErrors(ref ObState obstate) override void visit(NewExp e) { + if (e.placement) + e.placement.accept(this); + if (e.arguments) { foreach (ex; *e.arguments) @@ -2460,7 +2506,7 @@ void checkObErrors(ref ObState obstate) { static if (log) { - printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr); + printf("%d: %s\n", cast(int) obi, ob.exp ? ob.exp.toChars() : "".ptr); printf(" input:\n"); foreach (i, ref pvs; ob.input[]) { @@ -2490,7 +2536,9 @@ void checkObErrors(ref ObState obstate) if (s1 != s2 && (s1 == PtrState.Owner || s2 == PtrState.Owner)) { auto v = obstate.vars[i]; - .error(ob.exp ? ob.exp.loc : v.loc, "%s `%s` is both %s and %s", v.kind, v.toPrettyChars, PtrStateToChars(s1), PtrStateToChars(s2)); + // Don't worry about non-pointers + if (hasPointers(v.type)) + .error(ob.exp ? ob.exp.loc : v.loc, "%s `%s` is both %s and %s", v.kind, v.toPrettyChars, PtrStateToChars(s1), PtrStateToChars(s2)); } pvs1.combine(*pvs2, i, ob.gen); } @@ -2522,9 +2570,6 @@ void checkObErrors(ref ObState obstate) if (ob.obtype == ObType.retexp) { - EscapeByResults er; - escapeByValue(ob.exp, &er, true); - void by(VarDeclaration r) // `r` is the rvalue { const ri = obstate.vars.find(r); @@ -2552,11 +2597,7 @@ void checkObErrors(ref ObState obstate) } } } - - foreach (VarDeclaration v2; er.byvalue) - by(v2); - foreach (VarDeclaration v2; er.byref) - by(v2); + escapeLive(ob.exp, &by); } if (ob.obtype == ObType.return_ || ob.obtype == ObType.retexp) @@ -2649,6 +2690,9 @@ void makeChildrenUndefined(size_t vi, PtrVarState[] gen) /******************** * Recursively make Undefined vi undefined and all who list vi as a dependency + * Params: + * vi = variable's index + * gen = array of the states of variables */ void makeUndefined(size_t vi, PtrVarState[] gen) { diff --git a/gcc/d/dmd/objc.d b/gcc/d/dmd/objc.d index 2f36d5d..32878b5 100644 --- a/gcc/d/dmd/objc.d +++ b/gcc/d/dmd/objc.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc.d, _objc.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/objc.d, _objc.d) * Documentation: https://dlang.org/phobos/dmd_objc.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/objc.d */ module dmd.objc; @@ -17,11 +17,11 @@ import dmd.aggregate; import dmd.arraytypes; import dmd.astenums; import dmd.attrib; +import dmd.attribsem; import dmd.cond; import dmd.dclass; import dmd.declaration; import dmd.denum; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -37,6 +37,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle; import dmd.mtype; import dmd.root.array; import dmd.common.outbuffer; @@ -92,39 +93,66 @@ struct ObjcSelector return sel; } + static const(char)[] toPascalCase(const(char)[] id) { + OutBuffer buf; + char firstChar = id[0]; + if (firstChar >= 'a' && firstChar <= 'z') + firstChar = cast(char)(firstChar - 'a' + 'A'); + + buf.writeByte(firstChar); + buf.writestring(id[1..$]); + return cast(const(char)[])buf.extractSlice(false); + } + extern (C++) static ObjcSelector* create(FuncDeclaration fdecl) { OutBuffer buf; - TypeFunction ftype = cast(TypeFunction)fdecl.type; + auto ftype = cast(TypeFunction)fdecl.type; const id = fdecl.ident.toString(); const nparams = ftype.parameterList.length; + // Special case: property setter - if (ftype.isproperty && nparams == 1) + if (ftype.isProperty && nparams == 1) { - // rewrite "identifier" as "setIdentifier" - char firstChar = id[0]; - if (firstChar >= 'a' && firstChar <= 'z') - firstChar = cast(char)(firstChar - 'a' + 'A'); - buf.writestring("set"); - buf.writeByte(firstChar); - buf.write(id[1 .. id.length - 1]); + + // Special case: "isXYZ:" + if (id.length >= 2 && id[0..2] == "is") + { + buf.writestring("set"); + buf.write(toPascalCase(id[2..$])); + } + else + { + buf.writestring("set"); + buf.write(toPascalCase(id)); + } buf.writeByte(':'); goto Lcomplete; } + // write identifier in selector buf.write(id[]); - // add mangled type and colon for each parameter - if (nparams) + + // To make it easier to match the selectors of objects nicely, + // the implementation has been replaced so that the parameter name followed by a colon + // is used instead. + // eg. void myFunction(int a, int b, int c) would be mangled to a selector as `myFunction:b:c: + if (nparams > 1) { - buf.writeByte('_'); - foreach (i, fparam; ftype.parameterList) + buf.writeByte(':'); + foreach(i; 1..nparams) { - mangleToBuffer(fparam.type, buf); + buf.write(ftype.parameterList[i].ident.toString()); buf.writeByte(':'); } } + else if (nparams == 1) + { + buf.writeByte(':'); + } Lcomplete: buf.writeByte('\0'); + // the slice is not expected to include a terminating 0 return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams); } @@ -564,13 +592,23 @@ extern(C++) private final class Supported : Objc return 0; }); + + // Avoid attempting to generate selectors for template instances. + if (fd.parent && fd.parent.isTemplateInstance()) + return; + + // No selector declared, generate one. + if (fd._linkage == LINK.objc && !fd.objc.selector) + { + fd.objc.selector = ObjcSelector.create(fd); + } } override void validateSelector(FuncDeclaration fd) { if (!fd.objc.selector) return; - TypeFunction tf = cast(TypeFunction)fd.type; + auto tf = cast(TypeFunction)fd.type; if (fd.objc.selector.paramCount != tf.parameterList.parameters.length) .error(fd.loc, "%s `%s` number of colons in Objective-C selector must match number of parameters", fd.kind, fd.toPrettyChars); if (fd.parent && fd.parent.isTemplateInstance()) @@ -676,8 +714,8 @@ extern(C++) private final class Supported : Objc { if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta) return cd.objc.metaclass; - else - return cd; + + return cd; } override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const @@ -767,11 +805,10 @@ extern(C++) private final class Supported : Objc { if (classDeclaration.baseClass) return getRuntimeMetaclass(classDeclaration.baseClass); - else - return classDeclaration; + + return classDeclaration; } - else - return classDeclaration.objc.metaclass; + return classDeclaration.objc.metaclass; } override void addSymbols(AttribDeclaration attribDeclaration, diff --git a/gcc/d/dmd/objc.h b/gcc/d/dmd/objc.h index 0390115..af6f2e4 100644 --- a/gcc/d/dmd/objc.h +++ b/gcc/d/dmd/objc.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2015-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2015-2025 by The D Language Foundation, All Rights Reserved * written by Michel Fortin * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/opover.d b/gcc/d/dmd/opover.d index 70eeaff..7baaeaa 100644 --- a/gcc/d/dmd/opover.d +++ b/gcc/d/dmd/opover.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/operatoroverloading.html, Operator Overloading) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/opover.d, _opover.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/opover.d, _opover.d) * Documentation: https://dlang.org/phobos/dmd_opover.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/opover.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/opover.d */ module dmd.opover; @@ -29,6 +29,7 @@ import dmd.errors; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.hdrgen; import dmd.id; @@ -70,81 +71,54 @@ bool isCommutative(EXP op) @safe return false; } -/*********************************** - * Get Identifier for operator overload. - */ -private Identifier opId(Expression e) +/// Returns: whether `op` can be overloaded with `opBinary` +private bool hasOpBinary(EXP op) pure @safe { - switch (e.op) + switch (op) { - case EXP.uadd: return Id.uadd; - case EXP.negate: return Id.neg; - case EXP.tilde: return Id.com; - case EXP.cast_: return Id._cast; - case EXP.in_: return Id.opIn; - case EXP.plusPlus: return Id.postinc; - case EXP.minusMinus: return Id.postdec; - case EXP.add: return Id.add; - case EXP.min: return Id.sub; - case EXP.mul: return Id.mul; - case EXP.div: return Id.div; - case EXP.mod: return Id.mod; - case EXP.pow: return Id.pow; - case EXP.leftShift: return Id.shl; - case EXP.rightShift: return Id.shr; - case EXP.unsignedRightShift: return Id.ushr; - case EXP.and: return Id.iand; - case EXP.or: return Id.ior; - case EXP.xor: return Id.ixor; - case EXP.concatenate: return Id.cat; - case EXP.assign: return Id.assign; - case EXP.addAssign: return Id.addass; - case EXP.minAssign: return Id.subass; - case EXP.mulAssign: return Id.mulass; - case EXP.divAssign: return Id.divass; - case EXP.modAssign: return Id.modass; - case EXP.powAssign: return Id.powass; - case EXP.leftShiftAssign: return Id.shlass; - case EXP.rightShiftAssign: return Id.shrass; - case EXP.unsignedRightShiftAssign: return Id.ushrass; - case EXP.andAssign: return Id.andass; - case EXP.orAssign: return Id.orass; - case EXP.xorAssign: return Id.xorass; - case EXP.concatenateAssign: return Id.catass; - case EXP.equal: return Id.eq; - case EXP.lessThan: - case EXP.lessOrEqual: - case EXP.greaterThan: - case EXP.greaterOrEqual: return Id.cmp; - case EXP.array: return Id.index; - case EXP.star: return Id.opStar; - default: assert(0); + case EXP.add: return true; + case EXP.min: return true; + case EXP.mul: return true; + case EXP.div: return true; + case EXP.mod: return true; + case EXP.and: return true; + case EXP.or: return true; + case EXP.xor: return true; + case EXP.leftShift: return true; + case EXP.rightShift: return true; + case EXP.unsignedRightShift: return true; + case EXP.concatenate: return true; + case EXP.pow: return true; + case EXP.in_: return true; + default: return false; } } -/*********************************** - * Get Identifier for reverse operator overload, - * `null` if not supported for this operator. - */ -private Identifier opId_r(Expression e) +/** + * Remove the = from op=, e.g. += becomes + + * + * Params: + * op = tag for a binary assign operator + * Returns: the corresponding binary operator, or `op` if it wasn't an assign operator +*/ +private EXP stripAssignOp(EXP op) { - switch (e.op) + switch (op) { - case EXP.in_: return Id.opIn_r; - case EXP.add: return Id.add_r; - case EXP.min: return Id.sub_r; - case EXP.mul: return Id.mul_r; - case EXP.div: return Id.div_r; - case EXP.mod: return Id.mod_r; - case EXP.pow: return Id.pow_r; - case EXP.leftShift: return Id.shl_r; - case EXP.rightShift: return Id.shr_r; - case EXP.unsignedRightShift:return Id.ushr_r; - case EXP.and: return Id.iand_r; - case EXP.or: return Id.ior_r; - case EXP.xor: return Id.ixor_r; - case EXP.concatenate: return Id.cat_r; - default: return null; + case EXP.addAssign: return EXP.add; + case EXP.minAssign: return EXP.min; + case EXP.mulAssign: return EXP.mul; + case EXP.divAssign: return EXP.div; + case EXP.modAssign: return EXP.mod; + case EXP.andAssign: return EXP.and; + case EXP.orAssign: return EXP.or; + case EXP.xorAssign: return EXP.xor; + case EXP.leftShiftAssign: return EXP.leftShift; + case EXP.rightShiftAssign: return EXP.rightShift; + case EXP.unsignedRightShiftAssign: return EXP.unsignedRightShift; + case EXP.concatenateAssign: return EXP.concatenate; + case EXP.powAssign: return EXP.pow; + default: return op; } } @@ -153,53 +127,7 @@ private Identifier opId_r(Expression e) */ Objects* opToArg(Scope* sc, EXP op) { - /* Remove the = from op= - */ - switch (op) - { - case EXP.addAssign: - op = EXP.add; - break; - case EXP.minAssign: - op = EXP.min; - break; - case EXP.mulAssign: - op = EXP.mul; - break; - case EXP.divAssign: - op = EXP.div; - break; - case EXP.modAssign: - op = EXP.mod; - break; - case EXP.andAssign: - op = EXP.and; - break; - case EXP.orAssign: - op = EXP.or; - break; - case EXP.xorAssign: - op = EXP.xor; - break; - case EXP.leftShiftAssign: - op = EXP.leftShift; - break; - case EXP.rightShiftAssign: - op = EXP.rightShift; - break; - case EXP.unsignedRightShiftAssign: - op = EXP.unsignedRightShift; - break; - case EXP.concatenateAssign: - op = EXP.concatenate; - break; - case EXP.powAssign: - op = EXP.pow; - break; - default: - break; - } - Expression e = new StringExp(Loc.initial, EXPtoString(op)); + Expression e = new StringExp(Loc.initial, EXPtoString(stripAssignOp(op))); e = e.expressionSemantic(sc); auto tiargs = new Objects(); tiargs.push(e); @@ -207,7 +135,7 @@ Objects* opToArg(Scope* sc, EXP op) } // Try alias this on first operand -private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e) +Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinExp e, Type[2] aliasThisStop) { if (!ad || !ad.aliasthis) return null; @@ -215,35 +143,29 @@ private Expression checkAliasThisForLhs(AggregateDeclaration ad, Scope* sc, BinE /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ - if (isRecursiveAliasThis(e.att1, e.e1.type)) + if (isRecursiveAliasThis(aliasThisStop[0], e.e1.type)) return null; //printf("att %s e1 = %s\n", Token.toChars(e.op), e.e1.type.toChars()); BinExp be = cast(BinExp)e.copy(); // Resolve 'alias this' but in case of assigment don't resolve properties yet // because 'e1 = e2' could mean 'e1(e2)' or 'e1() = e2' - bool findOnly = (e.op == EXP.assign); + bool findOnly = e.isAssignExp() !is null; be.e1 = resolveAliasThis(sc, e.e1, true, findOnly); if (!be.e1) return null; - Expression result; - if (be.op == EXP.concatenateAssign) - result = be.op_overload(sc); - else - result = be.trySemantic(sc); - - return result; + return be.trySemanticAliasThis(sc, aliasThisStop); } // Try alias this on second operand -private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e) +Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinExp e, Type[2] aliasThisStop) { if (!ad || !ad.aliasthis) return null; /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ - if (isRecursiveAliasThis(e.att2, e.e2.type)) + if (isRecursiveAliasThis(aliasThisStop[1], e.e2.type)) return null; //printf("att %s e2 = %s\n", Token.toChars(e.op), e.e2.type.toChars()); BinExp be = cast(BinExp)e.copy(); @@ -251,1168 +173,995 @@ private Expression checkAliasThisForRhs(AggregateDeclaration ad, Scope* sc, BinE if (!be.e2) return null; - Expression result; - if (be.op == EXP.concatenateAssign) - result = be.op_overload(sc); - else - result = be.trySemantic(sc); - - return result; + return be.trySemanticAliasThis(sc, aliasThisStop); } -/************************************ - * Operator overload. - * Check for operator overload, if so, replace - * with function call. - * Params: - * e = expression with operator - * sc = context - * pop = if not null, is set to the operator that was actually overloaded, - * which may not be `e.op`. Happens when operands are reversed to - * match an overload - * Returns: - * `null` if not an operator overload, - * otherwise the lowered expression - */ -Expression op_overload(Expression e, Scope* sc, EXP* pop = null) +Expression opOverloadUnary(UnaExp e, Scope* sc) { - Expression visit(Expression e) - { - assert(0); - } - - Expression visitUna(UnaExp e) + if (auto ae = e.e1.isArrayExp()) + { + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].isIntervalExp()); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - //printf("UnaExp::op_overload() (%s)\n", e.toChars()); - Expression result; - if (auto ae = e.e1.isArrayExp()) - { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); - } - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, &e0); - if (!result) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - /* Rewrite op(a[arguments]) as: - * a.opIndexUnary!(op)(arguments) - */ - Expressions* a = ae.arguments.copy(); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexUnary, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceUnary)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - /* Rewrite op(a[i..j]) as: - * a.opSliceUnary!(op)(i, j) - */ - auto a = new Expressions(); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceUnary, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - } - e.e1 = e.e1.expressionSemantic(sc); - e.e1 = resolveProperties(sc, e.e1); - Type att = null; // first cyclic `alias this` type - while (1) - { - if (e.e1.op == EXP.error) - { - return e.e1; - } - - AggregateDeclaration ad = isAggregate(e.e1.type); - if (!ad) - break; - - Dsymbol fd = null; - /* Rewrite as: - * e1.opUnary!(op)() - */ - fd = search_function(ad, Id.opUnary); - if (fd) - { - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - // D1-style operator overloads, deprecated - if (e.op != EXP.prePlusPlus && e.op != EXP.preMinusMinus) - { - auto id = opId(e); - fd = search_function(ad, id); - if (fd) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - e.e1 = e1; - continue; - } - break; - } - break; - } - return result; + ie = (*ae.arguments)[0].isIntervalExp(); } - - Expression visitArray(ArrayExp ae) + Type att = null; // first cyclic `alias this` type + while (true) { - //printf("ArrayExp::op_overload() (%s)\n", ae.toChars()); - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) + if (ae.e1.isErrorExp()) { - ie = (*ae.arguments)[0].isIntervalExp(); + return ae.e1; } - Expression result; - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - { - // If the non-aggregate expression ae.e1 is indexable or sliceable, - // convert it to the corresponding concrete expression. - if (isIndexableNonAggregate(t1b) || ae.e1.op == EXP.type) - { - // Convert to SliceExp - if (maybeSlice) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - return result; - } - // Convert to IndexExp - if (ae.arguments.length == 1) - { - result = new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]); - result = result.expressionSemantic(sc); - return result; - } - } - break; - } - if (search_function(ad, Id.index)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, &e0); - if (!result) // a[i..j] might be: a.opSlice(i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - /* Rewrite e1[arguments] as: - * e1.opIndex(arguments) - */ - Expressions* a = ae.arguments.copy(); - result = new DotIdExp(ae.loc, ae.e1, Id.index); - result = new CallExp(ae.loc, result, a); - if (maybeSlice) // a[] might be: a.opSlice() - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && ae.e1.op == EXP.type) - { - result = new SliceExp(ae.loc, ae.e1, ie); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - if (maybeSlice && search_function(ad, Id.slice)) - { - // Deal with $ - result = resolveOpDollar(sc, ae, ie, &e0); + Expression ae1save = ae.e1; + ae.lengthVar = null; - if (result.op == EXP.error) - { - if (!e0 && !search_function(ad, Id.dollar)) { - ae.loc.errorSupplemental("Aggregate declaration '%s' does not define 'opDollar'", ae.e1.toChars()); - } - return result; - } - /* Rewrite a[i..j] as: - * a.opSlice(i, j) - */ - auto a = new Expressions(); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - result = new DotIdExp(ae.loc, ae.e1, Id.slice); - result = new CallExp(ae.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - //printf("att arr e1 = %s\n", this.e1.type.toChars()); - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } + AggregateDeclaration ad = isAggregate(ae.e1.type); + if (!ad) break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - return result; - } - /*********************************************** - * This is mostly the same as UnaryExp::op_overload(), but has - * a different rewrite. - */ - Expression visitCast(CastExp e, Type att = null) - { - //printf("CastExp::op_overload() (%s)\n", e.toChars()); - Expression result; - AggregateDeclaration ad = isAggregate(e.e1.type); - if (ad) + if (search_function(ad, Id.opIndexUnary)) { - Dsymbol fd = null; - /* Rewrite as: - * e1.opCast!(T)() + Expression e0; + // Deal with $ + Expression ae2 = resolveOpDollar(sc, ae, e0); + if (!ae2) // op(a[i..j]) might be: a.opSliceUnary!(op)(i, j) + goto Lfallback; + if (ae2.isErrorExp()) + return ae2; + /* Rewrite op(a[arguments]) as: + * a.opIndexUnary!(op)(arguments) */ - fd = search_function(ad, Id._cast); - if (fd) - { - version (all) - { - // Backwards compatibility with D1 if opCast is a function, not a template - if (fd.isFuncDeclaration()) - { - // Rewrite as: e1.opCast() - return build_overload(e.loc, sc, e.e1, null, fd); - } - } - auto tiargs = new Objects(); - tiargs.push(e.to); - result = new DotTemplateInstanceExp(e.loc, e.e1, fd.ident, tiargs); - result = new CallExp(e.loc, result); + Expression result = dotTemplateCall(ae.e1, Id.opIndexUnary, opToArg(sc, e.op), (*ae.arguments)[]); + if (maybeSlice) // op(a[]) might be: a.opSliceUnary!(op)() + result = result.trySemantic(sc); + else result = result.expressionSemantic(sc); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - if (auto e1 = resolveAliasThis(sc, e.e1, true)) - { - result = e.copy(); - (cast(UnaExp)result).e1 = e1; - result = visitCast(result.isCastExp(), att); - return result; - } - } - } - return result; - } - Expression visitBin(BinExp e) - { - //printf("BinExp::op_overload() (%s)\n", e.toChars()); - Identifier id = opId(e); - Identifier id_r = opId_r(e); - int argsset = 0; - AggregateDeclaration ad1 = isAggregate(e.e1.type); - AggregateDeclaration ad2 = isAggregate(e.e2.type); - if (e.op == EXP.assign && ad1 == ad2) - { - StructDeclaration sd = ad1.isStructDeclaration(); - if (sd && - (!sd.hasIdentityAssign || - /* Do a blit if we can and the rvalue is something like .init, - * where a postblit is not necessary. - */ - (sd.hasBlitAssign && !e.e2.isLvalue()))) - { - /* This is bitwise struct assignment. */ - return null; - } - } - Dsymbol s = null; - Dsymbol s_r = null; - Objects* tiargs = null; - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) - { - // Bug4099 fix - if (ad1 && search_function(ad1, Id.opUnary)) - return null; + if (result) + return Expression.combine(e0, result); } - if (e.op != EXP.equal && e.op != EXP.notEqual && e.op != EXP.assign && e.op != EXP.plusPlus && e.op != EXP.minusMinus) + Lfallback: + if (maybeSlice && search_function(ad, Id.opSliceUnary)) { - /* Try opBinary and opBinaryRight + // Deal with $ + Expression e0; + auto ae2 = resolveOpDollar(sc, ae, ie, e0); + if (ae2.isErrorExp()) + return ae2; + /* Rewrite op(a[i..j]) as: + * a.opSliceUnary!(op)(i, j) */ - if (ad1) - { - s = search_function(ad1, Id.opBinary); - if (s && !s.isTemplateDeclaration()) - { - error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); - return ErrorExp.get(); - } - } - if (ad2) - { - s_r = search_function(ad2, Id.opBinaryRight); - if (s_r && !s_r.isTemplateDeclaration()) - { - error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); - return ErrorExp.get(); - } - if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 - s_r = null; - } - // Set tiargs, the template argument list, which will be the operator string - if (s || s_r) - { - id = Id.opBinary; - id_r = Id.opBinaryRight; - tiargs = opToArg(sc, e.op); - } - } - if (!s && !s_r) - { - // Try the D1-style operators, deprecated - if (ad1 && id) - { - s = search_function(ad1, id); - if (s && id != Id.assign) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - if (id == Id.postinc || id == Id.postdec) - error(e.loc, "`%s` is obsolete. Use `opUnary(string op)() if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - else - error(e.loc, "`%s` is obsolete. Use `opBinary(string op)(...) if (op == \"%s\")` instead.", id.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } - if (ad2 && id_r) - { - s_r = search_function(ad2, id_r); - // https://issues.dlang.org/show_bug.cgi?id=12778 - // If both x.opBinary(y) and y.opBinaryRight(x) found, - // and they are exactly same symbol, x.opBinary(y) should be preferred. - if (s_r && s_r == s) - s_r = null; - if (s_r) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - error(e.loc, "`%s` is obsolete. Use `opBinaryRight(string op)(...) if (op == \"%s\")` instead.", id_r.toChars(), EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - } + Expression result = ie ? + dotTemplateCall(ae.e1, Id.opSliceUnary, opToArg(sc, e.op), ie.lwr, ie.upr) : + dotTemplateCall(ae.e1, Id.opSliceUnary, opToArg(sc, e.op)); + + return Expression.combine(e0, result.expressionSemantic(sc)); } - Expressions* args1 = new Expressions(); - Expressions* args2 = new Expressions(); - if (s || s_r) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) { - /* Try: - * a.opfunc(b) - * b.opfunc_r(a) - * and see which is better. + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) */ - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - argsset = 1; - MatchAccumulator m; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - FuncDeclaration lastf = m.lastf; - if (s_r) - { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - if (tiargs) - goto L1; - m.lastf = null; - } - if (e.op == EXP.plusPlus || e.op == EXP.minusMinus) - { - // Kludge because operator overloading regards e++ and e-- - // as unary, but it's implemented as a binary. - // Rewrite (e1 ++ e2) as e1.postinc() - // Rewrite (e1 -- e2) as e1.postdec() - return build_overload(e.loc, sc, e.e1, null, m.lastf ? m.lastf : s); - } - else if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); - } - } - L1: - version (all) - { - // Retained for D1 compatibility - if (isCommutative(e.op) && !tiargs) - { - s = null; - s_r = null; - if (ad1 && id_r) - { - s_r = search_function(ad1, id_r); - } - if (ad2 && id) - { - s = search_function(ad2, id); - if (s && s == s_r) // https://issues.dlang.org/show_bug.cgi?id=12778 - s = null; - } - if (s || s_r) - { - /* Try: - * a.opfunc_r(b) - * b.opfunc(a) - * and see which is better. - */ - if (!argsset) - { - args1.setDim(1); - (*args1)[0] = e.e1; - expandTuples(args1); - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - } - MatchAccumulator m; - if (s_r) - { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - FuncDeclaration lastf = m.lastf; - if (s) - { - functionResolve(m, s, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) - { - m.lastf = null; - } - - if (lastf && m.lastf == lastf || !s && m.last == MATCH.nomatch) - { - // Rewrite (e1 op e2) as e1.opfunc_r(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s_r); - } - else - { - // Rewrite (e1 op e2) as e2.opfunc(e1) - Expression result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); - return result; - } - } - } + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; } + break; + } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + } + e.e1 = e.e1.expressionSemantic(sc); + e.e1 = resolveProperties(sc, e.e1); + Type att = null; // first cyclic `alias this` type + while (1) + { + if (e.e1.isErrorExp()) + { + return e.e1; + } - Expression rewrittenLhs; - if (!(e.op == EXP.assign && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 - { - if (Expression result = checkAliasThisForLhs(ad1, sc, e)) - { - /* https://issues.dlang.org/show_bug.cgi?id=19441 - * - * alias this may not be used for partial assignment. - * If a struct has a single member which is aliased this - * directly or aliased to a ref getter function that returns - * the mentioned member, then alias this may be - * used since the object will be fully initialised. - * If the struct is nested, the context pointer is considered - * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` - * condition. - */ - if (result.op != EXP.assign) - return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + break; - auto ae = result.isAssignExp(); - if (ae.e1.op != EXP.dotVariable) - return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` + /* Rewrite as: + * e1.opUnary!(op)() + */ + if (Dsymbol fd = search_function(ad, Id.opUnary)) + return dotTemplateCall(e.e1, Id.opUnary, opToArg(sc, e.op)).expressionSemantic(sc); - auto dve = ae.e1.isDotVarExp(); - if (auto ad = dve.var.isMember2()) - { - // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` - // Ensure that `var` is the only field member in `ad` - if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) - { - if (dve.var == ad.aliasthis.sym) - return result; - } - } - rewrittenLhs = ae.e1; - } - } - if (!(e.op == EXP.assign && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 - { - if (Expression result = checkAliasThisForRhs(ad2, sc, e)) - return result; - } - if (rewrittenLhs) + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + //printf("att una %s e1 = %s\n", EXPtoString(op).ptr, this.e1.type.toChars()); + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", - e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); - return ErrorExp.get(); + e.e1 = e1; + continue; } - return null; + break; } - Expression visitEqual(EqualExp e) + // For ++ and --, rewrites to += and -= are also tried, so don't error yet + if (!e.isPreExp()) { - //printf("EqualExp::op_overload() (%s)\n", e.toChars()); - Type t1 = e.e1.type.toBasetype(); - Type t2 = e.e2.type.toBasetype(); + error(e.loc, "operator `%s` is not defined for `%s`", EXPtoString(e.op).ptr, ad.toChars()); + errorSupplemental(ad.loc, "perhaps overload the operator with `auto opUnary(string op : \"%s\")() {}`", + EXPtoString(e.op).ptr); + return ErrorExp.get(); + } - /* Array equality is handled by expressionSemantic() potentially - * lowering to object.__equals(), which takes care of overloaded - * operators for the element types. - */ - if ((t1.ty == Tarray || t1.ty == Tsarray) && - (t2.ty == Tarray || t2.ty == Tsarray)) - { - return null; - } + break; + } + return null; +} - /* Check for class equality with null literal or typeof(null). - */ - if (t1.ty == Tclass && e.e2.op == EXP.null_ || - t2.ty == Tclass && e.e1.op == EXP.null_) - { - error(e.loc, "use `%s` instead of `%s` when comparing with `null`", - EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, - EXPtoString(e.op).ptr); - return ErrorExp.get(); - } - if (t1.ty == Tclass && t2.ty == Tnull || - t1.ty == Tnull && t2.ty == Tclass) +Expression opOverloadArray(ArrayExp ae, Scope* sc) +{ + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].isIntervalExp()); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) + { + ie = (*ae.arguments)[0].isIntervalExp(); + } + Type att = null; // first cyclic `alias this` type + while (true) + { + if (ae.e1.isErrorExp()) + { + return ae.e1; + } + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + Type t1b = ae.e1.type.toBasetype(); + AggregateDeclaration ad = isAggregate(t1b); + if (!ad) + { + // If the non-aggregate expression ae.e1 is indexable or sliceable, + // convert it to the corresponding concrete expression. + if (isIndexableNonAggregate(t1b) || ae.e1.isTypeExp()) { - // Comparing a class with typeof(null) should not call opEquals - return null; - } + // Convert to SliceExp + if (maybeSlice) + return new SliceExp(ae.loc, ae.e1, ie).expressionSemantic(sc); - /* Check for class equality. + // Convert to IndexExp + if (ae.arguments.length == 1) + return new IndexExp(ae.loc, ae.e1, (*ae.arguments)[0]).expressionSemantic(sc); + } + break; + } + if (search_function(ad, Id.opIndex)) + { + // Deal with $ + auto ae2 = resolveOpDollar(sc, ae, e0); + if (!ae2) // a[i..j] might be: a.opSlice(i, j) + goto Lfallback; + if (ae2.isErrorExp()) + return ae2; + /* Rewrite e1[arguments] as: + * e1.opIndex(arguments) */ - if (t1.ty == Tclass && t2.ty == Tclass) - { - ClassDeclaration cd1 = t1.isClassHandle(); - ClassDeclaration cd2 = t2.isClassHandle(); - if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) - { - /* Rewrite as: - * .object.opEquals(e1, e2) - */ - if (!ClassDeclaration.object) - { - error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); - return null; - } - - Expression e1x = e.e1; - Expression e2x = e.e2; + Expressions* a = ae.arguments.copy(); + Expression result = new DotIdExp(ae.loc, ae.e1, Id.opIndex); + result = new CallExp(ae.loc, result, a); + if (maybeSlice) // a[] might be: a.opSlice() + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); - /* The explicit cast is necessary for interfaces - * https://issues.dlang.org/show_bug.cgi?id=4088 - */ - Type to = ClassDeclaration.object.getType(); - if (cd1.isInterfaceDeclaration()) - e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); - if (cd2.isInterfaceDeclaration()) - e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); - - Expression result = new IdentifierExp(e.loc, Id.empty); - result = new DotIdExp(e.loc, result, Id.object); - result = new DotIdExp(e.loc, result, Id.eq); - result = new CallExp(e.loc, result, e1x, e2x); - if (e.op == EXP.notEqual) - result = new NotExp(e.loc, result); - result = result.expressionSemantic(sc); - return result; - } - } + if (result) + return Expression.combine(e0, result); + } + Lfallback: + if (maybeSlice && ae.e1.isTypeExp()) + { + Expression result = new SliceExp(ae.loc, ae.e1, ie); + result = result.expressionSemantic(sc); + return Expression.combine(e0, result); + } + if (maybeSlice && search_function(ad, Id.opSlice)) + { + // Deal with $ + auto ae2 = resolveOpDollar(sc, ae, ie, e0); - if (Expression result = compare_overload(e, sc, Id.eq, null)) + if (ae2.isErrorExp()) { - if (lastComma(result).op == EXP.call && e.op == EXP.notEqual) - { - result = new NotExp(result.loc, result); - result = result.expressionSemantic(sc); - } - return result; - } + if (!e0 && !search_function(ad, Id.dollar)) + ad.loc.errorSupplemental("perhaps define `opDollar` for `%s`", ad.toChars()); - /* Check for pointer equality. - */ - if (t1.ty == Tpointer || t2.ty == Tpointer) - { - /* Rewrite: - * ptr1 == ptr2 - * as: - * ptr1 is ptr2 - * - * This is just a rewriting for deterministic AST representation - * as the backend input. - */ - auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; - Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); - return r.expressionSemantic(sc); + return ae2; } - - /* Check for struct equality without opEquals. + /* Rewrite a[i..j] as: + * a.opSlice(i, j) */ - if (t1.ty == Tstruct && t2.ty == Tstruct) + auto a = new Expressions(); + if (ie) { - auto sd = t1.isTypeStruct().sym; - if (sd != t2.isTypeStruct().sym) - return null; - - import dmd.clone : needOpEquals; - if (global.params.fieldwise != FeatureState.enabled && !needOpEquals(sd)) - { - // Use bitwise equality. - auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; - Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); - return r.expressionSemantic(sc); - } - - /* Do memberwise equality. - * https://dlang.org/spec/expression.html#equality_expressions - * Rewrite: - * e1 == e2 - * as: - * e1.tupleof == e2.tupleof - * - * If sd is a nested struct, and if it's nested in a class, it will - * also compare the parent class's equality. Otherwise, compares - * the identity of parent context through void*. - */ - e = e.copy().isEqualExp(); - e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); - e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); - - auto sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; - Expression r = e.expressionSemantic(sc2); - sc2.pop(); - return r; + a.push(ie.lwr); + a.push(ie.upr); } - - /* Check for tuple equality. + Expression result = new DotIdExp(ae.loc, ae.e1, Id.opSlice); + result = new CallExp(ae.loc, result, a); + result = result.expressionSemantic(sc); + return Expression.combine(e0, result); + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + { + //printf("att arr e1 = %s\n", this.e1.type.toChars()); + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) */ - if (e.e1.op == EXP.tuple && e.e2.op == EXP.tuple) - { - auto tup1 = e.e1.isTupleExp(); - auto tup2 = e.e2.isTupleExp(); - size_t dim = tup1.exps.length; - if (dim != tup2.exps.length) - { - error(e.loc, "mismatched sequence lengths, `%d` and `%d`", - cast(int)dim, cast(int)tup2.exps.length); - return ErrorExp.get(); - } + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; + } + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + return null; +} - Expression result; - if (dim == 0) - { - // zero-length tuple comparison should always return true or false. - result = IntegerExp.createBool(e.op == EXP.equal); - } - else - { - for (size_t i = 0; i < dim; i++) - { - auto ex1 = (*tup1.exps)[i]; - auto ex2 = (*tup2.exps)[i]; - auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); - - if (!result) - result = eeq; - else if (e.op == EXP.equal) - result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); - else - result = new LogicalExp(e.loc, EXP.orOr, result, eeq); - } - assert(result); - } - result = Expression.combine(tup1.e0, tup2.e0, result); - result = result.expressionSemantic(sc); +/*********************************************** + * This is mostly the same as opOverloadUnary but has + * a different rewrite. + */ +Expression opOverloadCast(CastExp e, Scope* sc, Type att = null) +{ + AggregateDeclaration ad = isAggregate(e.e1.type); + if (!ad) + return null; - return result; + // Rewrite as: e1.opCast!(T)() + if (Dsymbol fd = search_function(ad, Id.opCast)) + { + version (all) + { + // Backwards compatibility with D1 if opCast is a function, not a template + if (fd.isFuncDeclaration()) + { + // Rewrite as: e1.opCast() + return build_overload(e.loc, sc, e.e1, null, fd); } - return null; } - - Expression visitCmp(CmpExp e) + auto tiargs = new Objects(); + tiargs.push(e.to); + return dotTemplateCall(e.e1, Id.opCast, tiargs).expressionSemantic(sc); + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, e.e1.type)) + { + // Rewrite `e1.opCast()` as `e1.aliasthis.opCast()` + if (auto e1 = resolveAliasThis(sc, e.e1, true)) { - //printf("CmpExp:: () (%s)\n", e.toChars()); - return compare_overload(e, sc, Id.cmp, pop); + CastExp result = e.copy().isCastExp(); + result.e1 = e1; + return result.opOverloadCast(sc, att); } + } + return null; +} - /********************************* - * Operator overloading for op= - */ - Expression visitBinAssign(BinAssignExp e) +// When no operator overload functions are found for `e`, recursively try with `alias this` +// Returns: `null` when still no overload found, otherwise resolved lowering +Expression binAliasThis(BinExp e, Scope* sc, Type[2] aliasThisStop) +{ + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + Expression rewrittenLhs; + if (!(e.isAssignExp && ad2 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + { + if (Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop)) { - //printf("BinAssignExp::op_overload() (%s)\n", e.toChars()); - if (auto ae = e.e1.isArrayExp()) - { - ae.e1 = ae.e1.expressionSemantic(sc); - ae.e1 = resolveProperties(sc, ae.e1); - Expression ae1old = ae.e1; - const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].op == EXP.interval); - IntervalExp ie = null; - if (maybeSlice && ae.arguments.length) - { - ie = (*ae.arguments)[0].isIntervalExp(); - } - Type att = null; // first cyclic `alias this` type - while (true) - { - if (ae.e1.op == EXP.error) - { - return ae.e1; - } - Expression e0 = null; - Expression ae1save = ae.e1; - ae.lengthVar = null; - Type t1b = ae.e1.type.toBasetype(); - AggregateDeclaration ad = isAggregate(t1b); - if (!ad) - break; - if (search_function(ad, Id.opIndexOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, &e0); - if (!result) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) - goto Lfallback; - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite a[arguments] op= e2 as: - * a.opIndexOpAssign!(op)(e2, arguments) - */ - Expressions* a = ae.arguments.copy(); - a.insert(0, e.e2); - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opIndexOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) - result = result.trySemantic(sc); - else - result = result.expressionSemantic(sc); - if (result) - { - return Expression.combine(e0, result); - } - } - Lfallback: - if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) - { - // Deal with $ - Expression result = resolveOpDollar(sc, ae, ie, &e0); - if (result.op == EXP.error) - return result; - result = e.e2.expressionSemantic(sc); - if (result.op == EXP.error) - return result; - e.e2 = result; - /* Rewrite (a[i..j] op= e2) as: - * a.opSliceOpAssign!(op)(e2, i, j) - */ - auto a = new Expressions(); - a.push(e.e2); - if (ie) - { - a.push(ie.lwr); - a.push(ie.upr); - } - Objects* tiargs = opToArg(sc, e.op); - result = new DotTemplateInstanceExp(e.loc, ae.e1, Id.opSliceOpAssign, tiargs); - result = new CallExp(e.loc, result, a); - result = result.expressionSemantic(sc); - result = Expression.combine(e0, result); - return result; - } - // Didn't find it. Forward to aliasthis - if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) - { - /* Rewrite (a[arguments] op= e2) as: - * a.aliasthis[arguments] op= e2 - */ - ae.e1 = resolveAliasThis(sc, ae1save, true); - if (ae.e1) - continue; - } - break; - } - ae.e1 = ae1old; // recovery - ae.lengthVar = null; - } - Expression result = e.binSemanticProp(sc); - if (result) - return result; - // Don't attempt 'alias this' if an error occurred - if (e.e1.type.ty == Terror || e.e2.type.ty == Terror) - { - return ErrorExp.get(); - } - Identifier id = opId(e); - Expressions* args2 = new Expressions(); - AggregateDeclaration ad1 = isAggregate(e.e1.type); - Dsymbol s = null; - Objects* tiargs = null; - /* Try opOpAssign + /* https://issues.dlang.org/show_bug.cgi?id=19441 + * + * alias this may not be used for partial assignment. + * If a struct has a single member which is aliased this + * directly or aliased to a ref getter function that returns + * the mentioned member, then alias this may be + * used since the object will be fully initialised. + * If the struct is nested, the context pointer is considered + * one of the members, hence the `ad1.fields.length == 2 && ad1.vthis` + * condition. */ - if (ad1) - { - s = search_function(ad1, Id.opOpAssign); - if (s && !s.isTemplateDeclaration()) - { - error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); - return ErrorExp.get(); - } - } - // Set tiargs, the template argument list, which will be the operator string - if (s) - { - id = Id.opOpAssign; - tiargs = opToArg(sc, e.op); - } + auto ae = result.isAssignExp(); + if (!ae) + return result; // i.e: Rewrote `e1 = e2` -> `e1(e2)` - // Try D1-style operator overload, deprecated - if (!s && ad1 && id) - { - s = search_function(ad1, id); - if (s) - { - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.088, made an error in 2.100 - scope char[] op = EXPtoString(e.op).dup; - op[$-1] = '\0'; // remove trailing `=` - error(e.loc, "`%s` is obsolete. Use `opOpAssign(string op)(...) if (op == \"%s\")` instead.", id.toChars(), op.ptr); - return ErrorExp.get(); - } - } + auto dve = ae.e1.isDotVarExp(); + if (!dve) + return result; // i.e: Rewrote `e1 = e2` -> `e1() = e2` - if (s) + if (auto ad = dve.var.isMember2()) { - /* Try: - * a.opOpAssign(b) - */ - args2.setDim(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - { - return ErrorExp.get(); - } - if (m.count > 1) - { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); - } - else if (m.last == MATCH.nomatch) + // i.e: Rewrote `e1 = e2` -> `e1.some.var = e2` + // Ensure that `var` is the only field member in `ad` + if (ad.fields.length == 1 || (ad.fields.length == 2 && ad.vthis)) { - if (tiargs) - goto L1; - m.lastf = null; + if (dve.var == ad.aliasthis.sym) + return result; } - // Rewrite (e1 op e2) as e1.opOpAssign(e2) - return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); } - L1: - result = checkAliasThisForLhs(ad1, sc, e); - if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs - return result; + rewrittenLhs = ae.e1; + } + } + if (!(e.isAssignExp && ad1 && ad1 == ad2)) // https://issues.dlang.org/show_bug.cgi?id=2943 + { + if (Expression result = checkAliasThisForRhs(ad2, sc, e, aliasThisStop)) + return result; + } + if (rewrittenLhs) + { + error(e.loc, "cannot use `alias this` to partially initialize variable `%s` of type `%s`. Use `%s`", + e.e1.toChars(), ad1.toChars(), rewrittenLhs.toChars()); + return ErrorExp.get(); + } + return null; +} - return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); +Expression opOverloadAssign(AssignExp e, Scope* sc, Type[2] aliasThisStop) +{ + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + if (ad1 == ad2) + { + StructDeclaration sd = ad1.isStructDeclaration(); + if (sd && + (!sd.hasIdentityAssign || + /* Do a blit if we can and the rvalue is something like .init, + * where a postblit is not necessary. + */ + (sd.hasBlitAssign && !e.e2.isLvalue()))) + { + /* This is bitwise struct assignment. */ + return null; } + } + Dsymbol s = search_function(ad1, Id.opAssign); - if (pop) - *pop = e.op; + bool choseReverse; + if (auto result = pickBestBinaryOverload(sc, null, s, null, e, choseReverse)) + return result; - switch (e.op) - { - case EXP.cast_ : return visitCast(e.isCastExp()); - case EXP.array : return visitArray(e.isArrayExp()); + return binAliasThis(e, sc, aliasThisStop); +} - case EXP.notEqual : - case EXP.equal : return visitEqual(e.isEqualExp()); +Expression opOverloadBinary(BinExp e, Scope* sc, Type[2] aliasThisStop) +{ + if (Expression err = binSemanticProp(e, sc)) + return err; - case EXP.lessOrEqual : - case EXP.greaterThan : - case EXP.greaterOrEqual: - case EXP.lessThan : return visitCmp(cast(CmpExp)e); + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); - default: - if (auto ex = e.isBinAssignExp()) return visitBinAssign(ex); - if (auto ex = e.isBinExp()) return visitBin(ex); - if (auto ex = e.isUnaExp()) return visitUna(ex); - return visit(e); + // Try opBinary and opBinaryRight + Dsymbol s = search_function(ad1, Id.opBinary); + if (s && !s.isTemplateDeclaration()) + { + error(e.e1.loc, "`%s.opBinary` isn't a template", e.e1.toChars()); + return ErrorExp.get(); + } + + Dsymbol s_r = search_function(ad2, Id.opBinaryRight); + if (s_r && !s_r.isTemplateDeclaration()) + { + error(e.e2.loc, "`%s.opBinaryRight` isn't a template", e.e2.toChars()); + return ErrorExp.get(); } + if (s_r && s_r == s) // https://issues.dlang.org/show_bug.cgi?id=12778 + s_r = null; + + bool choseReverse; + if (auto result = pickBestBinaryOverload(sc, opToArg(sc, e.op), s, s_r, e, choseReverse)) + return result; + + return binAliasThis(e, sc, aliasThisStop); } -/****************************************** - * Common code for overloading of EqualExp and CmpExp +/** + * If applicable, print an error relating to implementing / fixing `opBinary` functions. + * Params: + * e = binary operation + * sc = scope to try `opBinary!""` semantic in for error messages + * Returns: `true` when an error related to `opBinary` was printed */ -private Expression compare_overload(BinExp e, Scope* sc, Identifier id, EXP* pop) +bool suggestBinaryOverloads(BinExp e, Scope* sc) { - //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); + if (!e.op.hasOpBinary) + return false; + AggregateDeclaration ad1 = isAggregate(e.e1.type); AggregateDeclaration ad2 = isAggregate(e.e2.type); - Dsymbol s = null; - Dsymbol s_r = null; + if (ad1) { - s = search_function(ad1, id); + if (Dsymbol s = search_function(ad1, Id.opBinary)) + { + // This expressionSemantic will fail, otherwise operator overloading would have succeeded before + dotTemplateCall(e.e1, Id.opBinary, opToArg(sc, e.op), e.e2).expressionSemantic(sc); + errorSupplemental(s.loc, "`opBinary` defined here"); + return true; + } + error(e.loc, "operator `%s` is not defined for type `%s`", EXPtoString(e.op).ptr, e.e1.type.toChars); + errorSupplemental(ad1.loc, "perhaps overload the operator with `auto opBinary(string op : \"%s\")(%s rhs) {}`", EXPtoString(e.op).ptr, e.e2.type.toChars); + return true; } - if (ad2) + else if (ad2) { - s_r = search_function(ad2, id); - if (s == s_r) - s_r = null; + if (Dsymbol s_r = search_function(ad1, Id.opBinaryRight)) + { + dotTemplateCall(e.e2, Id.opBinaryRight, opToArg(sc, e.op), e.e1).expressionSemantic(sc); + errorSupplemental(s_r.loc, "`opBinaryRight` defined here"); + return true; + } + error(e.loc, "operator `%s` is not defined for type `%s`", EXPtoString(e.op).ptr, e.e2.type.toChars); + errorSupplemental(ad2.loc, "perhaps overload the operator with `auto opBinaryRight(string op : \"%s\")(%s rhs) {}`", EXPtoString(e.op).ptr, e.e1.type.toChars); + return true; } - Objects* tiargs = null; - if (s || s_r) + return false; +} + +/** + * If applicable, print an error relating to implementing / fixing `opOpAssign` or `opUnary` functions. + * Params: + * exp = binary operation + * sc = scope to try `opOpAssign!""` semantic in for error messages + * parent = if `exp` was lowered from this `PreExp` or `PostExp`, mention `opUnary` as well + * Returns: `true` when an error related to `opOpAssign` was printed + */ +bool suggestOpOpAssign(BinAssignExp exp, Scope* sc, Expression parent) +{ + auto ad = isAggregate(exp.e1.type); + if (!ad) + return false; + + if (parent && (parent.isPreExp() || parent.isPostExp())) + { + error(exp.loc, "operator `%s` not supported for `%s` of type `%s`", EXPtoString(parent.op).ptr, exp.e1.toChars(), ad.toChars()); + errorSupplemental(ad.loc, + "perhaps implement `auto opUnary(string op : \"%s\")() {}`"~ + " or `auto opOpAssign(string op : \"%s\")(int) {}`", + EXPtoString(stripAssignOp(parent.op)).ptr, + EXPtoString(stripAssignOp(exp.op)).ptr + ); + return true; + } + + if (const s = search_function(ad, Id.opOpAssign)) + { + // This expressionSemantic will fail, otherwise operator overloading would have succeeded before + dotTemplateCall(exp.e1, Id.opOpAssign, opToArg(sc, exp.op), exp.e2).expressionSemantic(sc); + } + else + { + error(exp.loc, "operator `%s` not supported for `%s` of type `%s`", EXPtoString(exp.op).ptr, exp.e1.toChars(), ad.toChars()); + errorSupplemental(ad.loc, "perhaps implement `auto opOpAssign(string op : \"%s\")(%s) {}`", + EXPtoString(stripAssignOp(exp.op)).ptr, exp.e2.type.toChars()); + } + return true; +} + +// Helper to construct e.id!tiargs(args), e.g. `lhs.opBinary!"+"(rhs)` +private Expression dotTemplateCall(Expression e, Identifier id, Objects* tiargs, Expression[] args...) +{ + auto ti = new DotTemplateInstanceExp(e.loc, e, id, tiargs); + auto expressions = new Expressions(); + expressions.pushSlice(args); + return new CallExp(e.loc, ti, expressions); +} + +Expression opOverloadEqual(EqualExp e, Scope* sc, Type[2] aliasThisStop) +{ + Type t1 = e.e1.type.toBasetype(); + Type t2 = e.e2.type.toBasetype(); + + /* Array equality is handled by expressionSemantic() potentially + * lowering to object.__equals(), which takes care of overloaded + * operators for the element types. + */ + if (t1.isStaticOrDynamicArray() && t2.isStaticOrDynamicArray()) + { + return null; + } + + /* Check for class equality with null literal or typeof(null). + */ + if (t1.isTypeClass() && e.e2.isNullExp() || + t2.isTypeClass() && e.e1.isNullExp()) + { + error(e.loc, "use `%s` instead of `%s` when comparing with `null`", + EXPtoString(e.op == EXP.equal ? EXP.identity : EXP.notIdentity).ptr, + EXPtoString(e.op).ptr); + return ErrorExp.get(); + } + if (t1.isTypeClass() && t2.isTypeNull() || + t1.isTypeNull() && t2.isTypeClass()) + { + // Comparing a class with typeof(null) should not call opEquals + return null; + } + + /* Check for class equality. + */ + if (t1.isTypeClass() && t2.isTypeClass()) + { + ClassDeclaration cd1 = t1.isClassHandle(); + ClassDeclaration cd2 = t2.isClassHandle(); + if (!(cd1.classKind == ClassKind.cpp || cd2.classKind == ClassKind.cpp)) + { + /* Rewrite as: + * .object.opEquals(e1, e2) + */ + if (!ClassDeclaration.object) + { + error(e.loc, "cannot compare classes for equality because `object.Object` was not declared"); + return null; + } + + Expression e1x = e.e1; + Expression e2x = e.e2; + + /* The explicit cast is necessary for interfaces + * https://issues.dlang.org/show_bug.cgi?id=4088 + */ + Type to = ClassDeclaration.object.getType(); + if (cd1.isInterfaceDeclaration()) + e1x = new CastExp(e.loc, e.e1, t1.isMutable() ? to : to.constOf()); + if (cd2.isInterfaceDeclaration()) + e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); + + Expression result = new IdentifierExp(e.loc, Id.empty); + result = new DotIdExp(e.loc, result, Id.object); + result = new DotIdExp(e.loc, result, Id.opEquals); + result = new CallExp(e.loc, result, e1x, e2x); + if (e.op == EXP.notEqual) + result = new NotExp(e.loc, result); + result = result.expressionSemantic(sc); + return result; + } + } + + EXP cmpOp; + if (Expression result = compare_overload(e, sc, Id.opEquals, cmpOp, aliasThisStop)) { - /* Try: - * a.opEquals(b) - * b.opEquals(a) - * and see which is better. + if (lastComma(result).isCallExp() && e.op == EXP.notEqual) + { + result = new NotExp(result.loc, result); + result = result.expressionSemantic(sc); + } + return result; + } + + /* Check for pointer equality. + */ + if (t1.isTypePointer() || t2.isTypePointer()) + { + /* Rewrite: + * ptr1 == ptr2 + * as: + * ptr1 is ptr2 + * + * This is just a rewriting for deterministic AST representation + * as the backend input. */ - Expressions* args1 = new Expressions(1); - (*args1)[0] = e.e1; - expandTuples(args1); - Expressions* args2 = new Expressions(1); - (*args2)[0] = e.e2; - expandTuples(args2); - MatchAccumulator m; - if (0 && s && s_r) + auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; + Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); + return r.expressionSemantic(sc); + } + + /* Check for struct equality without opEquals. + */ + if (t1.isTypeStruct() && t2.isTypeStruct()) + { + auto sd = t1.isTypeStruct().sym; + if (sd != t2.isTypeStruct().sym) + return null; + + import dmd.clone : needOpEquals; + if (!sc.previews.fieldwise && !needOpEquals(sd)) { - printf("s : %s\n", s.toPrettyChars()); - printf("s_r: %s\n", s_r.toPrettyChars()); + // Use bitwise equality. + auto op2 = e.op == EXP.equal ? EXP.identity : EXP.notIdentity; + Expression r = new IdentityExp(op2, e.loc, e.e1, e.e2); + return r.expressionSemantic(sc); } - if (s) + + /* Do memberwise equality. + * https://dlang.org/spec/expression.html#equality_expressions + * Rewrite: + * e1 == e2 + * as: + * e1.tupleof == e2.tupleof + * + * If sd is a nested struct, and if it's nested in a class, it will + * also compare the parent class's equality. Otherwise, compares + * the identity of parent context through void*. + */ + e = e.copy().isEqualExp(); + e.e1 = new DotIdExp(e.loc, e.e1, Id._tupleof); + e.e2 = new DotIdExp(e.loc, e.e2, Id._tupleof); + + auto sc2 = sc.push(); + sc2.noAccessCheck = true; + Expression r = e.expressionSemantic(sc2); + sc2.pop(); + return r; + } + + /* Check for tuple equality. + */ + auto tup1 = e.e1.isTupleExp(); + auto tup2 = e.e2.isTupleExp(); + if (tup1 && tup2) + { + size_t dim = tup1.exps.length; + if (dim != tup2.exps.length) { - functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - return ErrorExp.get(); + error(e.loc, "mismatched sequence lengths, `%d` and `%d`", + cast(int)dim, cast(int)tup2.exps.length); + return ErrorExp.get(); } - FuncDeclaration lastf = m.lastf; - int count = m.count; - if (s_r) + + Expression result; + if (dim == 0) { - functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1)); - if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) - return ErrorExp.get(); + // zero-length tuple comparison should always return true or false. + result = IntegerExp.createBool(e.op == EXP.equal); } - if (m.count > 1) + else { - /* The following if says "not ambiguous" if there's one match - * from s and one from s_r, in which case we pick s. - * This doesn't follow the spec, but is a workaround for the case - * where opEquals was generated from templates and we cannot figure - * out if both s and s_r came from the same declaration or not. - * The test case is: - * import std.typecons; - * void main() { - * assert(tuple("has a", 2u) == tuple("has a", 1)); - * } - */ - if (!(m.lastf == lastf && m.count == 2 && count == 1)) + for (size_t i = 0; i < dim; i++) { - // Error, ambiguous - error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); + auto ex1 = (*tup1.exps)[i]; + auto ex2 = (*tup2.exps)[i]; + auto eeq = new EqualExp(e.op, e.loc, ex1, ex2); + + if (!result) + result = eeq; + else if (e.op == EXP.equal) + result = new LogicalExp(e.loc, EXP.andAnd, result, eeq); + else + result = new LogicalExp(e.loc, EXP.orOr, result, eeq); } + assert(result); } - else if (m.last == MATCH.nomatch) + result = Expression.combine(tup1.e0, tup2.e0, result); + result = result.expressionSemantic(sc); + + return result; + } + return null; +} + +Expression opOverloadCmp(CmpExp exp, Scope* sc, Type[2] aliasThisStop) +{ + //printf("CmpExp:: () (%s)\n", e.toChars()); + EXP cmpOp = exp.op; + auto e = compare_overload(exp, sc, Id.opCmp, cmpOp, aliasThisStop); + if (!e) + return null; + + if (!e.type.isScalar() && e.type.equals(exp.e1.type)) + { + error(e.loc, "recursive `opCmp` expansion"); + return ErrorExp.get(); + } + if (!e.isCallExp()) + return e; + + Type t1 = exp.e1.type.toBasetype(); + Type t2 = exp.e2.type.toBasetype(); + if (!t1.isTypeClass() || !t2.isTypeClass()) + { + return new CmpExp(cmpOp, exp.loc, e, IntegerExp.literal!0).expressionSemantic(sc); + } + + // Lower to object.__cmp(e1, e2) + Expression cl = new IdentifierExp(exp.loc, Id.empty); + cl = new DotIdExp(exp.loc, cl, Id.object); + cl = new DotIdExp(exp.loc, cl, Id.__cmp); + cl = cl.expressionSemantic(sc); + + auto arguments = new Expressions(); + // Check if op_overload found a better match by calling e2.opCmp(e1) + // If the operands were swapped, then the result must be reversed + // e1.opCmp(e2) == -e2.opCmp(e1) + // cmpop takes care of this + if (exp.op == cmpOp) + { + arguments.push(exp.e1); + arguments.push(exp.e2); + } + else + { + // Use better match found by op_overload + arguments.push(exp.e2); + arguments.push(exp.e1); + } + + cl = new CallExp(e.loc, cl, arguments); + cl = new CmpExp(cmpOp, exp.loc, cl, new IntegerExp(0)); + return cl.expressionSemantic(sc); +} + +/********************************* + * Operator overloading for op= + */ +Expression opOverloadBinaryAssign(BinAssignExp e, Scope* sc, Type[2] aliasThisStop) +{ + if (auto ae = e.e1.isArrayExp()) + { + ae.e1 = ae.e1.expressionSemantic(sc); + ae.e1 = resolveProperties(sc, ae.e1); + Expression ae1old = ae.e1; + const(bool) maybeSlice = (ae.arguments.length == 0 || ae.arguments.length == 1 && (*ae.arguments)[0].isIntervalExp()); + IntervalExp ie = null; + if (maybeSlice && ae.arguments.length) { - m.lastf = null; + ie = (*ae.arguments)[0].isIntervalExp(); } - Expression result; - if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + Type att = null; // first cyclic `alias this` type + while (true) { - // Rewrite (e1 op e2) as e1.opfunc(e2) - result = build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + if (ae.e1.isErrorExp()) + return ae.e1; + + Expression e0 = null; + Expression ae1save = ae.e1; + ae.lengthVar = null; + AggregateDeclaration ad = isAggregate(ae.e1.type); + if (!ad) + break; + if (search_function(ad, Id.opIndexOpAssign)) + { + // Deal with $ + Expression ae2 = resolveOpDollar(sc, ae, e0); + if (!ae2) // (a[i..j] op= e2) might be: a.opSliceOpAssign!(op)(e2, i, j) + goto Lfallback; + if (ae2.isErrorExp()) + return ae2; + e.e2 = e.e2.expressionSemantic(sc); + if (e.e2.isErrorExp()) + return e.e2; + + /* Rewrite a[arguments] op= e2 as: + * a.opIndexOpAssign!(op)(e2, arguments) + */ + Expressions* a = ae.arguments.copy(); + a.insert(0, e.e2); + Expression result = dotTemplateCall(ae.e1, Id.opIndexOpAssign, opToArg(sc, e.op), (*a)[]); + if (maybeSlice) // (a[] op= e2) might be: a.opSliceOpAssign!(op)(e2) + result = result.trySemantic(sc); + else + result = result.expressionSemantic(sc); + + if (result) + return Expression.combine(e0, result); + } + Lfallback: + if (maybeSlice && search_function(ad, Id.opSliceOpAssign)) + { + // Deal with $ + Expression ae2 = resolveOpDollar(sc, ae, ie, e0); + if (ae2.isErrorExp()) + return ae2; + + e.e2 = e.e2.expressionSemantic(sc); + if (e.e2.isErrorExp()) + return e.e2; + + /* Rewrite (a[i..j] op= e2) as: + * a.opSliceOpAssign!(op)(e2, i, j) + */ + auto result = ie ? + dotTemplateCall(ae.e1, Id.opSliceOpAssign, opToArg(sc, e.op), e.e2, ie.lwr, ie.upr) : + dotTemplateCall(ae.e1, Id.opSliceOpAssign, opToArg(sc, e.op), e.e2); + + return Expression.combine(e0, result.expressionSemantic(sc)); + } + // Didn't find it. Forward to aliasthis + if (ad.aliasthis && !isRecursiveAliasThis(att, ae.e1.type)) + { + /* Rewrite (a[arguments] op= e2) as: + * a.aliasthis[arguments] op= e2 + */ + ae.e1 = resolveAliasThis(sc, ae1save, true); + if (ae.e1) + continue; + } + break; } - else + ae.e1 = ae1old; // recovery + ae.lengthVar = null; + } + + if (Expression result = e.binSemanticProp(sc)) + return result; + + // Don't attempt 'alias this' if an error occurred + if (e.e1.type.isTypeError() || e.e2.type.isTypeError()) + return ErrorExp.get(); + + AggregateDeclaration ad1 = isAggregate(e.e1.type); + Dsymbol s = search_function(ad1, Id.opOpAssign); + if (s && !s.isTemplateDeclaration()) + { + error(e.loc, "`%s.opOpAssign` isn't a template", e.e1.toChars()); + return ErrorExp.get(); + } + + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, opToArg(sc, e.op), s, null, e, choseReverse)) + return res; + + Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop); + if (result || !s) // no point in trying Rhs alias-this if there's no overload of any kind in lhs + return result; + + return checkAliasThisForRhs(isAggregate(e.e2.type), sc, e, aliasThisStop); +} + +/** +Given symbols `s` and `s_r`, try to instantiate `e.e1.s!tiargs(e.e2)` and `e.e2.s_r!tiargs(e.e1)`, +and return the one with the best match level. + +Params: + sc = scope + tiargs = (optional) template arguments to instantiate symbols with + s = (optional) symbol of straightforward template (e.g. opBinary) + s_r = (optional) symbol of reversed template (e.g. opBinaryRight) + e = binary expression being overloaded, supplying arguments to the function calls + choseReverse = set to true when `s_r` was chosen instead of `s` +Returns: + Resulting operator overload function call, or `null` if neither symbol worked +*/ +private Expression pickBestBinaryOverload(Scope* sc, Objects* tiargs, Dsymbol s, Dsymbol s_r, BinExp e, out bool choseReverse) +{ + if (!s && !s_r) + return null; + + Expressions* args1 = new Expressions(1); + (*args1)[0] = e.e1; + expandTuples(args1); + Expressions* args2 = new Expressions(1); + (*args2)[0] = e.e2; + expandTuples(args2); + MatchAccumulator m; + + if (s) + { + functionResolve(m, s, e.loc, sc, tiargs, e.e1.type, ArgumentList(args2), null); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + return ErrorExp.get(); + } + FuncDeclaration lastf = m.lastf; + int count = m.count; + if (s_r) + { + functionResolve(m, s_r, e.loc, sc, tiargs, e.e2.type, ArgumentList(args1), null); + if (m.lastf && (m.lastf.errors || m.lastf.hasSemantic3Errors())) + return ErrorExp.get(); + } + if (m.count > 1) + { + /* The following if says "not ambiguous" if there's one match + * from s and one from s_r, in which case we pick s. + * This doesn't follow the spec, but is a workaround for the case + * where opEquals was generated from templates and we cannot figure + * out if both s and s_r came from the same declaration or not. + * The test case is: + * import std.typecons; + * void main() { + * assert(tuple("has a", 2u) == tuple("has a", 1)); + * } + */ + if (!(m.lastf == lastf && m.count == 2 && count == 1)) { - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - result = build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); - // When reversing operands of comparison operators, - // need to reverse the sense of the op - if (pop) - *pop = reverseRelation(e.op); + // Error, ambiguous + error(e.loc, "overloads `%s` and `%s` both match argument list for `%s`", m.lastf.type.toChars(), m.nextf.type.toChars(), m.lastf.toChars()); } - return result; } + else if (m.last == MATCH.nomatch) + { + if (tiargs) + return null; + m.lastf = null; + } + + if (lastf && m.lastf == lastf || !s_r && m.last == MATCH.nomatch) + { + choseReverse = false; + // Rewrite (e1 op e2) as e1.opfunc(e2) + return build_overload(e.loc, sc, e.e1, e.e2, m.lastf ? m.lastf : s); + } + else + { + choseReverse = true; + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + return build_overload(e.loc, sc, e.e2, e.e1, m.lastf ? m.lastf : s_r); + } +} + +/****************************************** + * Common code for overloading of EqualExp and CmpExp + */ +private Expression compare_overload(BinExp e, Scope* sc, Identifier id, ref EXP cmpOp, Type[2] aliasThisStop) +{ + //printf("BinExp::compare_overload(id = %s) %s\n", id.toChars(), e.toChars()); + AggregateDeclaration ad1 = isAggregate(e.e1.type); + AggregateDeclaration ad2 = isAggregate(e.e2.type); + Dsymbol s = search_function(ad1, id); + Dsymbol s_r = search_function(ad2, id); + + if (s == s_r) + s_r = null; + + bool choseReverse; + if (auto res = pickBestBinaryOverload(sc, null, s, s_r, e, choseReverse)) + { + if (choseReverse) + cmpOp = reverseRelation(e.op); + return res; + } + /* * https://issues.dlang.org/show_bug.cgi?id=16657 * at this point, no matching opEquals was found for structs, * so we should not follow the alias this comparison code. */ - if ((e.op == EXP.equal || e.op == EXP.notEqual) && ad1 == ad2) + if (e.isEqualExp() && ad1 == ad2) + return null; + Expression result = checkAliasThisForLhs(ad1, sc, e, aliasThisStop); + if (result) + return result; + + result = checkAliasThisForRhs(isAggregate(e.e2.type), sc, e, aliasThisStop); + if (result) + return result; + + if (s || s_r) return null; - Expression result = checkAliasThisForLhs(ad1, sc, e); - return result ? result : checkAliasThisForRhs(isAggregate(e.e2.type), sc, e); + + Expression suggestOverloading(Expression other, AggregateDeclaration ad) + { + error(e.loc, "no operator `%s` for type `%s`", EXPtoString(e.op).ptr, ad.toChars); + string op = e.isEqualExp() ? "bool" : "int"; + errorSupplemental(ad.loc, "perhaps overload it with `%.*s %s(%s other) const {}`", op.fTuple.expand, id.toChars, other.type.toChars); + return ErrorExp.get(); + } + + // Classes have opCmp and opEquals defined in `Object` to fall back on already + if (ad1 && ad1.isStructDeclaration) + return suggestOverloading(e.e2, ad1); + if (ad2 && ad2.isStructDeclaration) + return suggestOverloading(e.e1, ad2); + + return null; } /*********************************** * Utility to build a function call out of this reference and argument. */ -Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) +Expression build_overload(Loc loc, Scope* sc, Expression ethis, Expression earg, Dsymbol d) { assert(d); Expression e; - Declaration decl = d.isDeclaration(); - if (decl) + if (Declaration decl = d.isDeclaration()) e = new DotVarExp(loc, ethis, decl, false); else e = new DotIdExp(loc, ethis, d.ident); @@ -1426,17 +1175,17 @@ Expression build_overload(const ref Loc loc, Scope* sc, Expression ethis, Expres */ Dsymbol search_function(ScopeDsymbol ad, Identifier funcid) { - Dsymbol s = ad.search(Loc.initial, funcid); - if (s) + if (!ad) + return null; + if (Dsymbol s = ad.search(Loc.initial, funcid)) { //printf("search_function: s = '%s'\n", s.kind()); Dsymbol s2 = s.toAlias(); //printf("search_function: s2 = '%s'\n", s2.kind()); FuncDeclaration fd = s2.isFuncDeclaration(); - if (fd && fd.type.ty == Tfunction) + if (fd && fd.type.isTypeFunction()) return fd; - TemplateDeclaration td = s2.isTemplateDeclaration(); - if (td) + if (TemplateDeclaration td = s2.isTemplateDeclaration()) return td; } return null; @@ -1465,7 +1214,7 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out aggr = aggr.expressionSemantic(sc); aggr = resolveProperties(sc, aggr); aggr = aggr.optimize(WANTvalue); - if (!aggr.type || aggr.op == EXP.error) + if (!aggr.type || aggr.isErrorExp()) return false; Type tab = aggr.type.toBasetype(); switch (tab.ty) @@ -1479,8 +1228,7 @@ bool inferForeachAggregate(Scope* sc, bool isForeach, ref Expression feaggr, out case Tclass: case Tstruct: { - AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym - : tab.isTypeStruct().sym; + AggregateDeclaration ad = isAggregate(tab); if (!sliced) { sapply = search_function(ad, isForeach ? Id.apply : Id.applyReverse); @@ -1570,11 +1318,11 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) // Determine ethis for sapply Expression ethis; Type tab = fes.aggr.type.toBasetype(); - if (tab.ty == Tclass || tab.ty == Tstruct) + if (tab.isTypeClass() || tab.isTypeStruct()) ethis = fes.aggr; else { - assert(tab.ty == Tdelegate && fes.aggr.op == EXP.delegate_); + assert(tab.isTypeDelegate() && fes.aggr.isDelegateExp()); ethis = fes.aggr.isDelegateExp().e1; } @@ -1614,7 +1362,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) } p = (*fes.parameters)[1]; } - if (!p.type && tab.ty != Ttuple) + if (!p.type && !tab.isTypeTuple()) { p.type = tab.nextOf(); // value type p.type = p.type.addStorageClass(p.storageClass); @@ -1646,8 +1394,7 @@ bool inferApplyArgTypes(ForeachStatement fes, Scope* sc, ref Dsymbol sapply) case Tclass: case Tstruct: { - AggregateDeclaration ad = (tab.ty == Tclass) ? tab.isTypeClass().sym - : tab.isTypeStruct().sym; + AggregateDeclaration ad = isAggregate(tab); if (fes.parameters.length == 1) { if (!p.type) diff --git a/gcc/d/dmd/optimize.d b/gcc/d/dmd/optimize.d index 2c89a58..66b8c6a 100644 --- a/gcc/d/dmd/optimize.d +++ b/gcc/d/dmd/optimize.d @@ -1,12 +1,12 @@ /** * Perform constant folding. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d, _optimize.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/optimize.d, _optimize.d) * Documentation: https://dlang.org/phobos/dmd_optimize.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/optimize.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/optimize.d */ module dmd.optimize; @@ -84,7 +84,7 @@ Expression expandVar(int result, VarDeclaration v) { Type tb = v.type.toBasetype(); if (v.storage_class & STC.manifest || - tb.isscalar() || + tb.isScalar() || ((result & WANTexpand) && (tb.ty != Tsarray && tb.ty != Tstruct))) { if (v._init) @@ -110,7 +110,7 @@ Expression expandVar(int result, VarDeclaration v) } if (ei.op == EXP.construct || ei.op == EXP.blit) { - AssignExp ae = cast(AssignExp)ei; + auto ae = cast(AssignExp)ei; ei = ae.e2; if (ei.isConst() == 1) { @@ -183,31 +183,29 @@ private Expression fromConstInitializer(int result, Expression e1) { //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars()); //static int xx; if (xx++ == 10) assert(0); + auto ve = e1.isVarExp(); + if (!ve) + return e1; + Expression e = e1; - if (auto ve = e1.isVarExp()) + VarDeclaration v = ve.var.isVarDeclaration(); + e = expandVar(result, v); + if (!e) + return e1; + + // If it is a comma expression involving a declaration, we mustn't + // perform a copy -- we'd get two declarations of the same variable. + // See https://issues.dlang.org/show_bug.cgi?id=4465. + if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) + e = e1; + else if (e.type != e1.type && e1.type && e1.type.ty != Tident) { - VarDeclaration v = ve.var.isVarDeclaration(); - e = expandVar(result, v); - if (e) - { - // If it is a comma expression involving a declaration, we mustn't - // perform a copy -- we'd get two declarations of the same variable. - // See https://issues.dlang.org/show_bug.cgi?id=4465. - if (e.op == EXP.comma && e.isCommaExp().e1.isDeclarationExp()) - e = e1; - else if (e.type != e1.type && e1.type && e1.type.ty != Tident) - { - // Type 'paint' operation - e = e.copy(); - e.type = e1.type; - } - e.loc = e1.loc; - } - else - { - e = e1; - } + // Type 'paint' operation + e = e.copy(); + e.type = e1.type; } + e.loc = e1.loc; + return e; } @@ -372,10 +370,10 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitStructLiteral(StructLiteralExp e) { - if (e.stageflags & stageOptimize) + if (e.stageflags & StructLiteralExp.StageFlags.optimize) return; const old = e.stageflags; - e.stageflags |= stageOptimize; + e.stageflags |= StructLiteralExp.StageFlags.optimize; if (e.elements) { foreach (ref ex; (*e.elements)[]) @@ -753,6 +751,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitNew(NewExp e) { + expOptimize(e.placement, WANTvalue); expOptimize(e.thisexp, WANTvalue); // Optimize parameters if (e.arguments) @@ -1004,7 +1003,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) } } - extern (D) void shift_optimize(BinExp e, UnionExp function(const ref Loc, Type, Expression, Expression) shift) + extern (D) void shift_optimize(BinExp e, UnionExp function(Loc, Type, Expression, Expression) shift) { if (binOptimize(e, result)) return; @@ -1071,7 +1070,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) if (binOptimize(e, result)) return; // All negative integral powers are illegal. - if (e.e1.type.isintegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0) + if (e.e1.type.isIntegral() && (e.e2.op == EXP.int64) && cast(sinteger_t)e.e2.toInteger() < 0) { error(e.loc, "cannot raise `%s` to a negative integer power. Did you mean `(cast(real)%s)^^%s` ?", e.e1.type.toBasetype().toChars(), e.e1.toChars(), e.e2.toChars()); return errorReturn(); @@ -1080,7 +1079,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) if (e.e2.op == EXP.float64 && e.e2.toReal() == real_t(cast(sinteger_t)e.e2.toReal())) { // This only applies to floating point, or positive integral powers. - if (e.e1.type.isfloating() || cast(sinteger_t)e.e2.toInteger() >= 0) + if (e.e1.type.isFloating() || cast(sinteger_t)e.e2.toInteger() >= 0) e.e2 = new IntegerExp(e.loc, e.e2.toInteger(), Type.tint64); } if (e.e1.isConst() == 1 && e.e2.isConst() == 1) @@ -1238,18 +1237,21 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) if (expOptimize(e.e1, WANTvalue)) return; const oror = e.op == EXP.orOr; - if (e.e1.toBool().hasValue(oror)) + void returnE_e1() { - // Replace with (e1, oror) - ret = IntegerExp.createBool(oror); - ret = Expression.combine(e.e1, ret); - if (e.type.toBasetype().ty == Tvoid) + ret = e.e1; + if (!e.e1.type.equals(e.type)) { - ret = new CastExp(e.loc, ret, Type.tvoid); + ret = new CastExp(e.loc, ret, e.type); ret.type = e.type; + ret = optimize(ret, result, false); } - ret = optimize(ret, result, false); - return; + } + if (e.e1.toBool().hasValue(oror)) + { + // e_true || e2 -> e_true + // e_false && e2 -> e_false + return returnE_e1(); } expOptimize(e.e2, WANTvalue); if (e.e1.isConst()) @@ -1263,6 +1265,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) } else if (e1Opt.hasValue(!oror)) { + if (e.type.toBasetype().ty == Tvoid) ret = e.e2; else @@ -1272,6 +1275,29 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) } } } + else if (e.e2.isConst()) + { + const e2Opt = e.e2.toBool(); + if (e2Opt.hasValue(oror)) + { + // e1 || true -> (e1, true) + // e1 && false -> (e1, false) + ret = IntegerExp.createBool(oror); + ret = Expression.combine(e.e1, ret); + if (e.type.toBasetype().ty == Tvoid) + { + ret = new CastExp(e.loc, ret, Type.tvoid); + ret.type = e.type; + } + ret = optimize(ret, result, false); + } + else if (e2Opt.hasValue(!oror)) + { + // e1 || false -> e1 + // e1 && true -> e1 + return returnE_e1(); + } + } } void visitCmp(CmpExp e) diff --git a/gcc/d/dmd/parse.d b/gcc/d/dmd/parse.d index 646c4b7..e68017c 100644 --- a/gcc/d/dmd/parse.d +++ b/gcc/d/dmd/parse.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/grammar.html, D Grammar) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/parse.d, _parse.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/parse.d, _parse.d) * Documentation: https://dlang.org/phobos/dmd_parse.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parse.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/parse.d */ module dmd.parse; @@ -49,14 +49,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer bool doUnittests; // parse unittest blocks } - bool transitionIn = false; /// `-transition=in` is active, `in` parameters are listed - /********************* * Use this constructor for string mixins. * Input: * loc = location in source file of mixin */ - extern (D) this(const ref Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, + extern (D) this(Loc loc, AST.Module _module, const(char)[] input, bool doDocComment, ErrorSink errorSink, const CompileEnv* compileEnv, const bool doUnittests) scope { //printf("Parser::Parser()1 %d\n", doUnittests); @@ -110,43 +108,44 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer parseModuleAttributes(msg, isdeprecated); // ModuleDeclaration leads off - if (token.value == TOK.module_) + if (token.value != TOK.module_) + return true; + + const loc = token.loc; + nextToken(); + + /* parse ModuleFullyQualifiedName + * https://dlang.org/spec/module.html#ModuleFullyQualifiedName + */ + + if (token.value != TOK.identifier) { - const loc = token.loc; - nextToken(); + error("identifier expected following `module`"); + return false; + } - /* parse ModuleFullyQualifiedName - * https://dlang.org/spec/module.html#ModuleFullyQualifiedName - */ + Identifier[] a; + Identifier id = token.ident; + while (nextToken() == TOK.dot) + { + a ~= id; + nextToken(); if (token.value != TOK.identifier) { - error("identifier expected following `module`"); + error("identifier expected following `package`"); return false; } + id = token.ident; + } - Identifier[] a; - Identifier id = token.ident; + md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); - while (nextToken() == TOK.dot) - { - a ~= id; - nextToken(); - if (token.value != TOK.identifier) - { - error("identifier expected following `package`"); - return false; - } - id = token.ident; - } - - md = new AST.ModuleDeclaration(loc, a, id, msg, isdeprecated); + if (token.value != TOK.semicolon) + error("`;` expected following module declaration instead of `%s`", token.toChars()); + nextToken(); + addComment(mod, comment); - if (token.value != TOK.semicolon) - error("`;` expected following module declaration instead of `%s`", token.toChars()); - nextToken(); - addComment(mod, comment); - } return true; } @@ -233,6 +232,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } else { + static if (is(typeof(mod.edition))) + if (exps && exps.length > 0) + if (auto id = (*exps)[0].isIdentifierExp()) + if (id.ident == Id.__edition_latest_do_not_use) + { + mod.edition = Edition.latest; + continue; + } + udas = AST.UserAttributeDeclaration.concat(udas, exps); } if (stc) @@ -319,7 +327,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer pAttrs.comment = token.blockComment.ptr; } AST.Visibility.Kind prot; - StorageClass stc; + STC stc; AST.Condition condition; linkage = linksave; @@ -528,12 +536,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } // Workaround 14894. Add an empty unittest declaration to keep // the number of symbols in this scope independent of -unittest. - s = new AST.UnitTestDeclaration(loc, token.loc, STC.undefined_, null); + s = new AST.UnitTestDeclaration(loc, token.loc, STC.none, null); } break; case TOK.new_: - s = parseNew(pAttrs); + s = parseNewDeclaration(pAttrs); break; case TOK.colon: @@ -642,6 +650,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; goto Lstc; case TOK.scope_: @@ -734,7 +744,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer a = parseBlock(pLastDecl, pAttrs); auto stc2 = getStorageClass!AST(pAttrs); - if (stc2 != STC.undefined_) + if (stc2 != STC.none) { s = new AST.StorageClassDeclaration(scdLoc, stc2, a); } @@ -922,22 +932,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { const attrLoc = token.loc; - nextToken(); - - AST.Expression e = null; // default - if (token.value == TOK.leftParenthesis) - { - nextToken(); - e = parseAssignExp(); - check(TOK.rightParenthesis); - } + AST.Expression e = parseAlign(); if (pAttrs.setAlignment) { if (e) error("redundant alignment attribute `align(%s)`", e.toChars()); else - error("redundant alignment attribute `align`"); + error("redundant alignment attribute `align(default)`"); } pAttrs.setAlignment = true; @@ -1039,6 +1041,30 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); continue; + // The following are all errors, the cases are just for better error messages than the default case + case TOK.return_: + case TOK.goto_: + case TOK.break_: + case TOK.continue_: + error("`%s` statement must be inside function scope", token.toChars()); + goto Lerror; + case TOK.asm_: + case TOK.do_: + case TOK.for_: + case TOK.foreach_: + case TOK.foreach_reverse_: + case TOK.if_: + case TOK.switch_: + case TOK.try_: + case TOK.while_: + error("`%s` statement must be inside function scope", token.toChars()); + if (peekNext() == TOK.leftParenthesis || peekNext() == TOK.leftCurly) + { + parseStatement(0); + s = null; + continue; + } + goto Lerror; default: error("declaration expected, not `%s`", token.toChars()); Lerror: @@ -1075,7 +1101,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Starts with token on the first ident. * Ends with scanner past closing ';' */ - private AST.Dsymbols* parseAutoDeclarations(StorageClass storageClass, const(char)* comment) + private AST.Dsymbols* parseAutoDeclarations(STC storageClass, const(char)* comment) { //printf("parseAutoDeclarations\n"); auto a = new AST.Dsymbols(); @@ -1190,9 +1216,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Returns: * The combination of both storage classes (`orig | added`). */ - private StorageClass appendStorageClass(StorageClass orig, StorageClass added) + private STC appendStorageClass(STC orig, STC added) { - void checkConflictSTCGroup(bool at = false)(StorageClass group) + void checkConflictSTCGroup(bool at = false)(STC group) { if (added & group && orig & group & ((orig & group) - 1)) error( @@ -1282,13 +1308,13 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * If the attribute is builtin, the return value will be non-zero. * Otherwise, 0 is returned, and `pudas` will be appended to. */ - private StorageClass parseAttribute(ref AST.Expressions* udas) + private STC parseAttribute(ref AST.Expressions* udas) { nextToken(); if (token.value == TOK.identifier) { // If we find a builtin attribute, we're done, return immediately. - if (StorageClass stc = isBuiltinAtAttribute(token.ident)) + if (STC stc = isBuiltinAtAttribute(token.ident)) return stc; // Allow identifier, template instantiation, or function call @@ -1306,10 +1332,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (udas is null) udas = new AST.Expressions(); udas.push(exp); - return 0; + return STC.none; } - AST.Expression templateArgToExp(RootObject o, const ref Loc loc) + AST.Expression templateArgToExp(RootObject o, Loc loc) { switch (o.dyncast) { @@ -1333,7 +1359,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto args = parseTemplateArgumentList(); foreach (arg; *args) udas.push(templateArgToExp(arg, token.loc)); - return 0; + return STC.none; } if (auto o = parseTemplateSingleArgument()) @@ -1341,7 +1367,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (udas is null) udas = new AST.Expressions(); udas.push(templateArgToExp(o, token.loc)); - return 0; + return STC.none; } if (token.isKeyword()) @@ -1349,17 +1375,17 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else error("`@identifier` or `@(ArgumentList)` expected, not `@%s`", token.toChars()); - return 0; + return STC.none; } /*********************************************** * Parse const/immutable/shared/inout/nothrow/pure postfix */ - private StorageClass parsePostfix(StorageClass storageClass, AST.Expressions** pudas) + private STC parsePostfix(STC storageClass, AST.Expressions** pudas) { while (1) { - StorageClass stc; + STC stc; switch (token.value) { case TOK.const_: @@ -1396,6 +1422,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer stc = STC.scope_; break; + case TOK.rvalue: + stc = STC.rvalue; + break; + case TOK.at: { AST.Expressions* udas = null; @@ -1432,16 +1462,16 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } - private StorageClass parseTypeCtor() + private STC parseTypeCtor() { - StorageClass storageClass = STC.undefined_; + STC storageClass = STC.none; while (1) { if (peekNext() == TOK.leftParenthesis) return storageClass; - StorageClass stc; + STC stc; switch (token.value) { case TOK.const_: @@ -1502,28 +1532,26 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value != TOK.identifier) { error("identifier expected following `template`"); - goto Lerr; + return null; } id = token.ident; nextToken(); tpl = parseTemplateParameterList(); if (!tpl) - goto Lerr; + return null; constraint = parseConstraint(); if (token.value != TOK.leftCurly) { error("`{` expected after template parameter list, not `%s`", token.toChars()); /* } */ - goto Lerr; + nextToken(); + return null; } decldefs = parseBlock(null); tempdecl = new AST.TemplateDeclaration(loc, id, tpl, constraint, decldefs, ismixin); return tempdecl; - - Lerr: - return null; } /****************************************** @@ -1696,19 +1724,28 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * mixin Foo; * mixin Foo!(args); * mixin a.b.c!(args).Foo!(args); - * mixin Foo!(args) identifier; * mixin typeof(expr).identifier!(args); + * mixin Foo!(args) identifier; + * mixin identifier = Foo!(args); */ private AST.Dsymbol parseMixin() { AST.TemplateMixin tm; - Identifier id; + Identifier id, name; AST.Objects* tiargs; //printf("parseMixin()\n"); const locMixin = token.loc; nextToken(); // skip 'mixin' + // mixin Identifier = MixinTemplateName TemplateArguments; + if (token.value == TOK.identifier && peekNext() == TOK.assign) + { + name = token.ident; + nextToken(); + nextToken(); + } + auto loc = token.loc; AST.TypeQualified tqual = null; if (token.value == TOK.dot) @@ -1771,14 +1808,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); } - id = null; - if (token.value == TOK.identifier) + // mixin MixinTemplateName TemplateArguments Identifier; + if (!name && token.value == TOK.identifier) { - id = token.ident; + name = token.ident; nextToken(); } - tm = new AST.TemplateMixin(locMixin, id, tqual, tiargs); + tm = new AST.TemplateMixin(locMixin, name, tqual, tiargs); if (token.value != TOK.semicolon) error("`;` expected after `mixin`"); nextToken(); @@ -2135,11 +2172,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); if (id == Id.Windows) return returnLinkage(LINK.windows); - else if (id == Id.D) + if (id == Id.D) return returnLinkage(LINK.d); - else if (id == Id.System) + if (id == Id.System) return returnLinkage(LINK.system); - else if (id == Id.Objective) // Looking for tokens "Objective-C" + if (id == Id.Objective) // Looking for tokens "Objective-C" { if (token.value != TOK.min) return invalidLinkage(); @@ -2246,17 +2283,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); 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()); + error("identifier expected, not `%s`", token.toChars()); s = null; } nextToken(); @@ -2271,7 +2300,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private AST.Condition parseDebugCondition() { - uint level = 1; Identifier id = null; Loc loc = token.loc; @@ -2281,21 +2309,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()); + error("identifier expected inside `debug(...)`, not `%s`", token.toChars()); loc = token.loc; nextToken(); check(TOK.rightParenthesis); } - return new AST.DebugCondition(loc, mod, level, id); + return new AST.DebugCondition(loc, mod, id); } /************************************** @@ -2307,16 +2327,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); 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()); + error("identifier expected, not `%s`", token.toChars()); s = null; } nextToken(); @@ -2331,7 +2344,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private AST.Condition parseVersionCondition() { - uint level = 1; Identifier id = null; Loc loc; @@ -2346,26 +2358,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer loc = token.loc; 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_) id = Identifier.idPool(Token.toString(TOK.assert_)); else - error("identifier or integer expected inside `version(...)`, not `%s`", token.toChars()); + error("identifier expected inside `version(...)`, not `%s`", token.toChars()); nextToken(); check(TOK.rightParenthesis); } else error("(condition) expected following `version`"); - return new AST.VersionCondition(loc, mod, level, id); + return new AST.VersionCondition(loc, mod, id); } /*********************************************** @@ -2411,7 +2415,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); if (token.value == TOK.leftParenthesis && peekNext() == TOK.this_ && peekNext2() == TOK.rightParenthesis) @@ -2462,7 +2466,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (stc & STC.static_) error(loc, "constructor cannot be static"); } - else if (StorageClass ss = stc & (STC.shared_ | STC.static_)) // this() + else if (STC ss = stc & (STC.shared_ | STC.static_)) // this() { if (ss == STC.static_) error(loc, "use `static this()` to declare a static constructor"); @@ -2472,7 +2476,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Expression constraint = tpl ? parseConstraint() : null; - AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // RetrunType -> auto + AST.Type tf = new AST.TypeFunction(parameterList, null, linkage, stc); // ReturnType -> auto tf = tf.addSTC(stc); auto f = new AST.CtorDeclaration(loc, Loc.initial, stc, tf); @@ -2504,7 +2508,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); check(TOK.this_); @@ -2512,7 +2516,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.rightParenthesis); stc = parsePostfix(stc, &udas); - if (StorageClass ss = stc & (STC.shared_ | STC.static_)) + if (STC ss = stc & (STC.shared_ | STC.static_)) { if (ss == STC.static_) error(loc, "use `static ~this()` to declare a static destructor"); @@ -2540,7 +2544,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { //Expressions *udas = NULL; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2552,7 +2556,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error(loc, "use `shared static this()` to declare a shared static constructor"); else if (stc & STC.static_) appendStorageClass(stc, STC.static_); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2574,7 +2578,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2587,7 +2591,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error(loc, "use `shared static ~this()` to declare a shared static destructor"); else if (stc & STC.static_) appendStorageClass(stc, STC.static_); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2615,7 +2619,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { //Expressions *udas = NULL; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2624,9 +2628,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.rightParenthesis); stc = parsePostfix(stc & ~STC.TYPECTOR, null) | stc; - if (StorageClass ss = stc & (STC.shared_ | STC.static_)) + if (STC ss = stc & (STC.shared_ | STC.static_)) appendStorageClass(stc, ss); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2648,7 +2652,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { AST.Expressions* udas = null; const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); nextToken(); @@ -2658,9 +2662,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer check(TOK.rightParenthesis); stc = parsePostfix(stc & ~STC.TYPECTOR, &udas) | stc; - if (StorageClass ss = stc & (STC.shared_ | STC.static_)) + if (STC ss = stc & (STC.shared_ | STC.static_)) appendStorageClass(stc, ss); // complaint for the redundancy - else if (StorageClass modStc = stc & STC.TYPECTOR) + else if (STC modStc = stc & STC.TYPECTOR) { OutBuffer buf; AST.stcToBuffer(buf, modStc); @@ -2689,7 +2693,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private AST.Dsymbol parseInvariant(PrefixAttributes!AST* pAttrs) { const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); if (token.value == TOK.leftParenthesis) // optional () or invariant (expression); @@ -2731,7 +2735,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer private AST.Dsymbol parseUnitTest(PrefixAttributes!AST* pAttrs) { const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); nextToken(); @@ -2770,44 +2774,19 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * @disable new(); * Current token is 'new'. */ - private AST.Dsymbol parseNew(PrefixAttributes!AST* pAttrs) + private AST.Dsymbol parseNewDeclaration(PrefixAttributes!AST* pAttrs) { const loc = token.loc; - StorageClass stc = getStorageClass!AST(pAttrs); + STC stc = getStorageClass!AST(pAttrs); if (!(stc & STC.disable)) { error("`new` allocator must be annotated with `@disabled`"); } nextToken(); - - /* @@@DEPRECATED_2.108@@@ - * After deprecation period (2.108), remove all code in the version(all) block. - */ - version (all) - { - auto parameterList = parseParameterList(null); // parameterList ignored - if (parameterList.parameters.length > 0 || parameterList.varargs != VarArg.none) - deprecation("`new` allocator with non-empty parameter list is deprecated"); - auto f = new AST.NewDeclaration(loc, stc); - if (token.value != TOK.semicolon) - { - deprecation("`new` allocator with function definition is deprecated"); - parseContracts(f); // body ignored - f.fbody = null; - f.fensures = null; - f.frequires = null; - } - else - nextToken(); - return f; - } - else - { - check(TOK.leftParenthesis); - check(TOK.rightParenthesis); - check(TOK.semicolon); - return new AST.NewDeclaration(loc, stc); - } + check(TOK.leftParenthesis); + check(TOK.rightParenthesis); + check(TOK.semicolon); + return new AST.NewDeclaration(loc, stc); } /********************************************** @@ -2817,7 +2796,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { auto parameters = new AST.Parameters(); VarArg varargs = VarArg.none; - StorageClass varargsStc; + STC varargsStc; // Attributes allowed for ... enum VarArgsStc = STC.const_ | STC.immutable_ | STC.shared_ | STC.scope_ | STC.return_ | STC.returnScope; @@ -2827,8 +2806,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { Identifier ai = null; AST.Type at; - StorageClass storageClass = 0; - StorageClass stc; + STC storageClass = STC.none; + STC stc; AST.Expression ae; AST.Expressions* udas = null; for (; 1; nextToken()) @@ -2880,7 +2859,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.at: { AST.Expressions* exps = null; - StorageClass stc2 = parseAttribute(exps); + STC stc2 = parseAttribute(exps); if (stc2 & atAttrGroup) { error("`@%s` attribute for function parameter is not supported", token.toChars()); @@ -2897,7 +2876,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // Don't call nextToken again. } case TOK.in_: - if (transitionIn) + if (compileEnv.transitionIn) eSink.message(scanloc, "Usage of 'in' on parameter"); stc = STC.in_; if (compileEnv.previewIn) @@ -2926,6 +2905,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; goto L2; case TOK.return_: @@ -2943,8 +2924,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // if stcx is not a power of 2 if (stcx & (stcx - 1) && !(stcx == (STC.in_ | STC.ref_))) error("incompatible parameter storage classes"); - //if ((storageClass & STC.scope_) && (storageClass & (STC.ref_ | STC.out_))) - //error("scope cannot be ref or out"); + + // Deprecated in 2.111 + if ((storageClass & STC.auto_) && (storageClass & STC.ref_) && !(storageClass & STC.autoref)) + deprecation("`auto` and `ref` storage classes should be adjacent"); const tv = peekNext(); Loc loc; @@ -2982,7 +2965,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.at) { AST.Expressions* exps = null; - StorageClass stc2 = parseAttribute(exps); + STC stc2 = parseAttribute(exps); if (stc2 & atAttrGroup) { error("`@%s` attribute for function parameter is not supported", token.toChars()); @@ -3080,7 +3063,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Identifier ident = null; AST.Expressions* udas; - StorageClass stc; + STC stc; AST.Expression deprecationMessage; enum attributeErrorMessage = "`%s` is not a valid attribute for enum members"; Lattrs: @@ -3089,7 +3072,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer switch (token.value) { case TOK.at: - if (StorageClass _stc = parseAttribute(udas)) + if (STC _stc = parseAttribute(udas)) { if (_stc == STC.disable) stc |= _stc; @@ -3476,7 +3459,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer name = _alias; _alias = null; } - s.addAlias(name, _alias); + if (s.isstatic) + error(loc, "static import `%s` cannot have an import bind list", s.toPrettyChars()); + if (!s.aliasId) + s.ident = null; // make it an anonymous import + s.names.push(name); + s.aliases.push(_alias); } while (token.value == TOK.comma); break; // no comma-separated imports of this form @@ -3515,7 +3503,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * shared inout type * shared inout const type */ - StorageClass stc = 0; + STC stc = STC.none; while (1) { switch (token.value) @@ -3999,7 +3987,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto parameterList = parseParameterList(null); - StorageClass stc = parsePostfix(STC.undefined_, null); + STC stc = parsePostfix(STC.none, null); auto tf = new AST.TypeFunction(parameterList, t, linkage, stc); if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild | STC.return_)) { @@ -4035,7 +4023,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Reference: https://dlang.org/spec/declaration.html#Declarator */ private AST.Type parseDeclarator(AST.Type t, ref int palt, Identifier* pident, - AST.TemplateParameters** tpl = null, StorageClass storageClass = 0, + AST.TemplateParameters** tpl = null, STC storageClass = STC.none, bool* pdisable = null, AST.Expressions** pudas = null) { //printf("parseDeclarator(tpl = %p)\n", tpl); @@ -4176,7 +4164,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer /* Parse const/immutable/shared/inout/nothrow/pure/return postfix */ // merge prefix storage classes - StorageClass stc = parsePostfix(storageClass, pudas); + STC stc = parsePostfix(storageClass, pudas); AST.Type tf = new AST.TypeFunction(parameterList, t, linkage, stc); tf = tf.addSTC(stc); @@ -4203,11 +4191,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return ts; } - private void parseStorageClasses(ref StorageClass storage_class, ref LINK link, + private void parseStorageClasses(ref STC storage_class, ref LINK link, ref bool setAlignment, ref AST.Expression ealign, ref AST.Expressions* udas, out Loc linkloc) { - StorageClass stc; + STC stc; bool sawLinkage = false; // seen a linkage declaration linkloc = Loc.initial; @@ -4250,6 +4238,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; goto L1; case TOK.scope_: @@ -4341,14 +4331,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } case TOK.align_: { - nextToken(); setAlignment = true; - if (token.value == TOK.leftParenthesis) - { - nextToken(); - ealign = parseExpression(); - check(TOK.rightParenthesis); - } + ealign = parseAlign(); continue; } default: @@ -4358,6 +4342,27 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } } + /** + * Parse `align` or `align(n)` + * Returns: + * expression `n` if it is present, or `null` otherwise. + */ + private AST.Expression parseAlign() + { + assert(token.value == TOK.align_); + AST.Expression e = null; + nextToken(); + if (token.value == TOK.leftParenthesis) + { + nextToken(); + if (token.value == TOK.default_) + nextToken(); + else + e = parseAssignExp(); + check(TOK.rightParenthesis); + } + return e; + } /********************************** * Parse Declarations. * These can be: @@ -4368,7 +4373,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private AST.Dsymbols* parseDeclarations(bool autodecl, PrefixAttributes!AST* pAttrs, const(char)* comment) { - StorageClass storage_class = STC.undefined_; + STC storage_class = STC.none; LINK link = linkage; Loc linkloc = this.linkLoc; bool setAlignment = false; @@ -4499,7 +4504,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (pAttrs) { storage_class |= pAttrs.storageClass; - //pAttrs.storageClass = STC.undefined_; + //pAttrs.storageClass = STC.none; } AST.Type tfirst = null; @@ -4530,7 +4535,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (ident) checkCstyleTypeSyntax(loc, t, alt, ident); else if (!isThis && (t != AST.Type.terror)) - noIdentifierForDeclarator(t); + noIdentifierForDeclarator(t, token); if (isAliasDeclaration) { @@ -4592,6 +4597,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: error("semicolon expected to close `alias` declaration, not `%s`", token.toChars()); + nextToken(); break; } } @@ -4604,9 +4610,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Expression constraint = null; //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t.toChars(), storage_class); - auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : 0), t); + auto f = new AST.FuncDeclaration(loc, Loc.initial, ident, storage_class | (disable ? STC.disable : STC.none), t); if (pAttrs) - pAttrs.storageClass = STC.undefined_; + pAttrs.storageClass = STC.none; if (tpl) constraint = parseConstraint(); AST.Dsymbol s = parseContracts(f, !!tpl); @@ -4642,7 +4648,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto tempdecl = new AST.TemplateDeclaration(loc, tplIdent, tpl, constraint, decldefs); s = tempdecl; - StorageClass stc2 = STC.undefined_; + STC stc2 = STC.none; if (storage_class & STC.static_) { assert(f.storage_class & STC.static_); @@ -4655,7 +4661,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer f.storage_class &= ~STC.deprecated_; stc2 |= STC.deprecated_; } - if (stc2 != STC.undefined_) + if (stc2 != STC.none) { auto ax = new AST.Dsymbols(); ax.push(s); @@ -4695,7 +4701,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer auto v = new AST.VarDeclaration(loc, t, ident, _init); v.storage_class = storage_class; if (pAttrs) - pAttrs.storageClass = STC.undefined_; + pAttrs.storageClass = STC.none; s = v; } @@ -4755,11 +4761,12 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return a; } - /// Report an error that a declaration of type `t` is missing an identifier + /// Report an error that a declaration of type `t` is missing an identifier and got `tok` instead /// The parser is expected to sit on the next token after the type. - private void noIdentifierForDeclarator(AST.Type t) + private void noIdentifierForDeclarator(AST.Type t, Token tok) { - error("no identifier for declarator `%s`", t.toChars()); + error("variable name expected after type `%s`, not `%s`", t.toChars(), tok.toChars); + // A common mistake is to use a reserved keyword as an identifier, e.g. `in` or `out` if (token.isKeyword) { @@ -4809,7 +4816,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc linkloc = this.linkLoc; AST.Expressions* udas; LINK link = linkage; - StorageClass storage_class = STC.undefined_; + STC storage_class = STC.none; AST.Expression ealign; bool setAlignment = false; @@ -4867,7 +4874,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return; hasParsedAttributes = true; udas = null; - storage_class = STC.undefined_; + storage_class = STC.none; link = linkage; linkloc = this.linkLoc; setAlignment = false; @@ -4882,7 +4889,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Dsymbol s; bool attributesAppended; - const StorageClass funcStc = parseTypeCtor(); + const STC funcStc = parseTypeCtor(); Token* tk; // function literal? if (token.value == TOK.function_ || @@ -5037,6 +5044,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: error("semicolon expected to close `alias` declaration, not `%s`", token.toChars()); + nextToken(); break; } break; @@ -5054,7 +5062,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.TemplateParameters* tpl = null; AST.ParameterList parameterList; AST.Type tret = null; - StorageClass stc = 0; + STC stc = STC.none; TOK save = TOK.reserved; switch (token.value) @@ -5070,7 +5078,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { // function auto ref (parameters) { statements... } // delegate auto ref (parameters) { statements... } - stc = STC.auto_ | STC.ref_; + stc = STC.auto_ | STC.ref_ | STC.autoref; nextToken(); } else @@ -5112,7 +5120,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { // auto ref (parameters) => expression // auto ref (parameters) { statements... } - stc = STC.auto_ | STC.ref_; + stc = STC.auto_ | STC.ref_ | STC.autoref; nextToken(); } else @@ -5133,7 +5141,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer // (parameters) { statements... } parameterList = parseParameterList(&tpl); stc = parsePostfix(stc, null); - if (StorageClass modStc = stc & STC.TYPECTOR) + if (STC modStc = stc & STC.TYPECTOR) { if (save == TOK.function_) { @@ -5222,7 +5230,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("missing `do { ... }` after `in` or `out`"); const returnloc = token.loc; nextToken(); - f.fbody = new AST.ReturnStatement(returnloc, parseExpression()); + if (f.isCtorDeclaration) + f.fbody = new AST.ExpStatement(returnloc, parseExpression()); + else + f.fbody = new AST.ReturnStatement(returnloc, parseExpression()); f.endloc = token.loc; check(TOK.semicolon); break; @@ -5374,9 +5385,14 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("template constraint must follow parameter lists and attributes"); else error("cannot use function constraints for non-template functions. Use `static if` instead"); + + parseConstraint(); } else + { error("semicolon expected following function declaration, not `%s`", token.toChars()); + nextToken(); + } } break; } @@ -5395,7 +5411,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer */ private void checkDanglingElse(Loc elseloc) { - if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.linnum != 0) + if (token.value != TOK.else_ && token.value != TOK.catch_ && token.value != TOK.finally_ && lookingForElse.isValid) { eSink.warning(elseloc, "else is dangling, add { } after condition at %s", lookingForElse.toChars()); } @@ -5473,8 +5489,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Type at; Loc aloc; - StorageClass storageClass = 0; - StorageClass stc = 0; + STC storageClass = STC.none; + STC stc = STC.none; Lagain: if (stc) { @@ -5560,7 +5576,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } at = parseType(&ai); if (!ai) - noIdentifierForDeclarator(at); + noIdentifierForDeclarator(at, token); Larg: auto p = new AST.Parameter(aloc, storageClass, at, ai, null, null); parameters.push(p); @@ -5643,7 +5659,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } /*** - * Parse an assignment condition for if or while statements. + * Parse an assignment condition for `if`, `switch` or `while` statements. * * Returns: * The variable that is declared inside the condition @@ -5651,8 +5667,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer AST.Parameter parseAssignCondition() { AST.Parameter param = null; - StorageClass storageClass = 0; - StorageClass stc = 0; + STC storageClass = STC.none; + STC stc = STC.none; Lwhile: while (1) { @@ -5669,6 +5685,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.auto_: stc = STC.auto_; + if (peekNext() == TOK.ref_) + stc |= STC.autoref; break; case TOK.const_: @@ -5833,7 +5851,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.plusPlus: case TOK.minusMinus: case TOK.new_: - case TOK.delete_: case TOK.delegate_: case TOK.function_: case TOK.typeid_: @@ -5845,6 +5862,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.moduleString: case TOK.functionString: case TOK.prettyFunction: + case TOK.rvalue: Lexp: { AST.Expression exp = parseExpression(); @@ -5960,9 +5978,18 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer goto Lexp; goto case; + // FunctionLiteral `auto ref (` + case TOK.auto_: + if (peekNext() == TOK.ref_ && peekNext2() == TOK.leftParenthesis) + goto Lexp; + goto Ldeclaration; + case TOK.ref_: + if (peekNext() == TOK.leftParenthesis) + goto Lexp; + goto Ldeclaration; + case TOK.alias_: case TOK.const_: - case TOK.auto_: case TOK.abstract_: case TOK.extern_: case TOK.align_: @@ -5972,7 +5999,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.deprecated_: case TOK.nothrow_: case TOK.pure_: - case TOK.ref_: case TOK.gshared: case TOK.at: case TOK.struct_: @@ -6261,12 +6287,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.assign) { if (auto ds = parseDebugSpecification()) - { - if (ds.ident) - eSink.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); - else - eSink.error(ds.loc, "%s `%s` level declaration must be at module level", ds.kind, ds.toPrettyChars); - } + eSink.error(ds.loc, "%s `%s` declaration must be at module level", ds.kind, ds.toPrettyChars); + break; } cond = parseDebugCondition(); @@ -6277,12 +6299,8 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer if (token.value == TOK.assign) { if (auto vs = parseVersionSpecification()) - { - if (vs.ident) - eSink.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); - else - eSink.error(vs.loc, "%s `%s` level declaration must be at module level", vs.kind, vs.toPrettyChars); - } + eSink.error(vs.loc, "%s `%s` declaration must be at module level", vs.kind, vs.toPrettyChars); + break; } cond = parseVersionCondition(); @@ -6772,7 +6790,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { case TOK.identifier: { - if (commaExpected) error("comma expected separating field initializers"); const t = peek(&token); @@ -6806,6 +6823,20 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: if (commaExpected) error("comma expected separating field initializers"); + const t = peek(&token); + if (t.value == TOK.colon) + { + error("incorrect syntax for associative array, expected `[]`, found `{}`"); + while (token.value != TOK.rightCurly && token.value != TOK.endOfFile) + { + nextToken(); + } + if (token.value == TOK.rightCurly) + { + nextToken(); + } + break; + } auto value = parseInitializer(); _is.addInit(null, value); commaExpected = true; @@ -6978,7 +7009,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer Loc labelloc; nextToken(); - StorageClass stc = parsePostfix(STC.undefined_, null); // optional FunctionAttributes + STC stc = parsePostfix(STC.none, null); // optional FunctionAttributes if (stc & (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild)) error("`const`/`immutable`/`shared`/`inout` attributes are not allowed on `asm` blocks"); @@ -8370,6 +8401,15 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.TypeidExp(loc, o); break; } + case TOK.rvalue: + { + nextToken(); + check(TOK.leftParenthesis, "`__rvalue`"); + e = parseAssignExp(); + e.rvalue = true; + check(TOK.rightParenthesis); + break; + } case TOK.traits: { /* __traits(identifier, args...) @@ -8607,7 +8647,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer error("expression expected, not `%s`", token.toChars()); Lerr: // Anything for e, as long as it's not NULL - e = new AST.IntegerExp(loc, 0, AST.Type.tint32); + e = AST.ErrorExp.get(); nextToken(); break; } @@ -8671,15 +8711,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer e = new AST.ComExp(loc, e); break; - case TOK.delete_: - // @@@DEPRECATED_2.109@@@ - // Use of `delete` keyword has been an error since 2.099. - // Remove from the parser after 2.109. - nextToken(); - e = parseUnaryExp(); - e = new AST.DeleteExp(loc, e, false); - break; - case TOK.cast_: // cast(type) expression { nextToken(); @@ -8746,7 +8777,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.const_: case TOK.immutable_: // immutable(type)(arguments) / immutable(type).init { - StorageClass stc = parseTypeCtor(); + STC stc = parseTypeCtor(); AST.Type t = parseBasicType(); t = t.addSTC(stc); @@ -8797,7 +8828,6 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer case TOK.dot: case TOK.plusPlus: case TOK.minusMinus: - case TOK.delete_: case TOK.new_: case TOK.leftParenthesis: case TOK.identifier: @@ -9462,12 +9492,31 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } /******************************************* + * Params: + * thisexp = If not null, it is the `this` reference for the creation + * of an inner class. + * https://dlang.org/spec/class.html#nested-explicit + * https://dlang.org/spec/expression.html#postfix_expressions + * If null, then it is a NewExpression. + * https://dlang.org/spec/expression.html#NewExpression + * Returns: + * NewExpression */ private AST.Expression parseNewExp(AST.Expression thisexp) { const loc = token.loc; - nextToken(); + nextToken(); // skip past `new` + + // parse PlacementExpression if any + AST.Expression placement; + if (token.value == TOK.leftParenthesis) + { + nextToken(); + placement = parseAssignExp(); + check(TOK.rightParenthesis); + } + AST.Expressions* arguments = null; AST.Identifiers* names = null; @@ -9503,7 +9552,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false); - auto e = new AST.NewAnonClassExp(loc, thisexp, cd, arguments); + auto e = new AST.NewAnonClassExp(loc, placement, thisexp, cd, arguments); return e; } @@ -9527,7 +9576,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer parseNamedArguments(arguments, names); } - auto e = new AST.NewExp(loc, thisexp, t, arguments, names); + auto e = new AST.NewExp(loc, placement, thisexp, t, arguments, names); return e; } @@ -9555,7 +9604,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer * Returns: * storage class for attribute, 0 if not */ - static StorageClass isBuiltinAtAttribute(Identifier ident) + static STC isBuiltinAtAttribute(Identifier ident) { return (ident == Id.property) ? STC.property : (ident == Id.nogc) ? STC.nogc : @@ -9565,10 +9614,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer (ident == Id.live) ? STC.live : (ident == Id.future) ? STC.future : (ident == Id.disable) ? STC.disable : - 0; + STC.none; } - enum StorageClass atAttrGroup = + enum STC atAttrGroup = STC.property | STC.nogc | STC.safe | @@ -9580,9 +9629,9 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer void usageOfBodyKeyword() { - version (none) // disable obsolete warning + if (mod.edition >= Edition.v2024) { - eSink.warning(token.loc, "usage of identifer `body` as a keyword is obsolete. Use `do` instead."); + eSink.error(token.loc, "usage of identifer `body` as a keyword is obsolete. Use `do` instead."); } } } @@ -9732,6 +9781,7 @@ immutable PREC[EXP.max + 1] precedence = EXP.assign : PREC.assign, EXP.construct : PREC.assign, EXP.blit : PREC.assign, + EXP.loweredAssignExp : PREC.assign, EXP.addAssign : PREC.assign, EXP.minAssign : PREC.assign, EXP.concatenateAssign : PREC.assign, @@ -9764,7 +9814,7 @@ enum ParseStatementFlags : int struct PrefixAttributes(AST) { - StorageClass storageClass; + STC storageClass; AST.Expression depmsg; LINK link; AST.Visibility visibility; @@ -9816,13 +9866,13 @@ private enum CARRAYDECL = 1; /***************************** * Destructively extract storage class from pAttrs. */ -private StorageClass getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) +private STC getStorageClass(AST)(PrefixAttributes!(AST)* pAttrs) { - StorageClass stc = STC.undefined_; + STC stc = STC.none; if (pAttrs) { stc = pAttrs.storageClass; - pAttrs.storageClass = STC.undefined_; + pAttrs.storageClass = STC.none; } return stc; } diff --git a/gcc/d/dmd/postordervisitor.d b/gcc/d/dmd/postordervisitor.d deleted file mode 100644 index fe189d4..0000000 --- a/gcc/d/dmd/postordervisitor.d +++ /dev/null @@ -1,153 +0,0 @@ -/** - * A depth-first visitor for expressions. - * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved - * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) - * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/apply.d, _apply.d) - * Documentation: https://dlang.org/phobos/dmd_apply.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/apply.d - */ - -module dmd.postordervisitor; - -import dmd.arraytypes; -import dmd.dtemplate; -import dmd.expression; -import dmd.root.array; -import dmd.visitor; - -bool walkPostorder(Expression e, StoppableVisitor v) -{ - scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); - e.accept(pv); - return v.stop; -} - -/************************************** - * An Expression tree walker that will visit each Expression e in the tree, - * in depth-first evaluation order, and call fp(e,param) on it. - * fp() signals whether the walking continues with its return value: - * Returns: - * 0 continue - * 1 done - * It's a bit slower than using virtual functions, but more encapsulated and less brittle. - * Creating an iterator for this would be much more complex. - */ -private extern (C++) final class PostorderExpressionVisitor : StoppableVisitor -{ - alias visit = typeof(super).visit; -public: - StoppableVisitor v; - - extern (D) this(StoppableVisitor v) scope @safe - { - this.v = v; - } - - bool doCond(Expression e) - { - if (!stop && e) - e.accept(this); - return stop; - } - - extern(D) bool doCond(Expression[] e) - { - for (size_t i = 0; i < e.length && !stop; i++) - doCond(e[i]); - return stop; - } - - bool applyTo(Expression e) - { - e.accept(v); - stop = v.stop; - return true; - } - - override void visit(Expression e) - { - applyTo(e); - } - - override void visit(NewExp e) - { - //printf("NewExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(NewAnonClassExp e) - { - //printf("NewAnonClassExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(TypeidExp e) - { - doCond(isExpression(e.obj)) || applyTo(e); - } - - override void visit(UnaExp e) - { - doCond(e.e1) || applyTo(e); - } - - override void visit(BinExp e) - { - doCond(e.e1) || doCond(e.e2) || applyTo(e); - } - - override void visit(AssertExp e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.msg) || applyTo(e); - } - - override void visit(CallExp e) - { - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(ArrayExp e) - { - //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); - } - - override void visit(SliceExp e) - { - doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); - } - - override void visit(ArrayLiteralExp e) - { - doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e); - } - - override void visit(AssocArrayLiteralExp e) - { - doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e); - } - - override void visit(StructLiteralExp e) - { - if (e.stageflags & stageApply) - return; - const old = e.stageflags; - e.stageflags |= stageApply; - doCond(e.elements.peekSlice()) || applyTo(e); - e.stageflags = old; - } - - override void visit(TupleExp e) - { - doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e); - } - - override void visit(CondExp e) - { - doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); - } -} diff --git a/gcc/d/dmd/pragmasem.d b/gcc/d/dmd/pragmasem.d index b52b551..4459774 100644 --- a/gcc/d/dmd/pragmasem.d +++ b/gcc/d/dmd/pragmasem.d @@ -6,9 +6,9 @@ * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/pragmasem.d, _pragmasem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/pragmasem.d, _pragmasem.d) * Documentation: https://dlang.org/phobos/dmd_pragmasem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/pragmasem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/pragmasem.d */ module dmd.pragmasem; @@ -21,6 +21,7 @@ import dmd.attrib; import dmd.dinterpret; import dmd.dscope; import dmd.dsymbol; +import dmd.dsymbolsem : include; import dmd.errors; import dmd.expression; import dmd.expressionsem; @@ -40,10 +41,10 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) { import dmd.aggregate; import dmd.common.outbuffer; - import dmd.dmangle; import dmd.dmodule; import dmd.dsymbolsem; import dmd.identifier; + import dmd.mangle : isValidMangling; import dmd.root.rmem; import dmd.root.utf; import dmd.target; @@ -67,6 +68,8 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) } version (all) { + import dmd.common.charactertables; + /* Note: D language specification should not have any assumption about backend * implementation. Ideally pragma(mangle) can accept a string of any content. * @@ -94,7 +97,7 @@ void pragmaDeclSemantic(PragmaDeclaration pd, Scope* sc) .error(pd.loc, "%s `%s` %.*s", pd.kind, pd.toPrettyChars, cast(int)msg.length, msg.ptr); break; } - if (!isUniAlpha(c)) + if (!isAnyIdentifierCharacter(c)) { .error(pd.loc, "%s `%s` char `0x%04x` not allowed in mangled name", pd.kind, pd.toPrettyChars, c); break; @@ -508,10 +511,9 @@ package PINLINE evalPragmaInline(Loc loc, Scope* sc, Expressions* args) const opt = e.toBool(); if (opt.isEmpty()) return PINLINE.default_; - else if (opt.get()) + if (opt.get()) return PINLINE.always; - else - return PINLINE.never; + return PINLINE.never; } /** @@ -555,32 +557,17 @@ private uint setMangleOverride(Dsymbol s, const(char)[] sym) private bool pragmaMsgSemantic(Loc loc, Scope* sc, Expressions* args) { import dmd.tokens; + import dmd.common.outbuffer; if (!args) return true; - foreach (arg; *args) - { - sc = sc.startCTFE(); - auto e = arg.expressionSemantic(sc); - e = resolveProperties(sc, e); - sc = sc.endCTFE(); - // pragma(msg) is allowed to contain types as well as expressions - e = ctfeInterpretForPragmaMsg(e); - if (e.op == EXP.error) - { - errorSupplemental(loc, "while evaluating `pragma(msg, %s)`", arg.toChars()); - return false; - } - if (auto se = e.toStringExp()) - { - const slice = se.toUTF8(sc).peekString(); - fprintf(stderr, "%.*s", cast(int)slice.length, slice.ptr); - } - else - fprintf(stderr, "%s", e.toChars()); - } - fprintf(stderr, "\n"); + OutBuffer buf; + if (expressionsToString(buf, sc, args, loc, "while evaluating `pragma(msg, %s)`", false)) + return false; + + buf.writestring("\n"); + fprintf(stderr, "%s", buf.extractChars); return true; } diff --git a/gcc/d/dmd/printast.d b/gcc/d/dmd/printast.d index 02dc653..5e4c9f7 100644 --- a/gcc/d/dmd/printast.d +++ b/gcc/d/dmd/printast.d @@ -1,12 +1,12 @@ /** * Provides an AST printer for debugging. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/printast.d, _printast.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/printast.d, _printast.d) * Documentation: https://dlang.org/phobos/dmd_printast.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/printast.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/printast.d */ module dmd.printast; @@ -51,6 +51,12 @@ extern (C++) final class PrintASTVisitor : Visitor printf("%.*s %s\n", cast(int)s.length, s.ptr, e.type ? e.type.toChars() : ""); } + override void visit(IdentifierExp e) + { + printIndent(indent); + printf("Identifier `%s` %s\n", e.ident.toChars(), e.type ? e.type.toChars() : ""); + } + override void visit(IntegerExp e) { printIndent(indent); diff --git a/gcc/d/dmd/root/aav.d b/gcc/d/dmd/root/aav.d index 8929679..014d4a5 100644 --- a/gcc/d/dmd/root/aav.d +++ b/gcc/d/dmd/root/aav.d @@ -1,12 +1,12 @@ /** * Associative array implementation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/aav.d, root/_aav.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/aav.d, root/_aav.d) * Documentation: https://dlang.org/phobos/dmd_root_aav.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/aav.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/aav.d */ module dmd.root.aav; diff --git a/gcc/d/dmd/root/array.d b/gcc/d/dmd/root/array.d index 8135577..a80fc80 100644 --- a/gcc/d/dmd/root/array.d +++ b/gcc/d/dmd/root/array.d @@ -2,12 +2,12 @@ /** * Dynamic array implementation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/array.d, root/_array.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/array.d, root/_array.d) * Documentation: https://dlang.org/phobos/dmd_root_array.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/array.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/array.d */ module dmd.root.array; @@ -52,7 +52,7 @@ public: ~this() pure nothrow { debug (stomp) memset(data.ptr, 0xFF, data.length); - if (data.ptr != &smallarray[0]) + if (data.ptr && data.ptr != &smallarray[0]) mem.xfree(data.ptr); } ///returns elements comma separated in [] diff --git a/gcc/d/dmd/root/array.h b/gcc/d/dmd/root/array.h index 3e28804..6b761cf 100644 --- a/gcc/d/dmd/root/array.h +++ b/gcc/d/dmd/root/array.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 2011-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -26,7 +26,7 @@ struct Array public: Array() { - data.ptr = NULL; + data.ptr = nullptr; length = 0; data.length = 0; } @@ -86,7 +86,7 @@ struct Array if (nentries <= SMALLARRAYCAP) { data.length = SMALLARRAYCAP; - data.ptr = SMALLARRAYCAP ? &smallarray[0] : NULL; + data.ptr = SMALLARRAYCAP ? &smallarray[0] : nullptr; } else { diff --git a/gcc/d/dmd/root/bitarray.d b/gcc/d/dmd/root/bitarray.d index c32d59e..b5adaa8 100644 --- a/gcc/d/dmd/root/bitarray.d +++ b/gcc/d/dmd/root/bitarray.d @@ -1,12 +1,12 @@ /** * Implementation of a bit array. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/bitarray.d, root/_bitarray.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/bitarray.d, root/_bitarray.d) * Documentation: https://dlang.org/phobos/dmd_root_array.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/bitarray.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/bitarray.d */ module dmd.root.bitarray; diff --git a/gcc/d/dmd/root/bitarray.h b/gcc/d/dmd/root/bitarray.h index 2a82703..c50247f 100644 --- a/gcc/d/dmd/root/bitarray.h +++ b/gcc/d/dmd/root/bitarray.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 2011-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -15,7 +15,7 @@ struct BitArray { BitArray() : len(0) - , ptr(NULL) + , ptr(nullptr) {} ~BitArray() diff --git a/gcc/d/dmd/root/complex.d b/gcc/d/dmd/root/complex.d index de4c8d34..777c103 100644 --- a/gcc/d/dmd/root/complex.d +++ b/gcc/d/dmd/root/complex.d @@ -1,12 +1,12 @@ /** * Implements a complex number type. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/complex.d, _complex.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/complex.d, _complex.d) * Documentation: https://dlang.org/phobos/dmd_root_complex.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/complex.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/complex.d */ module dmd.root.complex; diff --git a/gcc/d/dmd/root/complex_t.h b/gcc/d/dmd/root/complex_t.h index 8134f9e..58a0705 100644 --- a/gcc/d/dmd/root/complex_t.h +++ b/gcc/d/dmd/root/complex_t.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/root/ctfloat.d b/gcc/d/dmd/root/ctfloat.d index 70446066..eb3b71c 100644 --- a/gcc/d/dmd/root/ctfloat.d +++ b/gcc/d/dmd/root/ctfloat.d @@ -1,12 +1,12 @@ /** * Collects functions for compile-time floating-point calculations. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/ctfloat.d, root/_ctfloat.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/ctfloat.d, root/_ctfloat.d) * Documentation: https://dlang.org/phobos/dmd_root_ctfloat.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/ctfloat.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/ctfloat.d */ module dmd.root.ctfloat; diff --git a/gcc/d/dmd/root/ctfloat.h b/gcc/d/dmd/root/ctfloat.h index ba8b447..815839c 100644 --- a/gcc/d/dmd/root/ctfloat.h +++ b/gcc/d/dmd/root/ctfloat.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/root/dcompat.h b/gcc/d/dmd/root/dcompat.h index db2b2c6..1c89b57 100644 --- a/gcc/d/dmd/root/dcompat.h +++ b/gcc/d/dmd/root/dcompat.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -18,7 +18,7 @@ struct DArray size_t length; T *ptr; - DArray() : length(0), ptr(NULL) { } + DArray() : length(0), ptr(nullptr) { } DArray(size_t length_in, T *ptr_in) : length(length_in), ptr(ptr_in) { } diff --git a/gcc/d/dmd/root/dsystem.h b/gcc/d/dmd/root/dsystem.h index 49404b7..df343dc 100644 --- a/gcc/d/dmd/root/dsystem.h +++ b/gcc/d/dmd/root/dsystem.h @@ -1,5 +1,5 @@ /* dsystem.h -- Get common system includes from the host. - * Copyright (C) 2018-2024 Free Software Foundation, Inc. + * Copyright (C) 2018-2025 Free Software Foundation, Inc. * * GCC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/gcc/d/dmd/root/file.d b/gcc/d/dmd/root/file.d index a4362e1..2046e59 100644 --- a/gcc/d/dmd/root/file.d +++ b/gcc/d/dmd/root/file.d @@ -1,12 +1,12 @@ /** * Read a file from disk and store it in memory. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/file.d, root/_file.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/file.d, root/_file.d) * Documentation: https://dlang.org/phobos/dmd_root_file.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/file.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/file.d */ module dmd.root.file; @@ -19,11 +19,13 @@ import core.sys.posix.fcntl; import core.sys.posix.unistd; import core.sys.windows.winbase; import core.sys.windows.winnt; + import dmd.root.filename; import dmd.root.rmem; import dmd.root.string; import dmd.common.file; +import dmd.common.outbuffer; import dmd.common.smallbuffer; nothrow: @@ -76,62 +78,52 @@ struct File } nothrow: - /// Read the full content of a file. - static ReadResult read(const(char)[] name) + /** Read the full content of a file, and append it to `buffer` + * Params: + * name = name of file + * buffer = file contents appended to it + * Returns: + * false = success, true = failed + */ + static bool read(const char[] name, ref OutBuffer buffer) { - ReadResult result; + enum Success = false; + enum Failure = true; version (Posix) { - size_t size; - stat_t buf; - ssize_t numread; - //printf("File::read('%s')\n",name); + //printf("File::read('%.*s')\n", cast(int)name.length, name.ptr); int fd = name.toCStringThen!(slice => open(slice.ptr, O_RDONLY)); if (fd == -1) { //perror("\topen error"); - return result; + return Failure; } //printf("\tfile opened\n"); - if (fstat(fd, &buf)) + stat_t statbuf; + if (fstat(fd, &statbuf)) { //perror("\tfstat error"); close(fd); - return result; + return Failure; } - size = cast(size_t)buf.st_size; - ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4); - numread = .read(fd, buffer, size); + size_t size = cast(size_t)statbuf.st_size; + auto buf = buffer.allocate(size); + ssize_t numread = .read(fd, buf.ptr, size); if (numread != size) { //perror("\tread error"); - goto err2; + close(fd); + return Failure; } if (close(fd) == -1) { //perror("\tclose error"); - goto err; + return Failure; } - // Always store a wchar ^Z past end of buffer so scanner has a - // sentinel, although ^Z got obselete, so fill with two 0s and add - // two more so lexer doesn't read pass the buffer. - buffer[size .. size + 4] = 0; - - result.success = true; - result.buffer.data = buffer[0 .. size]; - return result; - err2: - close(fd); - err: - mem.xfree(buffer); - return result; } else version (Windows) { - DWORD size; - DWORD numread; - // work around Windows file path length limitation // (see documentation for extendedPathThen). HANDLE h = name.extendedPathThen! @@ -143,32 +135,24 @@ nothrow: FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, null)); if (h == INVALID_HANDLE_VALUE) - return result; - size = GetFileSize(h, null); - ubyte* buffer = cast(ubyte*)mem.xmalloc_noscan(size + 4); - if (ReadFile(h, buffer, size, &numread, null) != TRUE) - goto err2; - if (numread != size) - goto err2; + return Failure; + DWORD size = GetFileSize(h, null); + auto buf = buffer.allocate(size); + DWORD numread; + if (ReadFile(h, buf.ptr, size, &numread, null) != TRUE || + numread != size) + { + CloseHandle(h); + return Failure; + } if (!CloseHandle(h)) - goto err; - // Always store a wchar ^Z past end of buffer so scanner has a - // sentinel, although ^Z got obselete, so fill with two 0s and add - // two more so lexer doesn't read pass the buffer. - buffer[size .. size + 4] = 0; - result.success = true; - result.buffer.data = buffer[0 .. size]; - return result; - err2: - CloseHandle(h); - err: - mem.xfree(buffer); - return result; + return Failure; } else { - assert(0); + static assert(0); } + return Success; } /// Write a file, returning `true` on success. diff --git a/gcc/d/dmd/root/filename.d b/gcc/d/dmd/root/filename.d index d9f1a04..5ad0775 100644 --- a/gcc/d/dmd/root/filename.d +++ b/gcc/d/dmd/root/filename.d @@ -1,18 +1,19 @@ /** * Encapsulate path and file names. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/filename.d, root/_filename.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/filename.d, root/_filename.d) * Documentation: https://dlang.org/phobos/dmd_root_filename.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/filename.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/filename.d */ module dmd.root.filename; import core.stdc.ctype; import core.stdc.errno; +import core.stdc.stdio; import core.stdc.string; import dmd.common.file; @@ -41,7 +42,8 @@ version (Windows) extern (Windows) DWORD GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR*) nothrow @nogc; extern (Windows) void SetLastError(DWORD) nothrow @nogc; - extern (C) char* getcwd(char* buffer, size_t maxlen) nothrow; + extern (C) char* _getcwd(char* buffer, size_t maxlen) nothrow; + alias getcwd = _getcwd; } version (CRuntime_Glibc) @@ -270,6 +272,20 @@ nothrow: } /******************************** + * Slice of file name without extension. + * Params: + * filename = file name + * Returns: + * the slice + */ + extern (D) static const(char)[] sansExt(const char[] filename) @safe + { + auto e = ext(filename); + size_t length = e.length; + return filename[0 .. filename.length - (length ? length + 1 : 0)]; // +1 for . + } + + /******************************** * Return filename name excluding path (read-only). */ extern (C++) static const(char)* name(const(char)* str) pure @nogc @@ -452,17 +468,15 @@ nothrow: assert(buildPath("a/", "bb", "ccc") == "a/bb/ccc"); } - // Split a path into an Array of paths - extern (C++) static Strings* splitPath(const(char)* path) + // Split a path and append the results to `array` + extern (C++) static void appendSplitPath(const(char)* path, ref Strings array) { - auto array = new Strings(); int sink(const(char)* p) nothrow { array.push(p); return 0; } splitPath(&sink, path); - return array; } /**** @@ -578,13 +592,13 @@ nothrow: /*************************** * Free returned value with FileName::free() */ - extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) + extern (C++) static const(char)* defaultExt(const(char)* name, const(char)* ext) pure { return defaultExt(name.toDString, ext.toDString).ptr; } /// Ditto - extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) + extern (D) static const(char)[] defaultExt(const char[] name, const char[] ext) pure { auto e = FileName.ext(name); if (e.length) // it already has an extension @@ -602,13 +616,13 @@ nothrow: /*************************** * Free returned value with FileName::free() */ - extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) + extern (C++) static const(char)* forceExt(const(char)* name, const(char)* ext) pure { return forceExt(name.toDString, ext.toDString).ptr; } /// Ditto - extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) + extern (D) static const(char)[] forceExt(const char[] name, const char[] ext) pure { if (auto e = FileName.ext(name)) return addExt(name[0 .. $ - e.length - 1], ext); @@ -846,6 +860,7 @@ nothrow: { if (!name.length) return 0; + //static int count; printf("count: %d %.*s\n", ++count, cast(int)name.length, name.ptr); version (Posix) { stat_t st; @@ -862,10 +877,9 @@ nothrow: const dw = GetFileAttributesW(&wname[0]); if (dw == -1) return 0; - else if (dw & FILE_ATTRIBUTE_DIRECTORY) + if (dw & FILE_ATTRIBUTE_DIRECTORY) return 2; - else - return 1; + return 1; }); } else @@ -1087,7 +1101,7 @@ nothrow: return str.ptr; } - const(char)[] toString() const pure nothrow @nogc @trusted + const(char)[] toString() const pure nothrow @nogc @safe { return str; } diff --git a/gcc/d/dmd/root/filename.h b/gcc/d/dmd/root/filename.h index e8c8b11..4f78221 100644 --- a/gcc/d/dmd/root/filename.h +++ b/gcc/d/dmd/root/filename.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -22,7 +22,7 @@ public: static FileName create(const char *name); static bool equals(const char *name1, const char *name2); static bool absolute(const char *name); - static const char *toAbsolute(const char *name, const char *base = NULL); + static const char *toAbsolute(const char *name, const char *base = nullptr); static const char *ext(const char *); const char *ext(); static const char *removeExt(const char *str); @@ -31,7 +31,7 @@ public: static const char *path(const char *); static const char *combine(const char *path, const char *name); - static Strings *splitPath(const char *path); + static void appendSplitPath(const char *path, Strings& array); static const char *defaultExt(const char *name, const char *ext); static const char *forceExt(const char *name, const char *ext); static bool equalsExt(const char *name, const char *ext); diff --git a/gcc/d/dmd/root/hash.d b/gcc/d/dmd/root/hash.d index 441620e..d327f4b 100644 --- a/gcc/d/dmd/root/hash.d +++ b/gcc/d/dmd/root/hash.d @@ -1,12 +1,12 @@ /** * Hash functions for arbitrary binary data. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Martin Nowak, Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/hash.d, root/_hash.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/hash.d, root/_hash.d) * Documentation: https://dlang.org/phobos/dmd_root_hash.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/hash.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/hash.d */ module dmd.root.hash; diff --git a/gcc/d/dmd/root/longdouble.d b/gcc/d/dmd/root/longdouble.d index 1f73fb7..d9f5c0e 100644 --- a/gcc/d/dmd/root/longdouble.d +++ b/gcc/d/dmd/root/longdouble.d @@ -1,6 +1,6 @@ /** * 80-bit floating point value implementation if the C/D compiler does not support them natively. - * Copyright (C) 2021-2024 Free Software Foundation, Inc. + * Copyright (C) 2021-2025 Free Software Foundation, Inc. * * GCC is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ pure: extern (D) longdouble opAssign(T)(T r) if (is (T : longdouble)) { - this.realvalue = r.realvalue; + this.realvalue = r.realvalue; return this; } diff --git a/gcc/d/dmd/root/optional.d b/gcc/d/dmd/root/optional.d index e7d0e1e..2b518eb 100644 --- a/gcc/d/dmd/root/optional.d +++ b/gcc/d/dmd/root/optional.d @@ -1,12 +1,12 @@ /** * Implementation of an 'Optional' type * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/optional.d, root/_optional.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/optional.d, root/_optional.d) * Documentation: https://dlang.org/phobos/dmd_root_optional.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/optional.d */ module dmd.root.optional; diff --git a/gcc/d/dmd/root/optional.h b/gcc/d/dmd/root/optional.h index a92dedd..12891f8 100644 --- a/gcc/d/dmd/root/optional.h +++ b/gcc/d/dmd/root/optional.h @@ -3,12 +3,12 @@ /** * Optional implementation. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/optional.h, root/_optional.h) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/optional.h, root/_optional.h) * Documentation: https://dlang.org/phobos/dmd_root_optional.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/optional.h + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/optional.h */ #include "dcompat.h" // for d_bool diff --git a/gcc/d/dmd/root/port.d b/gcc/d/dmd/root/port.d index ee846bd..5fd8080 100644 --- a/gcc/d/dmd/root/port.d +++ b/gcc/d/dmd/root/port.d @@ -1,12 +1,12 @@ /** * Portable routines for functions that have different implementations on different platforms. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/port.d, root/_port.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/port.d, root/_port.d) * Documentation: https://dlang.org/phobos/dmd_root_port.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/port.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/port.d */ module dmd.root.port; diff --git a/gcc/d/dmd/root/port.h b/gcc/d/dmd/root/port.h index 6c7dddd..dfb56b0 100644 --- a/gcc/d/dmd/root/port.h +++ b/gcc/d/dmd/root/port.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/root/region.d b/gcc/d/dmd/root/region.d index a9fab16..a8efbca 100644 --- a/gcc/d/dmd/root/region.d +++ b/gcc/d/dmd/root/region.d @@ -1,12 +1,12 @@ /** * Region storage allocator implementation. * - * Copyright: Copyright (C) 2019-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 2019-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/region.d, root/_region.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/region.d, root/_region.d) * Documentation: https://dlang.org/phobos/dmd_root_region.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/region.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/region.d */ module dmd.root.region; diff --git a/gcc/d/dmd/root/rmem.d b/gcc/d/dmd/root/rmem.d index 1965207..32d22d3 100644 --- a/gcc/d/dmd/root/rmem.d +++ b/gcc/d/dmd/root/rmem.d @@ -1,12 +1,12 @@ /** * Allocate memory using `malloc` or the GC depending on the configuration. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/rmem.d, root/_rmem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/rmem.d, root/_rmem.d) * Documentation: https://dlang.org/phobos/dmd_root_rmem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/rmem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/rmem.d */ module dmd.root.rmem; @@ -149,6 +149,7 @@ enum CHUNK_SIZE = (256 * 4096 - 64); __gshared size_t heapleft = 0; __gshared void* heapp; +__gshared size_t heapTotal = 0; // Total amount of memory allocated using malloc extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc { @@ -167,11 +168,13 @@ extern (D) void* allocmemoryNoFree(size_t m_size) nothrow @nogc if (m_size > CHUNK_SIZE) { + heapTotal += m_size; return Mem.check(malloc(m_size)); } heapleft = CHUNK_SIZE; heapp = Mem.check(malloc(CHUNK_SIZE)); + heapTotal += CHUNK_SIZE; goto L1; } @@ -318,7 +321,7 @@ Params: Returns: A null-terminated copy of the input array. */ -extern (D) char[] xarraydup(const(char)[] s) pure nothrow +extern (D) char[] xarraydup(scope const(char)[] s) pure nothrow { if (!s) return null; diff --git a/gcc/d/dmd/root/rmem.h b/gcc/d/dmd/root/rmem.h index 09c0fc0..b6645ec 100644 --- a/gcc/d/dmd/root/rmem.h +++ b/gcc/d/dmd/root/rmem.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/root/speller.d b/gcc/d/dmd/root/speller.d index ae09cba..a215110 100644 --- a/gcc/d/dmd/root/speller.d +++ b/gcc/d/dmd/root/speller.d @@ -3,12 +3,12 @@ * * Does not have any dependencies on the rest of DMD. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/speller.d, root/_speller.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/speller.d, root/_speller.d) * Documentation: https://dlang.org/phobos/dmd_root_speller.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/speller.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/speller.d */ module dmd.root.speller; diff --git a/gcc/d/dmd/root/string.d b/gcc/d/dmd/root/string.d index e82b0d2..369a79b 100644 --- a/gcc/d/dmd/root/string.d +++ b/gcc/d/dmd/root/string.d @@ -1,15 +1,18 @@ /** * Contains various string related functions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/string.d, root/_string.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/string.d, root/_string.d) * Documentation: https://dlang.org/phobos/dmd_root_string.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/string.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/string.d */ module dmd.root.string; +import core.stdc.string; +import dmd.root.rmem; + /// Slices a `\0`-terminated C-string, excluding the terminator inout(char)[] toDString (inout(char)* s) pure nothrow @nogc { @@ -17,6 +20,27 @@ inout(char)[] toDString (inout(char)* s) pure nothrow @nogc return s ? s[0 .. strlen(s)] : null; } +private struct FTuple(T...) +{ + T expand; +} + +/// Returns: a (length, ptr) tuple for passing a D string to `printf`-style functions with the format string `%.*s` +auto fTuple(const(char)[] str) +{ + return FTuple!(int, const(char)*)(cast(int) str.length, str.ptr); +} + +/// +unittest +{ + import core.stdc.stdio: snprintf; + char[6] buf = '.'; + const(char)[] str = "cutoff"[0..4]; + snprintf(buf.ptr, buf.length, "%.*s", str.fTuple.expand); + assert(buf[] == "cuto\0."); +} + /** Compare two slices for equality, in a case-insensitive way @@ -87,6 +111,23 @@ unittest assert(null.toCStringThen!((v) => v == "\0")); } +/********************************************* + * Convert a D string to a C string by allocating memory, + * copying it, and adding a terminating 0. + * Params: + * s = string to copy + * Result: + * 0-terminated copy of s + */ +char[] toCString(scope const(char)[] s) nothrow +{ + const length = s.length; + char* p = cast(char*)mem.xmalloc_noscan(length + 1); + memcpy(p, s.ptr, length); + p[length] = 0; + return p[0 .. length]; +} + /** * Strips one leading line terminator of the given string. * @@ -274,6 +315,15 @@ do return true; } +///ditto +nothrow @nogc pure @safe +bool startsWith(scope const(char)[] str, scope const(char)[] prefix) +{ + if (str.length < prefix.length) + return false; + return str[0 .. prefix.length] == prefix; +} + /// @system pure nothrow @nogc unittest @@ -286,3 +336,184 @@ unittest assert(ptr.startsWith("123")); assert(!ptr.startsWith("1234")); } + +/********************************** + * Take `text` and turn it into an InputRange that emits + * slices into `text` for each line. + * Params: + * text = array of characters + * Returns: + * InputRange accessing `text` as a sequence of lines + * Reference: + * `std.string.splitLines()` + */ +auto splitLines(const char[] text) +{ + struct Range + { + @safe: + @nogc: + nothrow: + pure: + private: + + const char[] text; + size_t index; // index of start of line + size_t eolIndex; // index of end of line before newline characters + size_t nextIndex; // index past end of line + + public this(const char[] text) + { + this.text = text; + this.index = 0; + this.eolIndex = 0; + this.nextIndex = 0; + } + + public bool empty() { advance(); return index >= text.length; } + + public void popFront() { advance(); index = nextIndex; } + + public const(char)[] front() + { + advance(); + if (index > eolIndex || index >= text.length) + return ""; + + return text[index .. eolIndex]; + } + + private void advance() + { + if (index != nextIndex) // if already advanced + return; + + for (size_t i = index; i < text.length; ++i) + { + switch (text[i]) + { + case '\v', '\f', '\n': + eolIndex = i; + nextIndex = i + 1; + return; + + case '\r': + if (i + 1 < text.length && text[i + 1] == '\n') // decode "\r\n" + { + eolIndex = i; + nextIndex = i + 2; + return; + } + eolIndex = i; + nextIndex = i + 1; + return; + + /* Manually decode: + * NEL is C2 85 + */ + case 0xC2: + if (i + 1 < text.length && text[i + 1] == 0x85) + { + eolIndex = i; + nextIndex = i + 2; + return; + } + break; + + /* Manually decode: + * lineSep is E2 80 A8 + * paraSep is E2 80 A9 + */ + case 0xE2: + if (i + 2 < text.length && + text[i + 1] == 0x80 && + (text[i + 2] == 0xA8 || text[i + 2] == 0xA9) + ) + { + eolIndex = i; + nextIndex = i + 3; + return; + } + break; + + default: + break; + } + } + + // No newline found; set indices to the end of the text + eolIndex = text.length; + nextIndex = text.length; + } + } + + return Range(text); +} + +private struct FindSplit +{ +@nogc nothrow pure @safe: + const(char)[][3] elem; + + ref const(char)[] opIndex(size_t i) scope return { return elem[i]; } + bool opCast() const scope { return elem[1].length > 0; } +} + +/** +Find a substring in a string and split the string into before and after parts. +Params: + str = string to look into + needle = substring to find in str (must not be empty) +Returns: + a `FindSplit` object that casts to `true` iff `needle` was found inside `str`. + In that case, `split[1]` is the needle, and `split[0]`/`split[2]` are before/after the needle. +*/ +FindSplit findSplit(return scope const(char)[] str, scope const(char)[] needle) @safe +{ + if (needle.length > str.length) + return FindSplit([str, null, null]); + + foreach (i; 0 .. str.length - needle.length + 1) + { + if (str[i .. i+needle.length] == needle[]) + return FindSplit([ str[0 .. i], str[i .. i+needle.length], str[i+needle.length .. $] ]); + } + return FindSplit([str, null, null]); +} + +unittest +{ + auto s = findSplit("a b c", "c"); + assert(s[0] == "a b "); + assert(s[1] == "c"); + assert(s[2] == ""); + auto s1 = findSplit("a b c", "b"); + assert(s1[0] == "a "); + assert(s1[1] == "b"); + assert(s1[2] == " c"); + assert(!findSplit("a b c", "d")); + assert(!findSplit("", "d")); +} + +/** +Find a string inbetween two substrings +Params: + str = string to look into + l = substring to find on the left + r = substring to find on the right +Returns: + substring of `str` inbetween `l` and `r` +*/ +const(char)[] findBetween(const(char)[] str, const(char)[] l, const(char)[] r) @safe +{ + if (auto s0 = str.findSplit(l)) + if (auto s1 = s0[2].findSplit(r)) + return s1[0]; + return null; +} + +unittest +{ + assert(findBetween("a b c", "a ", " c") == "b"); + assert(findBetween("a b c", "a ", " d") == null); +} diff --git a/gcc/d/dmd/root/stringtable.d b/gcc/d/dmd/root/stringtable.d index 1fba919..c7a2c8c 100644 --- a/gcc/d/dmd/root/stringtable.d +++ b/gcc/d/dmd/root/stringtable.d @@ -1,12 +1,12 @@ /** * A specialized associative array with string keys stored in a variable length structure. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/stringtable.d, root/_stringtable.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/stringtable.d, root/_stringtable.d) * Documentation: https://dlang.org/phobos/dmd_root_stringtable.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/stringtable.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/stringtable.d */ module dmd.root.stringtable; diff --git a/gcc/d/dmd/root/utf.d b/gcc/d/dmd/root/utf.d index 7d732f2..5b2c42f 100644 --- a/gcc/d/dmd/root/utf.d +++ b/gcc/d/dmd/root/utf.d @@ -1,12 +1,12 @@ /** * Functions related to UTF encoding. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/utf.d, _utf.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/root/utf.d, _utf.d) * Documentation: https://dlang.org/phobos/dmd_root_utf.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/utf.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/root/utf.d */ module dmd.root.utf; @@ -27,281 +27,6 @@ bool utf_isValidDchar(dchar c) return false; } -/******************************* - * Return !=0 if unicode alpha. - * Use table from C99 Appendix D. - */ -bool isUniAlpha(dchar c) -{ - static immutable wchar[2][] ALPHA_TABLE = - [ - [0x00AA, 0x00AA], - [0x00B5, 0x00B5], - [0x00B7, 0x00B7], - [0x00BA, 0x00BA], - [0x00C0, 0x00D6], - [0x00D8, 0x00F6], - [0x00F8, 0x01F5], - [0x01FA, 0x0217], - [0x0250, 0x02A8], - [0x02B0, 0x02B8], - [0x02BB, 0x02BB], - [0x02BD, 0x02C1], - [0x02D0, 0x02D1], - [0x02E0, 0x02E4], - [0x037A, 0x037A], - [0x0386, 0x0386], - [0x0388, 0x038A], - [0x038C, 0x038C], - [0x038E, 0x03A1], - [0x03A3, 0x03CE], - [0x03D0, 0x03D6], - [0x03DA, 0x03DA], - [0x03DC, 0x03DC], - [0x03DE, 0x03DE], - [0x03E0, 0x03E0], - [0x03E2, 0x03F3], - [0x0401, 0x040C], - [0x040E, 0x044F], - [0x0451, 0x045C], - [0x045E, 0x0481], - [0x0490, 0x04C4], - [0x04C7, 0x04C8], - [0x04CB, 0x04CC], - [0x04D0, 0x04EB], - [0x04EE, 0x04F5], - [0x04F8, 0x04F9], - [0x0531, 0x0556], - [0x0559, 0x0559], - [0x0561, 0x0587], - [0x05B0, 0x05B9], - [0x05BB, 0x05BD], - [0x05BF, 0x05BF], - [0x05C1, 0x05C2], - [0x05D0, 0x05EA], - [0x05F0, 0x05F2], - [0x0621, 0x063A], - [0x0640, 0x0652], - [0x0660, 0x0669], - [0x0670, 0x06B7], - [0x06BA, 0x06BE], - [0x06C0, 0x06CE], - [0x06D0, 0x06DC], - [0x06E5, 0x06E8], - [0x06EA, 0x06ED], - [0x06F0, 0x06F9], - [0x0901, 0x0903], - [0x0905, 0x0939], - [0x093D, 0x094D], - [0x0950, 0x0952], - [0x0958, 0x0963], - [0x0966, 0x096F], - [0x0981, 0x0983], - [0x0985, 0x098C], - [0x098F, 0x0990], - [0x0993, 0x09A8], - [0x09AA, 0x09B0], - [0x09B2, 0x09B2], - [0x09B6, 0x09B9], - [0x09BE, 0x09C4], - [0x09C7, 0x09C8], - [0x09CB, 0x09CD], - [0x09DC, 0x09DD], - [0x09DF, 0x09E3], - [0x09E6, 0x09F1], - [0x0A02, 0x0A02], - [0x0A05, 0x0A0A], - [0x0A0F, 0x0A10], - [0x0A13, 0x0A28], - [0x0A2A, 0x0A30], - [0x0A32, 0x0A33], - [0x0A35, 0x0A36], - [0x0A38, 0x0A39], - [0x0A3E, 0x0A42], - [0x0A47, 0x0A48], - [0x0A4B, 0x0A4D], - [0x0A59, 0x0A5C], - [0x0A5E, 0x0A5E], - [0x0A66, 0x0A6F], - [0x0A74, 0x0A74], - [0x0A81, 0x0A83], - [0x0A85, 0x0A8B], - [0x0A8D, 0x0A8D], - [0x0A8F, 0x0A91], - [0x0A93, 0x0AA8], - [0x0AAA, 0x0AB0], - [0x0AB2, 0x0AB3], - [0x0AB5, 0x0AB9], - [0x0ABD, 0x0AC5], - [0x0AC7, 0x0AC9], - [0x0ACB, 0x0ACD], - [0x0AD0, 0x0AD0], - [0x0AE0, 0x0AE0], - [0x0AE6, 0x0AEF], - [0x0B01, 0x0B03], - [0x0B05, 0x0B0C], - [0x0B0F, 0x0B10], - [0x0B13, 0x0B28], - [0x0B2A, 0x0B30], - [0x0B32, 0x0B33], - [0x0B36, 0x0B39], - [0x0B3D, 0x0B43], - [0x0B47, 0x0B48], - [0x0B4B, 0x0B4D], - [0x0B5C, 0x0B5D], - [0x0B5F, 0x0B61], - [0x0B66, 0x0B6F], - [0x0B82, 0x0B83], - [0x0B85, 0x0B8A], - [0x0B8E, 0x0B90], - [0x0B92, 0x0B95], - [0x0B99, 0x0B9A], - [0x0B9C, 0x0B9C], - [0x0B9E, 0x0B9F], - [0x0BA3, 0x0BA4], - [0x0BA8, 0x0BAA], - [0x0BAE, 0x0BB5], - [0x0BB7, 0x0BB9], - [0x0BBE, 0x0BC2], - [0x0BC6, 0x0BC8], - [0x0BCA, 0x0BCD], - [0x0BE7, 0x0BEF], - [0x0C01, 0x0C03], - [0x0C05, 0x0C0C], - [0x0C0E, 0x0C10], - [0x0C12, 0x0C28], - [0x0C2A, 0x0C33], - [0x0C35, 0x0C39], - [0x0C3E, 0x0C44], - [0x0C46, 0x0C48], - [0x0C4A, 0x0C4D], - [0x0C60, 0x0C61], - [0x0C66, 0x0C6F], - [0x0C82, 0x0C83], - [0x0C85, 0x0C8C], - [0x0C8E, 0x0C90], - [0x0C92, 0x0CA8], - [0x0CAA, 0x0CB3], - [0x0CB5, 0x0CB9], - [0x0CBE, 0x0CC4], - [0x0CC6, 0x0CC8], - [0x0CCA, 0x0CCD], - [0x0CDE, 0x0CDE], - [0x0CE0, 0x0CE1], - [0x0CE6, 0x0CEF], - [0x0D02, 0x0D03], - [0x0D05, 0x0D0C], - [0x0D0E, 0x0D10], - [0x0D12, 0x0D28], - [0x0D2A, 0x0D39], - [0x0D3E, 0x0D43], - [0x0D46, 0x0D48], - [0x0D4A, 0x0D4D], - [0x0D60, 0x0D61], - [0x0D66, 0x0D6F], - [0x0E01, 0x0E3A], - [0x0E40, 0x0E5B], - [0x0E81, 0x0E82], - [0x0E84, 0x0E84], - [0x0E87, 0x0E88], - [0x0E8A, 0x0E8A], - [0x0E8D, 0x0E8D], - [0x0E94, 0x0E97], - [0x0E99, 0x0E9F], - [0x0EA1, 0x0EA3], - [0x0EA5, 0x0EA5], - [0x0EA7, 0x0EA7], - [0x0EAA, 0x0EAB], - [0x0EAD, 0x0EAE], - [0x0EB0, 0x0EB9], - [0x0EBB, 0x0EBD], - [0x0EC0, 0x0EC4], - [0x0EC6, 0x0EC6], - [0x0EC8, 0x0ECD], - [0x0ED0, 0x0ED9], - [0x0EDC, 0x0EDD], - [0x0F00, 0x0F00], - [0x0F18, 0x0F19], - [0x0F20, 0x0F33], - [0x0F35, 0x0F35], - [0x0F37, 0x0F37], - [0x0F39, 0x0F39], - [0x0F3E, 0x0F47], - [0x0F49, 0x0F69], - [0x0F71, 0x0F84], - [0x0F86, 0x0F8B], - [0x0F90, 0x0F95], - [0x0F97, 0x0F97], - [0x0F99, 0x0FAD], - [0x0FB1, 0x0FB7], - [0x0FB9, 0x0FB9], - [0x10A0, 0x10C5], - [0x10D0, 0x10F6], - [0x1E00, 0x1E9B], - [0x1EA0, 0x1EF9], - [0x1F00, 0x1F15], - [0x1F18, 0x1F1D], - [0x1F20, 0x1F45], - [0x1F48, 0x1F4D], - [0x1F50, 0x1F57], - [0x1F59, 0x1F59], - [0x1F5B, 0x1F5B], - [0x1F5D, 0x1F5D], - [0x1F5F, 0x1F7D], - [0x1F80, 0x1FB4], - [0x1FB6, 0x1FBC], - [0x1FBE, 0x1FBE], - [0x1FC2, 0x1FC4], - [0x1FC6, 0x1FCC], - [0x1FD0, 0x1FD3], - [0x1FD6, 0x1FDB], - [0x1FE0, 0x1FEC], - [0x1FF2, 0x1FF4], - [0x1FF6, 0x1FFC], - [0x203F, 0x2040], - [0x207F, 0x207F], - [0x2102, 0x2102], - [0x2107, 0x2107], - [0x210A, 0x2113], - [0x2115, 0x2115], - [0x2118, 0x211D], - [0x2124, 0x2124], - [0x2126, 0x2126], - [0x2128, 0x2128], - [0x212A, 0x2131], - [0x2133, 0x2138], - [0x2160, 0x2182], - [0x3005, 0x3007], - [0x3021, 0x3029], - [0x3041, 0x3093], - [0x309B, 0x309C], - [0x30A1, 0x30F6], - [0x30FB, 0x30FC], - [0x3105, 0x312C], - [0x4E00, 0x9FA5], - [0xAC00, 0xD7A3] - ]; - - size_t high = ALPHA_TABLE.length - 1; - // Shortcut search if c is out of range - size_t low = (c < ALPHA_TABLE[0][0] || ALPHA_TABLE[high][1] < c) ? high + 1 : 0; - // Binary search - while (low <= high) - { - const size_t mid = low + ((high - low) >> 1); - if (c < ALPHA_TABLE[mid][0]) - high = mid - 1; - else if (ALPHA_TABLE[mid][1] < c) - low = mid + 1; - else - { - assert(ALPHA_TABLE[mid][0] <= c && c <= ALPHA_TABLE[mid][1]); - return true; - } - } - return false; -} - /** * Returns the code length of c in code units. */ diff --git a/gcc/d/dmd/rootobject.d b/gcc/d/dmd/rootobject.d index 7c926fe..71b36a4 100644 --- a/gcc/d/dmd/rootobject.d +++ b/gcc/d/dmd/rootobject.d @@ -1,12 +1,12 @@ /** * Provide the root object that AST classes in dmd inherit from. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: Walter Bright, https://www.digitalmars.com * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/rootobject.d, _rootobject.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/rootobject.d, _rootobject.d) * Documentation: https://dlang.org/phobos/dmd_rootobject.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/rootobject.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/rootobject.d */ module dmd.rootobject; diff --git a/gcc/d/dmd/rootobject.h b/gcc/d/dmd/rootobject.h index 718a54f..330d2c9 100644 --- a/gcc/d/dmd/rootobject.h +++ b/gcc/d/dmd/rootobject.h @@ -1,5 +1,5 @@ -/* Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved +/* Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. diff --git a/gcc/d/dmd/safe.d b/gcc/d/dmd/safe.d index 1e5fb47..3be9efe 100644 --- a/gcc/d/dmd/safe.d +++ b/gcc/d/dmd/safe.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/function.html#function-safety, Function Safety) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/safe.d, _safe.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/safe.d, _safe.d) * Documentation: https://dlang.org/phobos/dmd_safe.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/safe.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/safe.d */ module dmd.safe; @@ -17,17 +17,27 @@ import core.stdc.stdio; import dmd.aggregate; import dmd.astenums; +import dmd.common.outbuffer; +import dmd.dcast : implicitConvTo; import dmd.dclass; import dmd.declaration; import dmd.dscope; +import dmd.dsymbol; +import dmd.dsymbolsem : determineSize; +import dmd.errors; import dmd.expression; +import dmd.func; +import dmd.funcsem : isRootTraitsCompilesScope; +import dmd.globals : FeatureState, global; import dmd.id; import dmd.identifier; +import dmd.location; import dmd.mtype; +import dmd.rootobject; +import dmd.root.string : fTuple; import dmd.target; import dmd.tokens; -import dmd.typesem : hasPointers, arrayOf; -import dmd.func : setUnsafe, setUnsafePreview; +import dmd.typesem : hasPointers, arrayOf, size; /************************************************************* * Check for unsafe access in @safe code: @@ -49,92 +59,103 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) //printf("checkUnsafeAccess(e: '%s', readonly: %d, printmsg: %d)\n", e.toChars(), readonly, printmsg); if (e.op != EXP.dotVariable) return false; - DotVarExp dve = cast(DotVarExp)e; - if (VarDeclaration v = dve.var.isVarDeclaration()) - { - if (!sc.func) - return false; - auto ad = v.isMember2(); - if (!ad) - return false; + auto dve = cast(DotVarExp)e; + VarDeclaration v = dve.var.isVarDeclaration(); + if (!v) + return false; + if (!sc.func) + return false; + auto ad = v.isMember2(); + if (!ad) + return false; - import dmd.globals : global; - if (v.isSystem()) - { - if (sc.setUnsafePreview(global.params.systemVariables, !printmsg, e.loc, - "cannot access `@system` field `%s.%s` in `@safe` code", ad, v)) - return true; - } + if (v.isSystem()) + { + if (sc.setUnsafePreview(sc.previews.systemVariables, !printmsg, e.loc, + "accessing `@system` field `%s.%s`", ad, v)) + return true; + } - // This branch shouldn't be here, but unfortunately calling `ad.determineSize` - // breaks code with circular reference errors. Specifically, test23589.d fails - if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference()) - return false; + // This branch shouldn't be here, but unfortunately calling `ad.determineSize` + // breaks code with circular reference errors. Specifically, test23589.d fails + if (ad.sizeok != Sizeok.done && !sc.func.isSafeBypassingInference()) + return false; - // needed to set v.overlapped and v.overlapUnsafe - if (ad.sizeok != Sizeok.done) - ad.determineSize(ad.loc); + // needed to set v.overlapped and v.overlapUnsafe + if (ad.sizeok != Sizeok.done) + ad.determineSize(ad.loc); - const hasPointers = v.type.hasPointers(); - if (hasPointers) + import dmd.globals : FeatureState; + const hasPointers = v.type.hasPointers(); + if (hasPointers) + { + if (v.overlapped) { - if (v.overlapped) + if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc, + "accessing overlapped field `%s.%s` with pointers", ad, v)) { - if (sc.func.isSafeBypassingInference() && sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v)) - { - return true; - } - else - { - import dmd.globals : FeatureState; - // @@@DEPRECATED_2.116@@@ - // https://issues.dlang.org/show_bug.cgi?id=20655 - // Inferring `@system` because of union access breaks code, - // so make it a deprecation safety violation as of 2.106 - // To turn into an error, remove `isSafeBypassingInference` check in the - // above if statement and remove the else branch - sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc, - "field `%s.%s` cannot access pointers in `@safe` code that overlap other fields", ad, v); - } + return true; } - } - - if (v.type.hasInvariant()) - { - if (v.overlapped) + else { - if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot access structs with invariants in `@safe` code that overlap other fields", - ad, v)) - return true; + // @@@DEPRECATED_2.116@@@ + // https://issues.dlang.org/show_bug.cgi?id=20655 + // Inferring `@system` because of union access breaks code, + // so make it a deprecation safety violation as of 2.106 + // To turn into an error, remove `isSafeBypassingInference` check in the + // above if statement and remove the else branch + sc.setUnsafePreview(FeatureState.default_, !printmsg, e.loc, + "accessing overlapped field `%s.%s` with pointers", ad, v); } } + } - if (readonly || !e.type.isMutable()) - return false; - - if (hasPointers && v.type.toBasetype().ty != Tstruct) + if (v.type.hasInvariant()) + { + if (v.overlapped) { - if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize || - (v.offset & (target.ptrsize - 1)))) - { - if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot modify misaligned pointers in `@safe` code", ad, v)) - return true; - } + if (sc.setUnsafe(!printmsg, e.loc, + "accessing overlapped field `%s.%s` with a structs invariant", + ad, v)) + return true; } + } + + // @@@DEPRECATED_2.119@@@ + // https://issues.dlang.org/show_bug.cgi?id=24477 + // Should probably be turned into an error in a new edition + if (v.type.hasUnsafeBitpatterns() && v.overlapped && sc.setUnsafePreview( + FeatureState.default_, !printmsg, e.loc, + "accessing overlapped field `%s.%s` with unsafe bit patterns", ad, v) + ) + { + return true; + } - if (v.overlapUnsafe) + if (readonly || !e.type.isMutable()) + return false; + + if (hasPointers && v.type.toBasetype().ty != Tstruct) + { + if ((!ad.type.alignment.isDefault() && ad.type.alignment.get() < target.ptrsize) || + (v.offset & (target.ptrsize - 1))) { if (sc.setUnsafe(!printmsg, e.loc, - "field `%s.%s` cannot modify fields in `@safe` code that overlap fields with other storage classes", - ad, v)) - { + "modifying misaligned pointers through field `%s.%s`", ad, v)) return true; - } } } + + if (v.overlapUnsafe) + { + if (sc.setUnsafe(!printmsg, e.loc, + "modifying field `%s.%s` which overlaps with fields with other storage classes", + ad, v)) + { + return true; + } + } + return false; } @@ -145,10 +166,11 @@ bool checkUnsafeAccess(Scope* sc, Expression e, bool readonly, bool printmsg) * e = expression to be cast * tfrom = type of e * tto = type to cast e to + * msg = reason why cast is unsafe or deprecated * Returns: - * true if @safe + * true if @safe or deprecated */ -bool isSafeCast(Expression e, Type tfrom, Type tto) +bool isSafeCast(Expression e, Type tfrom, Type tto, ref string msg) { // Implicit conversions are always safe if (tfrom.implicitConvTo(tto)) @@ -160,22 +182,42 @@ bool isSafeCast(Expression e, Type tfrom, Type tto) auto tfromb = tfrom.toBasetype(); auto ttob = tto.toBasetype(); + // Casting to void* is always safe, https://github.com/dlang/dmd/issues/20514 + if (ttob.isTypePointer() && ttob.nextOf().toBasetype().ty == Tvoid) + return true; + if (ttob.ty == Tclass && tfromb.ty == Tclass) { ClassDeclaration cdfrom = tfromb.isClassHandle(); ClassDeclaration cdto = ttob.isClassHandle(); - int offset; + + if (cdfrom == cdto) + goto Lsame; + if (!cdfrom.isBaseOf(cdto, &offset) && !((cdfrom.isInterfaceDeclaration() || cdto.isInterfaceDeclaration()) && cdfrom.classKind == ClassKind.d && cdto.classKind == ClassKind.d)) + { + msg = "Source object type is incompatible with target type"; return false; + } + // no RTTI if (cdfrom.isCPPinterface() || cdto.isCPPinterface()) + { + msg = "No dynamic type information for extern(C++) classes"; return false; + } + if (cdfrom.classKind == ClassKind.cpp || cdto.classKind == ClassKind.cpp) + msg = "No dynamic type information for extern(C++) classes"; + Lsame: if (!MODimplicitConv(tfromb.mod, ttob.mod)) + { + msg = "Incompatible type qualifier"; return false; + } return true; } @@ -199,22 +241,52 @@ bool isSafeCast(Expression e, Type tfrom, Type tto) { if (ttob.ty == Tarray && e.op == EXP.arrayLiteral) return true; + msg = "`void` data may contain pointers and target element type is mutable"; return false; } // If the struct is opaque we don't know about the struct members then the cast becomes unsafe - if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members || - tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members) + if (ttobn.ty == Tstruct && !(cast(TypeStruct)ttobn).sym.members) + { + msg = "Target element type is opaque"; + return false; + } + if (tfromn.ty == Tstruct && !(cast(TypeStruct)tfromn).sym.members) + { + msg = "Source element type is opaque"; return false; + } + + if (e.op != EXP.arrayLiteral) + { + // For bool, only 0 and 1 are safe values + // Runtime array cast reinterprets data + if (ttobn.ty == Tbool && tfromn.ty != Tbool) + msg = "Source element may have bytes which are not 0 or 1"; + else if (ttobn.hasUnsafeBitpatterns()) + msg = "Target element type has unsafe bit patterns"; + + // Can't alias a bool pointer with a non-bool pointer + if (ttobn.ty != Tbool && tfromn.ty == Tbool && ttobn.isMutable()) + msg = "Target element could be assigned a byte which is not 0 or 1"; + else if (tfromn.hasUnsafeBitpatterns() && ttobn.isMutable()) + msg = "Source element type has unsafe bit patterns and target element type is mutable"; + } const frompointers = tfromn.hasPointers(); const topointers = ttobn.hasPointers(); if (frompointers && !topointers && ttobn.isMutable()) + { + msg = "Target element type is mutable and source element type contains a pointer"; return false; + } if (!frompointers && topointers) + { + msg = "Target element type contains a pointer"; return false; + } if (!topointers && ttobn.ty != Tfunction && tfromn.ty != Tfunction && @@ -224,6 +296,7 @@ bool isSafeCast(Expression e, Type tfrom, Type tto) return true; } } + msg = "Source type is incompatible with target type containing a pointer"; return false; } @@ -242,9 +315,245 @@ bool checkUnsafeDotExp(Scope* sc, Expression e, Identifier id, int flag) if (!(flag & DotExpFlag.noDeref)) // this use is attempting a dereference { if (id == Id.ptr) - return sc.setUnsafe(false, e.loc, "`%s.ptr` cannot be used in `@safe` code, use `&%s[0]` instead", e, e); - else - return sc.setUnsafe(false, e.loc, "`%s.%s` cannot be used in `@safe` code", e, id); + return sc.setUnsafe(false, e.loc, "using `%s.ptr` (instead of `&%s[0])`", e, e); + return sc.setUnsafe(false, e.loc, "using `%s.%s`", e, id); + } + return false; +} + +/************************************** + * Safer D adds safety checks to functions with the default + * trust setting. + */ +bool isSaferD(FuncDeclaration fd) +{ + return fd.type.toTypeFunction().trust == TRUST.default_ && fd.saferD; +} + +bool isSafe(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + setFunctionToUnsafe(fd); + return fd.type.toTypeFunction().trust == TRUST.safe; +} + +extern (D) bool isSafeBypassingInference(FuncDeclaration fd) +{ + return !(fd.safetyInprocess) && fd.isSafe(); +} + +bool isTrusted(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + setFunctionToUnsafe(fd); + return fd.type.toTypeFunction().trust == TRUST.trusted; +} + +/***************************************************** + * Report safety violation for function `fd`, or squirrel away + * error message in fd.safetyViolation if needed later. + * Call when `fd` was just inferred to be @system OR + * `fd` was @safe and an tried something unsafe. + * Params: + * fd = function we're gonna rat on + * gag = suppress error message (used in escape.d) + * loc = location of error + * format = printf-style format string + * args = arguments for %s format specifier + */ +extern (D) void reportSafeError(FuncDeclaration fd, bool gag, Loc loc, + const(char)* format, RootObject[] args...) +{ + if (fd.type.toTypeFunction().trust == TRUST.system) // function was just inferred to be @system + { + if (format) + { + fd.safetyViolation = new AttributeViolation(loc, format, args); + } + else if (args.length > 0) + { + if (FuncDeclaration fd2 = (cast(Dsymbol) args[0]).isFuncDeclaration()) + { + fd.safetyViolation = new AttributeViolation(loc, fd2); // call to non-@nogc function + } + } + } + else if (fd.isSafe() || fd.isSaferD()) + { + if (!gag && format) + { + OutBuffer buf; + buf.writestring(AttributeViolation(loc, format, args).action); + if (fd.isSafe()) + buf.writestring(" is not allowed in a `@safe` function"); + else + { + version (IN_GCC) + buf.writestring(" is not allowed in a function with default safety with `-fpreview=safer`"); + else + buf.writestring(" is not allowed in a function with default safety with `-preview=safer`"); + } + .error(loc, "%s", buf.extractChars()); + } + } +} + + +/********************************************** + * Function is doing something unsafe. If inference + * is in process, commit the function to be @system. + * Params: + * fd = the naughty function + * Returns: + * true if this is a safe function and so an error OR is inferred to be @system, + * false otherwise. + */ +extern (D) bool setFunctionToUnsafe(FuncDeclaration fd) +{ + if (fd.safetyInprocess) + { + fd.safetyInprocess = false; + fd.type.toTypeFunction().trust = TRUST.system; + + if (fd.fes) + setFunctionToUnsafe(fd.fes.func); + return true; + } + else if (fd.isSafe() || fd.isSaferD()) + return true; + return false; +} + + +/************************************** + * The function is calling `@system` function `f`, so mark it as unsafe. + * + * Params: + * fd = caller + * f = function being called (needed for diagnostic of inferred functions) + * Returns: whether there's a safe error + */ +extern (D) bool setUnsafeCall(FuncDeclaration fd, FuncDeclaration f) +{ + if (setFunctionToUnsafe(fd)) + { + reportSafeError(fd, false, f.loc, null, f, null); + return fd.isSafe(); + } + return false; +} + +/************************************** + * A statement / expression in this scope is not `@safe`, + * so mark the enclosing function as `@system` + * + * Params: + * sc = scope that the unsafe statement / expression is in + * gag = surpress error message (used in escape.d) + * loc = location of error + * format = printf-style format string + * args = arguments for format string + * Returns: whether there is a safe error + */ +bool setUnsafe(Scope* sc, bool gag, Loc loc, const(char)* format, RootObject[] args...) +{ + if (sc.intypeof) + return false; // typeof(cast(int*)0) is safe + + if (sc.debug_) // debug {} scopes are permissive + return false; + + if (!sc.func) + { + if (sc.varDecl) + { + if (sc.varDecl.storage_class & STC.safe) + { + string action = AttributeViolation(loc, format, args).action; + .error(loc, "%.*s can't initialize `@safe` variable `%s`", action.fTuple.expand, sc.varDecl.toChars()); + return true; + } + else if (!(sc.varDecl.storage_class & STC.trusted)) + { + sc.varDecl.storage_class |= STC.system; + sc.varDecl.systemInferred = true; + } + } + return false; + } + + + if (isRootTraitsCompilesScope(sc)) // __traits(compiles, x) + { + if (sc.func.isSafeBypassingInference()) + { + // Message wil be gagged, but still call error() to update global.errors and for + // -verrors=spec + string action = AttributeViolation(loc, format, args).action; + .error(loc, "%.*s is not allowed in a `@safe` function", action.fTuple.expand); + return true; + } + return false; + } + + if (setFunctionToUnsafe(sc.func)) + { + if (format || args.length > 0) + { + reportSafeError(sc.func, gag, loc, format, args); + } + return sc.func.isSafe(); // it is only an error if in an @safe function } return false; } + +/*************************************** + * Like `setUnsafe`, but for safety errors still behind preview switches + * + * Given a `FeatureState fs`, for example dip1000 / dip25 / systemVariables, + * the behavior changes based on the setting: + * + * - In case of `-revert=fs`, it does nothing. + * - In case of `-preview=fs`, it's the same as `setUnsafe` + * - By default, print a deprecation in `@safe` functions, or store an attribute violation in inferred functions. + * + * Params: + * sc = used to find affected function/variable, and for checking whether we are in a deprecated / speculative scope + * fs = feature state from the preview flag + * gag = surpress error message + * loc = location of error + * format = printf-style format string + * args = arguments for format string + * Returns: whether an actual safe error (not deprecation) occured + */ +bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)* format, RootObject[] args...) +{ + //printf("setUnsafePreview() fs:%d %s\n", fs, fmt); + assert(format); + with (FeatureState) final switch (fs) + { + case disabled: + return false; + + case enabled: + return sc.setUnsafe(gag, loc, format, args); + + case default_: + if (!sc.func) + return false; + if (sc.func.isSafeBypassingInference()) + { + if (!gag && !sc.isDeprecated()) + { + string action = AttributeViolation(loc, format, args).action; + deprecation(loc, "%.*s will become `@system` in a future release", action.fTuple.expand); + } + } + else if (!sc.func.safetyViolation) + { + import dmd.func : AttributeViolation; + sc.func.safetyViolation = new AttributeViolation(loc, format, args); + } + return false; + } +} diff --git a/gcc/d/dmd/sapply.d b/gcc/d/dmd/sapply.d deleted file mode 100644 index 340fbad..0000000 --- a/gcc/d/dmd/sapply.d +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Provides a depth-first statement visitor. - * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved - * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) - * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/sparse.d, _sparse.d) - * Documentation: https://dlang.org/phobos/dmd_sapply.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sapply.d - */ - -module dmd.sapply; - -import dmd.statement; -import dmd.visitor; - -bool walkPostorder(Statement s, StoppableVisitor v) -{ - scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v); - s.accept(pv); - return v.stop; -} - -/************************************** - * A Statement tree walker that will visit each Statement s in the tree, - * in depth-first evaluation order, and call fp(s,param) on it. - * fp() signals whether the walking continues with its return value: - * Returns: - * 0 continue - * 1 done - * It's a bit slower than using virtual functions, but more encapsulated and less brittle. - * Creating an iterator for this would be much more complex. - */ -private extern (C++) final class PostorderStatementVisitor : StoppableVisitor -{ - alias visit = typeof(super).visit; -public: - StoppableVisitor v; - - extern (D) this(StoppableVisitor v) scope @safe - { - this.v = v; - } - - bool doCond(Statement s) - { - if (!stop && s) - s.accept(this); - return stop; - } - - bool applyTo(Statement s) - { - s.accept(v); - stop = v.stop; - return true; - } - - override void visit(Statement s) - { - applyTo(s); - } - - override void visit(PeelStatement s) - { - doCond(s.s) || applyTo(s); - } - - override void visit(CompoundStatement s) - { - for (size_t i = 0; i < s.statements.length; i++) - if (doCond((*s.statements)[i])) - return; - applyTo(s); - } - - override void visit(UnrolledLoopStatement s) - { - for (size_t i = 0; i < s.statements.length; i++) - if (doCond((*s.statements)[i])) - return; - applyTo(s); - } - - override void visit(ScopeStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(WhileStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(DoStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(ForStatement s) - { - doCond(s._init) || doCond(s._body) || applyTo(s); - } - - override void visit(ForeachStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(ForeachRangeStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(IfStatement s) - { - doCond(s.ifbody) || doCond(s.elsebody) || applyTo(s); - } - - override void visit(PragmaStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(SwitchStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(CaseStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(DefaultStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(SynchronizedStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(WithStatement s) - { - doCond(s._body) || applyTo(s); - } - - override void visit(TryCatchStatement s) - { - if (doCond(s._body)) - return; - for (size_t i = 0; i < s.catches.length; i++) - if (doCond((*s.catches)[i].handler)) - return; - applyTo(s); - } - - override void visit(TryFinallyStatement s) - { - doCond(s._body) || doCond(s.finalbody) || applyTo(s); - } - - override void visit(ScopeGuardStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(DebugStatement s) - { - doCond(s.statement) || applyTo(s); - } - - override void visit(LabelStatement s) - { - doCond(s.statement) || applyTo(s); - } -} diff --git a/gcc/d/dmd/scope.h b/gcc/d/dmd/scope.h index f36a14b..b57d77d 100644 --- a/gcc/d/dmd/scope.h +++ b/gcc/d/dmd/scope.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -40,33 +40,15 @@ enum class CSX : uint16_t halt = 0x20, // assert(0) }; -enum class SCOPE +enum class Contract : uint8_t { - // Flags that would not be inherited beyond scope nesting - ctor = 0x0001, // constructor type - noaccesscheck = 0x0002, // don't do access checks - condition = 0x0004, // inside static if/assert condition - debug_ = 0x0008, // inside debug conditional - - // Flags that would be inherited beyond scope nesting - constraint = 0x0010, // inside template constraint - invariant_ = 0x0020, // inside invariant code - require = 0x0040, // inside in contract code - ensure = 0x0060, // inside out contract code - contract = 0x0060, // [mask] we're inside contract code - ctfe = 0x0080, // inside a ctfe-only expression - compile = 0x0100, // inside __traits(compile) - ignoresymbolvisibility = 0x0200, // ignore symbol visibility (Bugzilla 15907) - - Cfile = 0x0800, // C semantics apply - free = 0x8000, // is on free list - fullinst = 0x10000, // fully instantiate templates - ctfeBlock = 0x20000, // inside a `if (__ctfe)` block - dip1000 = 0x40000, // dip1000 errors enabled for this scope - dip25 = 0x80000, // dip25 errors enabled for this scope + none = 0u, + invariant_ = 1u, + require = 2u, + ensure = 3u, }; -struct Scope +struct Scope final { Scope *enclosing; // enclosing Scope @@ -76,10 +58,10 @@ struct Scope VarDeclaration *varDecl; // variable we are in during semantic2 Dsymbol *parent; // parent to use LabelStatement *slabel; // enclosing labelled statement - SwitchStatement *sw; // enclosing switch statement + SwitchStatement *switchStatement; // enclosing switch statement Statement *tryBody; // enclosing _body of TryCatchStatement or TryFinallyStatement - TryFinallyStatement *tf; // enclosing try finally statement - ScopeGuardStatement *os; // enclosing scope(xxx) statement + TryFinallyStatement *tryFinally; // enclosing try finally statement + ScopeGuardStatement *scopeGuard; // enclosing scope(xxx) statement Statement *sbreak; // enclosing statement that supports "break" Statement *scontinue; // enclosing statement that supports "continue" ForeachStatement *fes; // if nested function for ForeachStatement, this is it @@ -120,7 +102,35 @@ struct Scope DeprecatedDeclaration *depdecl; // customized deprecation message - unsigned flags; + uint16_t flags; + uint16_t previews; // state of preview switches + + bool ctor() const; + bool ctor(bool v); + bool noAccessCheck() const; + bool noAccessCheck(bool v); + bool condition() const; + bool condition(bool v); + bool debug_() const; + bool debug_(bool v); + bool inTemplateConstraint() const; + bool inTemplateConstraint(bool v); + Contract contract() const; + Contract contract(Contract v); + bool ctfe() const; + bool ctfe(bool v); + bool traitsCompiles() const; + bool traitsCompiles(bool v); + bool ignoresymbolvisibility() const; + bool ignoresymbolvisibility(bool v); + bool inCfile() const; + bool inCfile(bool v); + bool canFree() const; + bool canFree(bool v); + bool fullinst() const; + bool fullinst(bool v); + bool ctfeBlock() const; + bool ctfeBlock(bool v); UserAttributeDeclaration *userAttribDecl; // user defined attributes @@ -130,6 +140,7 @@ struct Scope AliasDeclaration *aliasAsg; // if set, then aliasAsg is being assigned a new value, // do not set wasRead for it + StructDeclaration *argStruct; // elimiate recursion when looking for rvalue construction - Dsymbol *search(const Loc &loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); + Dsymbol *search(Loc loc, Identifier *ident, Dsymbol *&pscopesym, SearchOptFlags flags = (SearchOptFlags)SearchOpt::all); }; diff --git a/gcc/d/dmd/semantic2.d b/gcc/d/dmd/semantic2.d index f5ce0c0..1c58e63 100644 --- a/gcc/d/dmd/semantic2.d +++ b/gcc/d/dmd/semantic2.d @@ -1,12 +1,12 @@ /** * Performs the semantic2 stage, which deals with initializer expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/semantic2.d, _semantic2.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic2.d, _semantic2.d) * Documentation: https://dlang.org/phobos/dmd_semantic2.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic2.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/semantic2.d */ module dmd.semantic2; @@ -21,6 +21,7 @@ import dmd.astcodegen; import dmd.astenums; import dmd.attrib; import dmd.blockexit; +import dmd.timetrace; import dmd.clone; import dmd.dcast; import dmd.dclass; @@ -40,6 +41,7 @@ import dmd.escape; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; import dmd.id; import dmd.identifier; @@ -55,6 +57,7 @@ import dmd.parse; import dmd.root.filename; import dmd.common.outbuffer; import dmd.root.rmem; +import dmd.root.string : toDString; import dmd.rootobject; import dmd.root.utf; import dmd.sideeffect; @@ -117,43 +120,7 @@ private extern(C++) final class Semantic2Visitor : Visitor else if (result) return; - if (sa.msgs) - { - OutBuffer msgbuf; - for (size_t i = 0; i < sa.msgs.length; i++) - { - Expression e = (*sa.msgs)[i]; - sc = sc.startCTFE(); - e = e.expressionSemantic(sc); - e = resolveProperties(sc, e); - sc = sc.endCTFE(); - e = ctfeInterpretForPragmaMsg(e); - if (e.op == EXP.error) - { - errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars()); - return; - } - StringExp se = e.toStringExp(); - if (se) - { - const slice = se.toUTF8(sc).peekString(); - // Hack to keep old formatting to avoid changing error messages everywhere - if (sa.msgs.length == 1) - msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr); - else - msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr); - } - else - msgbuf.printf("%s", e.toChars()); - } - error(sa.loc, "static assert: %s", msgbuf.extractChars()); - } - else - error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); - if (sc.tinst) - sc.tinst.printInstantiationTrace(); - if (!global.gag) - fatal(); + staticAssertFail(sa, sc); } override void visit(TemplateInstance tempinst) @@ -179,11 +146,9 @@ private extern(C++) final class Semantic2Visitor : Visitor sc.tinst = tempinst; sc.minst = tempinst.minst; - int needGagging = (tempinst.gagged && !global.gag); - uint olderrors = global.errors; - int oldGaggedErrors = -1; // dead-store to prevent spurious warning - if (needGagging) - oldGaggedErrors = global.startGagging(); + const needGagging = (tempinst.gagged && !global.gag); + const olderrors = global.errors; + const oldGaggedErrors = needGagging ? global.startGagging() : -1; for (size_t i = 0; i < tempinst.members.length; i++) { @@ -263,7 +228,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; } - UserAttributeDeclaration.checkGNUABITag(vd, vd._linkage); + checkGNUABITag(vd, vd._linkage); if (vd._init && !vd.toParent().isFuncDeclaration()) { @@ -281,7 +246,7 @@ private extern(C++) final class Semantic2Visitor : Visitor // https://issues.dlang.org/show_bug.cgi?id=14166 // https://issues.dlang.org/show_bug.cgi?id=20417 // Don't run CTFE for the temporary variables inside typeof or __traits(compiles) - vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret); + vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.traitsCompiles ? INITnointerpret : INITinterpret); lowerStaticAAs(vd, sc); vd.inuse--; } @@ -314,7 +279,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return arrayHasInvalidEnumInitializer((cast(StructLiteralExp)e).elements); if (e.op == EXP.assocArrayLiteral) { - AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e; + auto ae = cast(AssocArrayLiteralExp)e; return arrayHasInvalidEnumInitializer(ae.values) || arrayHasInvalidEnumInitializer(ae.keys); } @@ -329,7 +294,7 @@ private extern(C++) final class Semantic2Visitor : Visitor { // Cannot initialize a thread-local class or pointer to struct variable with a literal // that itself is a thread-local reference and would need dynamic initialization also. - if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared()) + if (vd.type.ty == Tclass && vd.type.isMutable() && !vd.type.isShared()) { ExpInitializer ei = vd._init.isExpInitializer(); if (ei && ei.exp.op == EXP.classReference) @@ -392,6 +357,9 @@ private extern(C++) final class Semantic2Visitor : Visitor assert(fd.semanticRun <= PASS.semantic2); fd.semanticRun = PASS.semantic2; + timeTraceBeginEvent(TimeTraceEventType.sema2); + scope(exit) timeTraceEndEvent(TimeTraceEventType.sema2, fd); + //printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr); // Only check valid functions which have a body to avoid errors @@ -501,7 +469,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; TypeFunction f = cast(TypeFunction) fd.type; - UserAttributeDeclaration.checkGNUABITag(fd, fd._linkage); + checkGNUABITag(fd, fd._linkage); //semantic for parameters' UDAs foreach (i, param; f.parameterList) { @@ -535,7 +503,7 @@ private extern(C++) final class Semantic2Visitor : Visitor printf("+Nspace::semantic2('%s')\n", ns.toChars()); scope(exit) printf("-Nspace::semantic2('%s')\n", ns.toChars()); } - UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); + checkGNUABITag(ns, LINK.cpp); if (!ns.members) return; @@ -593,7 +561,7 @@ private extern(C++) final class Semantic2Visitor : Visitor override void visit(CPPNamespaceDeclaration decl) { - UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp); + checkGNUABITag(decl, LINK.cpp); visit(cast(AttribDeclaration)decl); } @@ -620,7 +588,7 @@ private extern(C++) final class Semantic2Visitor : Visitor } // Handles compiler-recognized `core.attribute.gnuAbiTag` - if (UserAttributeDeclaration.isGNUABITag(e)) + if (isGNUABITag(e)) doGNUABITagSemantic(e, lastTag); } } @@ -642,8 +610,7 @@ private extern(C++) final class Semantic2Visitor : Visitor return; } - UserAttributeDeclaration.checkGNUABITag( - ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); + checkGNUABITag(ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); auto sc2 = ad.newScope(sc); @@ -750,8 +717,7 @@ private extern(C++) final class Semantic2Visitor : Visitor */ private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag) { - import dmd.dmangle; - + import dmd.mangle : isValidMangling; // When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal if (e.op == EXP.type) { @@ -886,5 +852,62 @@ private extern(C++) final class StaticAAVisitor : SemanticTimeTransitiveVisitor loweredExp = loweredExp.ctfeInterpret(); aaExp.lowering = loweredExp; + + semanticTypeInfo(sc, loweredExp.type); + } + + // https://issues.dlang.org/show_bug.cgi?id=24602 + // TODO: Is this intionally not visited by SemanticTimeTransitiveVisitor? + override void visit(ClassReferenceExp crExp) + { + this.visit(crExp.value); + } +} + +/** + * Given a static assert with a failing condition, print an error + * Params: + * sa = Static assert with failing condition + * sc = scope for evaluating assert message and printing context + */ +void staticAssertFail(StaticAssert sa, Scope* sc) +{ + if (sa.msgs) + { + OutBuffer msgbuf; + for (size_t i = 0; i < sa.msgs.length; i++) + { + Expression e = (*sa.msgs)[i]; + sc = sc.startCTFE(); + e = e.expressionSemantic(sc); + e = resolveProperties(sc, e); + sc = sc.endCTFE(); + e = ctfeInterpretForPragmaMsg(e); + if (e.op == EXP.error) + { + errorSupplemental(sa.loc, "while evaluating `static assert` argument `%s`", (*sa.msgs)[i].toChars()); + if (!global.gag) + fatal(); + return; + } + if (StringExp se = e.toStringExp()) + { + const slice = se.toUTF8(sc).peekString(); + // Hack to keep old formatting to avoid changing error messages everywhere + if (sa.msgs.length == 1) + msgbuf.printf("\"%.*s\"", cast(int)slice.length, slice.ptr); + else + msgbuf.printf("%.*s", cast(int)slice.length, slice.ptr); + } + else + msgbuf.printf("%s", e.toChars()); + } + error(sa.loc, "static assert: %s", msgbuf.extractChars()); } + else + error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); + if (sc.tinst) + sc.tinst.printInstantiationTrace(); + if (!global.gag) + fatal(); } diff --git a/gcc/d/dmd/semantic3.d b/gcc/d/dmd/semantic3.d index 882d1a9..a24af7b 100644 --- a/gcc/d/dmd/semantic3.d +++ b/gcc/d/dmd/semantic3.d @@ -1,12 +1,12 @@ /** * Performs the semantic3 stage, which deals with function bodies. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/semantic3.d, _semantic3.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/semantic3.d, _semantic3.d) * Documentation: https://dlang.org/phobos/dmd_semantic3.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic3.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/semantic3.d */ module dmd.semantic3; @@ -119,16 +119,14 @@ private extern(C++) final class Semantic3Visitor : Visitor sc.tinst = tempinst; sc.minst = tempinst.minst; - int needGagging = (tempinst.gagged && !global.gag); - uint olderrors = global.errors; - int oldGaggedErrors = -1; // dead-store to prevent spurious warning + bool needGagging = tempinst.gagged && !global.gag; + const olderrors = global.errors; + const oldGaggedErrors = needGagging ? global.startGagging() : -1; /* If this is a gagged instantiation, gag errors. * Future optimisation: If the results are actually needed, errors * would already be gagged, so we don't really need to run semantic * on the members. */ - if (needGagging) - oldGaggedErrors = global.startGagging(); for (size_t i = 0; i < tempinst.members.length; i++) { @@ -171,7 +169,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc = sc.push(tmix.argsym); sc = sc.push(tmix); - uint olderrors = global.errors; + const olderrors = global.errors; for (size_t i = 0; i < tmix.members.length; i++) { @@ -221,6 +219,11 @@ private extern(C++) final class Semantic3Visitor : Visitor override void visit(FuncDeclaration funcdecl) { //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", funcdecl.kind(), funcdecl.toChars(), sc); + import dmd.timetrace; + import dmd.root.string : toDString; + timeTraceBeginEvent(TimeTraceEventType.sema3); + scope (exit) timeTraceEndEvent(TimeTraceEventType.sema3, funcdecl); + /* Determine if function should add `return 0;` */ bool addReturn0() @@ -229,7 +232,7 @@ private extern(C++) final class Semantic3Visitor : Visitor auto f = funcdecl.type.isTypeFunction(); // C11 5.1.2.2.3 - if (sc.flags & SCOPE.Cfile && funcdecl.isCMain() && f.next.ty == Tint32) + if (sc.inCfile && funcdecl.isCMain() && f.next.ty == Tint32) return true; return f.next.ty == Tvoid && (funcdecl.isMain() || funcdecl.isCMain()); @@ -267,7 +270,7 @@ private extern(C++) final class Semantic3Visitor : Visitor //{ static int x; if (++x == 2) *(char*)0=0; } //printf("\tlinkage = %d\n", sc.linkage); - if (funcdecl.ident == Id.assign && !funcdecl.inuse) + if (funcdecl.ident == Id.opAssign && !funcdecl.inuse) { if (funcdecl.storage_class & STC.inference) { @@ -275,7 +278,7 @@ private extern(C++) final class Semantic3Visitor : Visitor * For generated opAssign function, any errors * from its body need to be gagged. */ - uint oldErrors = global.startGagging(); + const oldErrors = global.startGagging(); ++funcdecl.inuse; funcdecl.semantic3(sc); --funcdecl.inuse; @@ -290,11 +293,12 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - //printf(" sc.incontract = %d\n", (sc.flags & SCOPE.contract)); + //printf(" sc.incontract = %d\n", sc.contract); if (funcdecl.semanticRun >= PASS.semantic3) return; funcdecl.semanticRun = PASS.semantic3; funcdecl.hasSemantic3Errors = false; + funcdecl.saferD = sc.previews.safer; if (!funcdecl.type || funcdecl.type.ty != Tfunction) return; @@ -308,13 +312,13 @@ private extern(C++) final class Semantic3Visitor : Visitor return; } - uint oldErrors = global.errors; + const oldErrors = global.errors; auto fds = FuncDeclSem3(funcdecl,sc); fds.checkInContractOverrides(); // Remember whether we need to generate an 'out' contract. - immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl); + immutable bool needEnsure = funcdecl.needsFensure(); if (funcdecl.fbody || funcdecl.frequires || needEnsure) { @@ -334,7 +338,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.ctorflow.callSuper = CSX.none; sc2.sbreak = null; sc2.scontinue = null; - sc2.sw = null; + sc2.switchStatement = null; sc2.fes = funcdecl.fes; sc2.linkage = funcdecl.isCsymbol() ? LINK.c : LINK.d; sc2.stc &= STC.flowThruFunction; @@ -342,9 +346,12 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.explicitVisibility = 0; sc2.aligndecl = null; if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure) - sc2.flags = sc.flags & ~SCOPE.contract; - sc2.tf = null; - sc2.os = null; + { + sc2.copyFlagsFrom(sc); + sc2.contract = Contract.none; + } + sc2.tryFinally = null; + sc2.scopeGuard = null; sc2.inLoop = false; sc2.inDefaultArg = false; sc2.userAttribDecl = null; @@ -373,7 +380,7 @@ private extern(C++) final class Semantic3Visitor : Visitor if (!sc.intypeof) { if (fld.tok == TOK.delegate_) - .error(funcdecl.loc, "%s `%s` cannot be %s members", funcdecl.kind, funcdecl.toPrettyChars, ad.kind()); + .error(funcdecl.loc, "%s `%s` cannot be %s members", funcdecl.kind, funcdecl.toErrMsg, ad.kind()); else fld.tok = TOK.function_; } @@ -424,9 +431,12 @@ private extern(C++) final class Semantic3Visitor : Visitor .error(funcdecl.loc, "%s `%s` `object.TypeInfo_Tuple` could not be found, but is implicitly used in D-style variadic functions", funcdecl.kind, funcdecl.toPrettyChars); else .error(funcdecl.loc, "%s `%s` `object.TypeInfo` could not be found, but is implicitly used in D-style variadic functions", funcdecl.kind, funcdecl.toPrettyChars); - fatal(); + funcdecl.errors = true; } + } + if (!funcdecl.errors && f.linkage == LINK.d) + { // Declare _arguments[] funcdecl.v_arguments = new VarDeclaration(funcdecl.loc, Type.typeinfotypelist.type, Id._arguments_typeinfo, null); funcdecl.v_arguments.storage_class |= STC.temp | STC.parameter; @@ -442,7 +452,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2.insert(_arguments); _arguments.parent = funcdecl; } - if (f.linkage == LINK.d || f.parameterList.length) + if (!funcdecl.errors && (f.linkage == LINK.d || f.parameterList.length)) { // Declare _argptr Type t = target.va_listType(funcdecl.loc, sc); @@ -468,7 +478,7 @@ private extern(C++) final class Semantic3Visitor : Visitor foreach (i, fparam; f.parameterList) { Identifier id = fparam.ident; - StorageClass stc = 0; + STC stc = STC.none; if (!id) { /* Generate identifier for un-named parameter, @@ -540,8 +550,7 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement fpreinv = null; if (funcdecl.addPreInvariant()) { - Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); - if (e) + if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis)) fpreinv = new ExpStatement(Loc.initial, e); } @@ -549,8 +558,7 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement fpostinv = null; if (funcdecl.addPostInvariant()) { - Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis); - if (e) + if (Expression e = addInvariant(funcdecl.isThis(), funcdecl.vthis)) fpostinv = new ExpStatement(Loc.initial, e); } @@ -601,7 +609,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - bool inferRef = (f.isref && (funcdecl.storage_class & STC.auto_)); + bool inferRef = (f.isRef && (funcdecl.storage_class & STC.auto_)); funcdecl.fbody = funcdecl.fbody.statementSemantic(sc2); if (!funcdecl.fbody) @@ -645,12 +653,12 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.returns.remove(i); continue; } - if (inferRef && f.isref && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336 - f.isref = false; + if (inferRef && f.isRef && !exp.type.constConv(f.next)) // https://issues.dlang.org/show_bug.cgi?id=13336 + f.isRef = false; i++; } } - if (f.isref) // Function returns a reference + if (f.isRef) // Function returns a reference { if (funcdecl.storage_class & STC.auto_) funcdecl.storage_class &= ~STC.auto_; @@ -757,8 +765,8 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.buildEnsureRequire(); // Check for errors related to 'nothrow'. - const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isnothrow ? global.errorSink : null); - if (f.isnothrow && blockexit & BE.throw_) + const blockexit = funcdecl.fbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null); + if (f.isNothrow && blockexit & BE.throw_) error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); if (!(blockexit & (BE.throw_ | BE.halt) || funcdecl.hasCatches)) @@ -773,7 +781,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { if (funcdecl.type == f) f = cast(TypeFunction)f.copy(); - f.isnothrow = !(blockexit & BE.throw_); + f.isNothrow = !(blockexit & BE.throw_); } if (funcdecl.fbody.isErrorStatement()) @@ -790,7 +798,8 @@ private extern(C++) final class Semantic3Visitor : Visitor Statement s = new ReturnStatement(funcdecl.loc, null); s = s.statementSemantic(sc2); funcdecl.fbody = new CompoundStatement(funcdecl.loc, funcdecl.fbody, s); - funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); + funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp; + funcdecl.hasReturnExp = true; } } else if (funcdecl.fes) @@ -801,7 +810,8 @@ private extern(C++) final class Semantic3Visitor : Visitor Expression e = IntegerExp.literal!0; Statement s = new ReturnStatement(Loc.initial, e); funcdecl.fbody = new CompoundStatement(Loc.initial, funcdecl.fbody, s); - funcdecl.hasReturnExp |= (funcdecl.hasReturnExp & 1 ? 16 : 1); + funcdecl.hasMultipleReturnExp = funcdecl.hasReturnExp; + funcdecl.hasReturnExp = true; } assert(!funcdecl.returnLabel); } @@ -816,8 +826,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } else { - const(bool) inlineAsm = (funcdecl.hasReturnExp & 8) != 0; - if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !inlineAsm && !(sc.flags & SCOPE.Cfile)) + if ((blockexit & BE.fallthru) && f.next.ty != Tvoid && !funcdecl.hasInlineAsm && !sc.inCfile) { if (!funcdecl.hasReturnExp) .error(funcdecl.loc, "%s `%s` has no `return` statement, but is expected to return a value of type `%s`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars()); @@ -906,36 +915,66 @@ private extern(C++) final class Semantic3Visitor : Visitor } } - const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; - // if a copy constructor is present, the return type conversion will be handled by it - if (!(hasCopyCtor && exp.isLvalue())) + // Function returns a reference + if (f.isRef) { - if (f.isref && !MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray()) + if (!MODimplicitConv(exp.type.mod, tret.mod) && !tret.isTypeSArray()) error(exp.loc, "expression `%s` of type `%s` is not implicitly convertible to return type `ref %s`", exp.toChars(), exp.type.toChars(), tret.toChars()); else exp = exp.implicitCastTo(sc2, tret); - } - if (f.isref) - { - // Function returns a reference exp = exp.toLvalue(sc2, "`ref` return"); - checkReturnEscapeRef(sc2, exp, false); + checkReturnEscapeRef(*sc2, exp, false); exp = exp.optimize(WANTvalue, /*keepLvalue*/ true); } else { + // if a copy constructor is present, the return type conversion will be handled by it + const hasCopyCtor = exp.type.ty == Tstruct && (cast(TypeStruct)exp.type).sym.hasCopyCtor; + if (!hasCopyCtor || !exp.isLvalue()) + { + const errors = global.startGagging(); + auto implicitlyCastedExp = exp.implicitCastTo(sc2, tret); + global.endGagging(errors); + + // <https://github.com/dlang/dmd/issues/20888> + if (implicitlyCastedExp.isErrorExp()) + { + auto types = toAutoQualChars(exp.type, tret); + error( + exp.loc, + "return value `%s` of type `%s` does not match return type `%s`" + ~ ", and cannot be implicitly converted", + exp.toErrMsg(), + types[0], + types[1], + ); + + if (const func = exp.type.isFunction_Delegate_PtrToFunction()) + if (func.next.equals(tret)) + errorSupplemental( + exp.loc, + "Did you intend to call the %s?", + (exp.type.isPtrToFunction()) + ? "function pointer" + : exp.type.kind + ); + } + + exp = implicitlyCastedExp; + } + exp = exp.optimize(WANTvalue); /* https://issues.dlang.org/show_bug.cgi?id=10789 * If NRVO is not possible, all returned lvalues should call their postblits. */ if (!funcdecl.isNRVO()) - exp = doCopyOrMove(sc2, exp, f.next); + exp = doCopyOrMove(sc2, exp, f.next, true, true); if (tret.hasPointers()) - checkReturnEscape(sc2, exp, false); + checkReturnEscape(*sc2, exp, false); } exp = checkGC(sc2, exp); @@ -967,7 +1006,7 @@ private extern(C++) final class Semantic3Visitor : Visitor sc2 = sc2.pop(); } - if (global.params.inclusiveInContracts) + if (sc.previews.inclusiveInContracts) { funcdecl.frequire = funcdecl.mergeFrequireInclusivePreview( funcdecl.frequire, funcdecl.fdrequireParams); @@ -984,7 +1023,7 @@ private extern(C++) final class Semantic3Visitor : Visitor /* Do the semantic analysis on the [in] preconditions and * [out] postconditions. */ - immutable bool isnothrow = f.isnothrow && !funcdecl.nothrowInprocess; + immutable bool isNothrow = f.isNothrow && !funcdecl.nothrowInprocess; if (freq) { /* frequire is composed of the [in] contracts @@ -993,24 +1032,21 @@ private extern(C++) final class Semantic3Visitor : Visitor sym.parent = sc2.scopesym; sym.endlinnum = funcdecl.endloc.linnum; sc2 = sc2.push(sym); - sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.require; + sc2.contract = Contract.require; // BUG: need to error if accessing out parameters // BUG: need to disallow returns // BUG: verify that all in and ref parameters are read freq = freq.statementSemantic(sc2); - // @@@DEPRECATED_2.111@@@ - pass `isnothrow` instead of `false` to print a more detailed error msg` const blockExit = freq.blockExit(funcdecl, null); if (blockExit & BE.throw_) { - if (isnothrow) - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, can be made an error in 2.111 - deprecation(funcdecl.loc, "`%s`: `in` contract may throw but function is marked as `nothrow`", + if (isNothrow) + error(funcdecl.loc, "`%s`: `in` contract may throw but function is marked as `nothrow`", funcdecl.toPrettyChars()); else if (funcdecl.nothrowInprocess) - f.isnothrow = false; + f.isNothrow = false; } funcdecl.hasNoEH = false; @@ -1038,7 +1074,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } sc2 = scout; //push - sc2.flags = (sc2.flags & ~SCOPE.contract) | SCOPE.ensure; + sc2.contract = Contract.ensure; // BUG: need to disallow returns and throws @@ -1047,17 +1083,14 @@ private extern(C++) final class Semantic3Visitor : Visitor fens = fens.statementSemantic(sc2); - // @@@DEPRECATED_2.111@@@ - pass `isnothrow` instead of `false` to print a more detailed error msg` const blockExit = fens.blockExit(funcdecl, null); if (blockExit & BE.throw_) { - if (isnothrow) - // @@@DEPRECATED_2.111@@@ - // Deprecated in 2.101, can be made an error in 2.111 - deprecation(funcdecl.loc, "`%s`: `out` contract may throw but function is marked as `nothrow`", + if (isNothrow) + error(funcdecl.loc, "`%s`: `out` contract may throw but function is marked as `nothrow`", funcdecl.toPrettyChars()); else if (funcdecl.nothrowInprocess) - f.isnothrow = false; + f.isNothrow = false; } funcdecl.hasNoEH = false; @@ -1174,32 +1207,31 @@ private extern(C++) final class Semantic3Visitor : Visitor { if (v.isReference() || (v.storage_class & STC.lazy_)) continue; - if (v.needsScopeDtor()) - { - v.storage_class |= STC.nodtor; - if (!paramsNeedDtor) - continue; - - // same with ExpStatement.scopeCode() - Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); + if (!v.needsScopeDtor()) + continue; + v.storage_class |= STC.nodtor; + if (!paramsNeedDtor) + continue; - s = s.statementSemantic(sc2); + // same with ExpStatement.scopeCode() + Statement s = new DtorExpStatement(Loc.initial, v.edtor, v); - const blockexit = s.blockExit(funcdecl, isnothrow ? global.errorSink : null); - if (blockexit & BE.throw_) - { - funcdecl.hasNoEH = false; - if (isnothrow) - error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); - else if (funcdecl.nothrowInprocess) - f.isnothrow = false; - } + s = s.statementSemantic(sc2); - if (sbody.blockExit(funcdecl, f.isnothrow ? global.errorSink : null) == BE.fallthru) - sbody = new CompoundStatement(Loc.initial, sbody, s); - else - sbody = new TryFinallyStatement(Loc.initial, sbody, s); + const blockexit = s.blockExit(funcdecl, isNothrow ? global.errorSink : null); + if (blockexit & BE.throw_) + { + funcdecl.hasNoEH = false; + if (isNothrow) + error(funcdecl.loc, "%s `%s` may throw but is marked as `nothrow`", funcdecl.kind(), funcdecl.toPrettyChars()); + else if (funcdecl.nothrowInprocess) + f.isNothrow = false; } + + if (sbody.blockExit(funcdecl, f.isNothrow ? global.errorSink : null) == BE.fallthru) + sbody = new CompoundStatement(Loc.initial, sbody, s); + else + sbody = new TryFinallyStatement(Loc.initial, sbody, s); } } // from this point on all possible 'throwers' are checked @@ -1209,8 +1241,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { /* Wrap the entire function body in a synchronized statement */ - ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration(); - if (cd) + if (ClassDeclaration cd = funcdecl.toParentDecl().isClassDeclaration()) { if (target.libraryObjectMonitors(funcdecl, sbody)) { @@ -1303,7 +1334,7 @@ private extern(C++) final class Semantic3Visitor : Visitor funcdecl.nogcInprocess = false; if (funcdecl.type == f) f = cast(TypeFunction)f.copy(); - f.isnogc = true; + f.isNogc = true; } finishScopeParamInference(funcdecl, f); @@ -1317,8 +1348,8 @@ private extern(C++) final class Semantic3Visitor : Visitor { sc = sc.push(); if (funcdecl.isCtorDeclaration()) // https://issues.dlang.org/show_bug.cgi?id=#15665 - f.isctor = true; - sc.stc = 0; + f.isCtor = true; + sc.stc = STC.none; sc.linkage = funcdecl._linkage; // https://issues.dlang.org/show_bug.cgi?id=8496 funcdecl.type = f.typeSemantic(funcdecl.loc, sc); sc = sc.pop(); @@ -1332,7 +1363,7 @@ private extern(C++) final class Semantic3Visitor : Visitor // Don't allow D `immutable` and `shared` types to be interfaced with C++ if (type.isImmutable() || type.isShared()) return true; - else if (Type cpptype = target.cpp.parameterType(type)) + if (Type cpptype = target.cpp.parameterType(type)) type = cpptype; if (origType is null) @@ -1374,7 +1405,7 @@ private extern(C++) final class Semantic3Visitor : Visitor } if (isCppNonMappableType(f.next.toBasetype())) { - .error(funcdecl.loc, "%s `%s` cannot return type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toPrettyChars, f.next.toChars()); + .error(funcdecl.loc, "%s `%s` cannot return type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toErrMsg(), f.next.toChars()); if (f.next.isTypeDArray()) errorSupplemental(funcdecl.loc, "slices are specific to D and do not have a counterpart representation in C++", f.next.toChars()); funcdecl.errors = true; @@ -1383,7 +1414,7 @@ private extern(C++) final class Semantic3Visitor : Visitor { if (isCppNonMappableType(param.type.toBasetype(), param)) { - .error(funcdecl.loc, "%s `%s` cannot have parameter of type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toPrettyChars, param.type.toChars()); + .error(funcdecl.loc, "%s `%s` cannot have parameter of type `%s` because its linkage is `extern(C++)`", funcdecl.kind, funcdecl.toErrMsg(), param.type.toChars()); if (param.type.toBasetype().isTypeSArray()) errorSupplemental(funcdecl.loc, "perhaps use a `%s*` type instead", param.type.nextOf().mutableOf().unSharedOf().toChars()); @@ -1393,8 +1424,8 @@ private extern(C++) final class Semantic3Visitor : Visitor } // Do live analysis - if (global.params.useDIP1021 && funcdecl.fbody && funcdecl.type.ty != Terror && - funcdecl.type.isTypeFunction().islive) + if (sc.previews.dip1021 && funcdecl.fbody && funcdecl.type.ty != Terror && + funcdecl.type.isTypeFunction().isLive) { oblive(funcdecl); } @@ -1431,7 +1462,7 @@ private extern(C++) final class Semantic3Visitor : Visitor */ AggregateDeclaration ad = ctor.isMemberDecl(); if (!ctor.fbody || !ad || !ad.fieldDtor || - global.params.dtorFields == FeatureState.disabled || !global.params.useExceptions || ctor.type.toTypeFunction.isnothrow) + global.params.dtorFields == FeatureState.disabled || !global.params.useExceptions || ctor.type.toTypeFunction.isNothrow) return visit(cast(FuncDeclaration)ctor); /* Generate: @@ -1444,16 +1475,12 @@ private extern(C++) final class Semantic3Visitor : Visitor auto sexp = new ExpStatement(ctor.loc, ce); auto ss = new ScopeStatement(ctor.loc, sexp, ctor.loc); - // @@@DEPRECATED_2.106@@@ - // Allow negligible attribute violations to allow for a smooth - // transition. Remove this after the usual deprecation period - // after 2.106. if (global.params.dtorFields == FeatureState.default_) { auto ctf = cast(TypeFunction) ctor.type; auto dtf = cast(TypeFunction) ad.fieldDtor.type; - const ngErr = ctf.isnogc && !dtf.isnogc; + const ngErr = ctf.isNogc && !dtf.isNogc; const puErr = ctf.purity && !dtf.purity; const saErr = ctf.trust == TRUST.safe && dtf.trust <= TRUST.system; @@ -1462,13 +1489,13 @@ private extern(C++) final class Semantic3Visitor : Visitor // storage_class is apparently not set for dtor & ctor OutBuffer ob; stcToBuffer(ob, - (ngErr ? STC.nogc : 0) | - (puErr ? STC.pure_ : 0) | - (saErr ? STC.system : 0) + (ngErr ? STC.nogc : STC.none) | + (puErr ? STC.pure_ : STC.none) | + (saErr ? STC.system : STC.none) ); - ctor.loc.deprecation("`%s` has stricter attributes than its destructor (`%s`)", ctor.toPrettyChars(), ob.peekChars()); - ctor.loc.deprecationSupplemental("The destructor will be called if an exception is thrown"); - ctor.loc.deprecationSupplemental("Either make the constructor `nothrow` or adjust the field destructors"); + ctor.loc.error("`%s` has stricter attributes than its destructor (`%s`)", ctor.toPrettyChars(), ob.peekChars()); + ctor.loc.errorSupplemental("The destructor will be called if an exception is thrown"); + ctor.loc.errorSupplemental("Either make the constructor `nothrow` or adjust the field destructors"); ce.ignoreAttributes = true; } @@ -1642,7 +1669,7 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.xeq._scope && sd.xeq.semanticRun < PASS.semantic3done) { - uint errors = global.startGagging(); + const errors = global.startGagging(); sd.xeq.semantic3(sd.xeq._scope); if (global.endGagging(errors)) sd.xeq = sd.xerreq; @@ -1652,7 +1679,7 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.xcmp._scope && sd.xcmp.semanticRun < PASS.semantic3done) { - uint errors = global.startGagging(); + const errors = global.startGagging(); sd.xcmp.semantic3(sd.xcmp._scope); if (global.endGagging(errors)) sd.xcmp = sd.xerrcmp; @@ -1687,3 +1714,72 @@ void semanticTypeInfoMembers(StructDeclaration sd) sd.dtor.semantic3(sd.dtor._scope); } } + +/*********************************************** + * Check that the function contains any closure. + * If it's @nogc, report suitable errors. + * This is mostly consistent with FuncDeclaration::needsClosure(). + * + * Returns: + * true if any errors occur. + */ +extern (D) bool checkClosure(FuncDeclaration fd) +{ + //printf("checkClosure() %s\n", toPrettyChars()); + if (!fd.needsClosure()) + return false; + + if (fd.setGC(fd.loc, "allocating a closure for `%s()`", fd)) + { + .error(fd.loc, "%s `%s` is `@nogc` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars()); + if (global.gag) // need not report supplemental errors + return true; + } + else if (!global.params.useGC) + { + .error(fd.loc, "%s `%s` is `-betterC` yet allocates closure for `%s()` with the GC", fd.kind, fd.toPrettyChars(), fd.toChars()); + if (global.gag) // need not report supplemental errors + return true; + } + else + { + fd.printGCUsage(fd.loc, "using closure causes GC allocation"); + return false; + } + + FuncDeclarations a; + foreach (v; fd.closureVars) + { + foreach (f; v.nestedrefs) + { + assert(f !is fd); + + LcheckAncestorsOfANestedRef: + for (Dsymbol s = f; s && s !is fd; s = s.toParentP(fd)) + { + auto fx = s.isFuncDeclaration(); + if (!fx) + continue; + if (fx.isThis() || + fx.tookAddressOf || + checkEscapingSiblings(fx, fd)) + { + foreach (f2; a) + { + if (f2 == f) + break LcheckAncestorsOfANestedRef; + } + a.push(f); + .errorSupplemental(f.loc, "%s `%s` closes over variable `%s`", + f.kind, f.toErrMsg(), v.toChars()); + if (v.ident != Id.This) + .errorSupplemental(v.loc, "`%s` declared here", v.toChars()); + + break LcheckAncestorsOfANestedRef; + } + } + } + } + + return true; +} diff --git a/gcc/d/dmd/sideeffect.d b/gcc/d/dmd/sideeffect.d index 1d4745a..5984466 100644 --- a/gcc/d/dmd/sideeffect.d +++ b/gcc/d/dmd/sideeffect.d @@ -1,12 +1,12 @@ /** * Find side-effects of expressions. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d, _sideeffect.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/sideeffect.d, _sideeffect.d) * Documentation: https://dlang.org/phobos/dmd_sideeffect.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/sideeffect.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/sideeffect.d */ module dmd.sideeffect; @@ -18,15 +18,17 @@ import dmd.errors; import dmd.expression; import dmd.expressionsem; import dmd.func; +import dmd.funcsem; import dmd.globals; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; import dmd.mtype; -import dmd.postordervisitor; import dmd.tokens; import dmd.typesem; import dmd.visitor; +import dmd.visitor.postorder; /************************************************** * Front-end expression rewriting should create temporary variables for @@ -112,7 +114,7 @@ int callSideEffectLevel(FuncDeclaration f) return 0; assert(f.type.ty == Tfunction); TypeFunction tf = cast(TypeFunction)f.type; - if (!tf.isnothrow) + if (!tf.isNothrow) return 0; final switch (f.isPure()) { @@ -122,7 +124,7 @@ int callSideEffectLevel(FuncDeclaration f) return 0; case PURE.const_: - return mutabilityOfType(tf.isref, tf.next) == 2 ? 2 : 1; + return mutabilityOfType(tf.isRef, tf.next) == 2 ? 2 : 1; } } @@ -137,7 +139,7 @@ int callSideEffectLevel(Type t) assert(t.ty == Tfunction); tf = cast(TypeFunction)t; } - if (!tf.isnothrow) // function can throw + if (!tf.isNothrow) // function can throw return 0; tf.purityLevel(); @@ -151,7 +153,7 @@ int callSideEffectLevel(Type t) } if (purity == PURE.const_) - return mutabilityOfType(tf.isref, tf.next) == 2 ? 2 : 1; + return mutabilityOfType(tf.isRef, tf.next) == 2 ? 2 : 1; return 0; } @@ -345,12 +347,13 @@ bool discardValue(Expression e) BinExp tmp = e.isBinExp(); assert(tmp); - error(e.loc, "the result of the equality expression `%s` is discarded", e.toChars()); + error(e.loc, "the result of the equality expression `%s` is discarded", e.toErrMsg()); bool seenSideEffect = false; foreach(expr; [tmp.e1, tmp.e2]) { - if (hasSideEffect(expr)) { - errorSupplemental(expr.loc, "note that `%s` may have a side effect", expr.toChars()); + if (hasSideEffect(expr)) + { + errorSupplemental(expr.loc, "note that `%s` may have a side effect", expr.toErrMsg()); seenSideEffect |= true; } } @@ -358,7 +361,7 @@ bool discardValue(Expression e) default: break; } - error(e.loc, "`%s` has no effect", e.toChars()); + error(e.loc, "`%s` has no effect", e.toErrMsg()); return true; } @@ -371,7 +374,7 @@ bool discardValue(Expression e) * Returns: * Newly created temporary variable. */ -VarDeclaration copyToTemp(StorageClass stc, const char[] name, Expression e) +VarDeclaration copyToTemp(STC stc, const char[] name, Expression e) { assert(name[0] == '_' && name[1] == '_'); auto vd = new VarDeclaration(e.loc, e.type, @@ -407,10 +410,10 @@ Expression extractSideEffect(Scope* sc, const char[] name, * https://issues.dlang.org/show_bug.cgi?id=17145 */ if (!alwaysCopy && - ((sc.flags & SCOPE.ctfe) ? !hasSideEffect(e) : isTrivialExp(e))) + (sc.ctfe ? !hasSideEffect(e) : isTrivialExp(e))) return e; - auto vd = copyToTemp(0, name, e); + auto vd = copyToTemp(STC.none, name, e); vd.storage_class |= e.isLvalue() ? STC.ref_ : STC.rvalue; e0 = Expression.combine(e0, new DeclarationExp(vd.loc, vd) diff --git a/gcc/d/dmd/statement.d b/gcc/d/dmd/statement.d index a79b78a..2ade0a9 100644 --- a/gcc/d/dmd/statement.d +++ b/gcc/d/dmd/statement.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statement.d, _statement.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statement.d, _statement.d) * Documentation: https://dlang.org/phobos/dmd_statement.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/statement.d */ module dmd.statement; @@ -19,7 +19,6 @@ import core.stdc.stdio; import dmd.arraytypes; import dmd.astenums; import dmd.ast_node; -import dmd.errors; import dmd.cond; import dmd.declaration; import dmd.dsymbol; @@ -30,10 +29,10 @@ import dmd.identifier; import dmd.location; import dmd.mtype; import dmd.rootobject; -import dmd.sapply; import dmd.staticassert; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /*********************************************************** * Specification: https://dlang.org/spec/statement.html @@ -48,7 +47,7 @@ extern (C++) abstract class Statement : ASTNode return DYNCAST.statement; } - final extern (D) this(const ref Loc loc, STMT stmt) @safe + final extern (D) this(Loc loc, STMT stmt) @safe { this.loc = loc; this.stmt = stmt; @@ -267,57 +266,58 @@ extern (C++) abstract class Statement : ASTNode pure nothrow @nogc inout(ReturnStatement) endsWithReturnStatement() inout { return null; } - final pure inout nothrow @nogc @safe: - - /******************** - * A cheaper method of doing downcasting of Statements. - * Returns: - * the downcast statement if it can be downcasted, otherwise `null` - */ - inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; } - inout(PeelStatement) isPeelStatement() { return stmt == STMT.Peel ? cast(typeof(return))this : null; } - inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } - inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } - inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } - inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; } - inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; } - inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; } - inout(StaticForeachStatement) isStaticForeachStatement() { return stmt == STMT.StaticForeach ? cast(typeof(return))this : null; } - inout(CaseStatement) isCaseStatement() { return stmt == STMT.Case ? cast(typeof(return))this : null; } - inout(DefaultStatement) isDefaultStatement() { return stmt == STMT.Default ? cast(typeof(return))this : null; } - inout(LabelStatement) isLabelStatement() { return stmt == STMT.Label ? cast(typeof(return))this : null; } - inout(GotoStatement) isGotoStatement() { return stmt == STMT.Goto ? cast(typeof(return))this : null; } - inout(GotoDefaultStatement) isGotoDefaultStatement() { return stmt == STMT.GotoDefault ? cast(typeof(return))this : null; } - inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; } - inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; } - inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; } - inout(MixinStatement) isMixinStatement() { return stmt == STMT.Mixin ? cast(typeof(return))this : null; } - inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; } - inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; } - inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; } - inout(ForStatement) isForStatement() { return stmt == STMT.For ? cast(typeof(return))this : null; } - inout(ForeachStatement) isForeachStatement() { return stmt == STMT.Foreach ? cast(typeof(return))this : null; } - inout(SwitchStatement) isSwitchStatement() { return stmt == STMT.Switch ? cast(typeof(return))this : null; } - inout(ContinueStatement) isContinueStatement() { return stmt == STMT.Continue ? cast(typeof(return))this : null; } - inout(WithStatement) isWithStatement() { return stmt == STMT.With ? cast(typeof(return))this : null; } - inout(TryCatchStatement) isTryCatchStatement() { return stmt == STMT.TryCatch ? cast(typeof(return))this : null; } - inout(ThrowStatement) isThrowStatement() { return stmt == STMT.Throw ? cast(typeof(return))this : null; } - inout(DebugStatement) isDebugStatement() { return stmt == STMT.Debug ? cast(typeof(return))this : null; } - inout(TryFinallyStatement) isTryFinallyStatement() { return stmt == STMT.TryFinally ? cast(typeof(return))this : null; } - inout(ScopeGuardStatement) isScopeGuardStatement() { return stmt == STMT.ScopeGuard ? cast(typeof(return))this : null; } - inout(SwitchErrorStatement) isSwitchErrorStatement() { return stmt == STMT.SwitchError ? cast(typeof(return))this : null; } - inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; } - inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; } - inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; } - inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } - inout(PragmaStatement) isPragmaStatement() { return stmt == STMT.Pragma ? cast(typeof(return))this : null; } - inout(StaticAssertStatement) isStaticAssertStatement() { return stmt == STMT.StaticAssert ? cast(typeof(return))this : null; } - inout(CaseRangeStatement) isCaseRangeStatement() { return stmt == STMT.CaseRange ? cast(typeof(return))this : null; } - inout(SynchronizedStatement) isSynchronizedStatement() { return stmt == STMT.Synchronized ? cast(typeof(return))this : null; } - inout(AsmStatement) isAsmStatement() { return stmt == STMT.Asm ? cast(typeof(return))this : null; } - inout(InlineAsmStatement) isInlineAsmStatement() { return stmt == STMT.InlineAsm ? cast(typeof(return))this : null; } - inout(GccAsmStatement) isGccAsmStatement() { return stmt == STMT.GccAsm ? cast(typeof(return))this : null; } - inout(ImportStatement) isImportStatement() { return stmt == STMT.Import ? cast(typeof(return))this : null; } + final pure inout nothrow @nogc @trusted + { + /******************** + * A cheaper method of doing downcasting of Statements. + * Returns: + * the downcast statement if it can be downcasted, otherwise `null` + */ + inout(ErrorStatement) isErrorStatement() { return stmt == STMT.Error ? cast(typeof(return))this : null; } + inout(PeelStatement) isPeelStatement() { return stmt == STMT.Peel ? cast(typeof(return))this : null; } + inout(ScopeStatement) isScopeStatement() { return stmt == STMT.Scope ? cast(typeof(return))this : null; } + inout(ExpStatement) isExpStatement() { return stmt == STMT.Exp ? cast(typeof(return))this : null; } + inout(CompoundStatement) isCompoundStatement() { return stmt == STMT.Compound ? cast(typeof(return))this : null; } + inout(ReturnStatement) isReturnStatement() { return stmt == STMT.Return ? cast(typeof(return))this : null; } + inout(IfStatement) isIfStatement() { return stmt == STMT.If ? cast(typeof(return))this : null; } + inout(ConditionalStatement) isConditionalStatement() { return stmt == STMT.Conditional ? cast(typeof(return))this : null; } + inout(StaticForeachStatement) isStaticForeachStatement() { return stmt == STMT.StaticForeach ? cast(typeof(return))this : null; } + inout(CaseStatement) isCaseStatement() { return stmt == STMT.Case ? cast(typeof(return))this : null; } + inout(DefaultStatement) isDefaultStatement() { return stmt == STMT.Default ? cast(typeof(return))this : null; } + inout(LabelStatement) isLabelStatement() { return stmt == STMT.Label ? cast(typeof(return))this : null; } + inout(GotoStatement) isGotoStatement() { return stmt == STMT.Goto ? cast(typeof(return))this : null; } + inout(GotoDefaultStatement) isGotoDefaultStatement() { return stmt == STMT.GotoDefault ? cast(typeof(return))this : null; } + inout(GotoCaseStatement) isGotoCaseStatement() { return stmt == STMT.GotoCase ? cast(typeof(return))this : null; } + inout(BreakStatement) isBreakStatement() { return stmt == STMT.Break ? cast(typeof(return))this : null; } + inout(DtorExpStatement) isDtorExpStatement() { return stmt == STMT.DtorExp ? cast(typeof(return))this : null; } + inout(MixinStatement) isMixinStatement() { return stmt == STMT.Mixin ? cast(typeof(return))this : null; } + inout(ForwardingStatement) isForwardingStatement() { return stmt == STMT.Forwarding ? cast(typeof(return))this : null; } + inout(DoStatement) isDoStatement() { return stmt == STMT.Do ? cast(typeof(return))this : null; } + inout(WhileStatement) isWhileStatement() { return stmt == STMT.While ? cast(typeof(return))this : null; } + inout(ForStatement) isForStatement() { return stmt == STMT.For ? cast(typeof(return))this : null; } + inout(ForeachStatement) isForeachStatement() { return stmt == STMT.Foreach ? cast(typeof(return))this : null; } + inout(SwitchStatement) isSwitchStatement() { return stmt == STMT.Switch ? cast(typeof(return))this : null; } + inout(ContinueStatement) isContinueStatement() { return stmt == STMT.Continue ? cast(typeof(return))this : null; } + inout(WithStatement) isWithStatement() { return stmt == STMT.With ? cast(typeof(return))this : null; } + inout(TryCatchStatement) isTryCatchStatement() { return stmt == STMT.TryCatch ? cast(typeof(return))this : null; } + inout(ThrowStatement) isThrowStatement() { return stmt == STMT.Throw ? cast(typeof(return))this : null; } + inout(DebugStatement) isDebugStatement() { return stmt == STMT.Debug ? cast(typeof(return))this : null; } + inout(TryFinallyStatement) isTryFinallyStatement() { return stmt == STMT.TryFinally ? cast(typeof(return))this : null; } + inout(ScopeGuardStatement) isScopeGuardStatement() { return stmt == STMT.ScopeGuard ? cast(typeof(return))this : null; } + inout(SwitchErrorStatement) isSwitchErrorStatement() { return stmt == STMT.SwitchError ? cast(typeof(return))this : null; } + inout(UnrolledLoopStatement) isUnrolledLoopStatement() { return stmt == STMT.UnrolledLoop ? cast(typeof(return))this : null; } + inout(ForeachRangeStatement) isForeachRangeStatement() { return stmt == STMT.ForeachRange ? cast(typeof(return))this : null; } + inout(CompoundDeclarationStatement) isCompoundDeclarationStatement() { return stmt == STMT.CompoundDeclaration ? cast(typeof(return))this : null; } + inout(CompoundAsmStatement) isCompoundAsmStatement() { return stmt == STMT.CompoundAsm ? cast(typeof(return))this : null; } + inout(PragmaStatement) isPragmaStatement() { return stmt == STMT.Pragma ? cast(typeof(return))this : null; } + inout(StaticAssertStatement) isStaticAssertStatement() { return stmt == STMT.StaticAssert ? cast(typeof(return))this : null; } + inout(CaseRangeStatement) isCaseRangeStatement() { return stmt == STMT.CaseRange ? cast(typeof(return))this : null; } + inout(SynchronizedStatement) isSynchronizedStatement() { return stmt == STMT.Synchronized ? cast(typeof(return))this : null; } + inout(AsmStatement) isAsmStatement() { return stmt == STMT.Asm ? cast(typeof(return))this : null; } + inout(InlineAsmStatement) isInlineAsmStatement() { return stmt == STMT.InlineAsm ? cast(typeof(return))this : null; } + inout(GccAsmStatement) isGccAsmStatement() { return stmt == STMT.GccAsm ? cast(typeof(return))this : null; } + inout(ImportStatement) isImportStatement() { return stmt == STMT.Import ? cast(typeof(return))this : null; } + } } /*********************************************************** @@ -371,25 +371,25 @@ extern (C++) class ExpStatement : Statement { Expression exp; - final extern (D) this(const ref Loc loc, Expression exp) @safe + final extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.Exp); this.exp = exp; } - final extern (D) this(const ref Loc loc, Expression exp, STMT stmt) @safe + final extern (D) this(Loc loc, Expression exp, STMT stmt) @safe { super(loc, stmt); this.exp = exp; } - final extern (D) this(const ref Loc loc, Dsymbol declaration) @safe + final extern (D) this(Loc loc, Dsymbol declaration) @safe { super(loc, STMT.Exp); this.exp = new DeclarationExp(loc, declaration); } - static ExpStatement create(const ref Loc loc, Expression exp) @safe + static ExpStatement create(Loc loc, Expression exp) @safe { return new ExpStatement(loc, exp); } @@ -412,7 +412,7 @@ extern (C++) final class DtorExpStatement : ExpStatement // Wraps an expression that is the destruction of 'var' VarDeclaration var; - extern (D) this(const ref Loc loc, Expression exp, VarDeclaration var) @safe + extern (D) this(Loc loc, Expression exp, VarDeclaration var) @safe { super(loc, exp, STMT.DtorExp); this.var = var; @@ -437,14 +437,14 @@ extern (C++) final class MixinStatement : Statement { Expressions* exps; - extern (D) this(const ref Loc loc, Expression exp) + extern (D) this(Loc loc, Expression exp) { Expressions* exps = new Expressions(); exps.push(exp); this(loc, exps); } - extern (D) this(const ref Loc loc, Expressions* exps) @safe + extern (D) this(Loc loc, Expressions* exps) @safe { super(loc, STMT.Mixin); this.exps = exps; @@ -475,13 +475,13 @@ extern (C++) class CompoundStatement : Statement * loc = Instantiation information * statements = An array of `Statement`s, that will referenced by this class */ - final extern (D) this(const ref Loc loc, Statements* statements) @safe + final extern (D) this(Loc loc, Statements* statements) @safe { super(loc, STMT.Compound); this.statements = statements; } - final extern (D) this(const ref Loc loc, Statements* statements, STMT stmt) @safe + final extern (D) this(Loc loc, Statements* statements, STMT stmt) @safe { super(loc, stmt); this.statements = statements; @@ -495,7 +495,7 @@ extern (C++) class CompoundStatement : Statement * sts = A variadic array of `Statement`s, that will copied in this class * The entries themselves will not be copied. */ - final extern (D) this(const ref Loc loc, Statement[] sts...) + final extern (D) this(Loc loc, Statement[] sts...) { super(loc, STMT.Compound); statements = new Statements(); @@ -504,7 +504,7 @@ extern (C++) class CompoundStatement : Statement statements.push(s); } - static CompoundStatement create(const ref Loc loc, Statement s1, Statement s2) + static CompoundStatement create(Loc loc, Statement s1, Statement s2) { return new CompoundStatement(loc, s1, s2); } @@ -553,7 +553,7 @@ extern (C++) class CompoundStatement : Statement */ extern (C++) final class CompoundDeclarationStatement : CompoundStatement { - extern (D) this(const ref Loc loc, Statements* statements) @safe + extern (D) this(Loc loc, Statements* statements) @safe { super(loc, statements, STMT.CompoundDeclaration); } @@ -577,7 +577,7 @@ extern (C++) final class UnrolledLoopStatement : Statement { Statements* statements; - extern (D) this(const ref Loc loc, Statements* statements) @safe + extern (D) this(Loc loc, Statements* statements) @safe { super(loc, STMT.UnrolledLoop); this.statements = statements; @@ -611,7 +611,7 @@ extern (C++) final class ScopeStatement : Statement Statement statement; Loc endloc; // location of closing curly bracket - extern (D) this(const ref Loc loc, Statement statement, Loc endloc) @safe + extern (D) this(Loc loc, Statement statement, Loc endloc) @safe { super(loc, STMT.Scope); this.statement = statement; @@ -661,7 +661,7 @@ extern (C++) final class ForwardingStatement : Statement /// The wrapped statement. Statement statement; - extern (D) this(const ref Loc loc, ForwardingScopeDsymbol sym, Statement statement) @safe + extern (D) this(Loc loc, ForwardingScopeDsymbol sym, Statement statement) @safe { super(loc, STMT.Forwarding); this.sym = sym; @@ -669,7 +669,7 @@ extern (C++) final class ForwardingStatement : Statement this.statement = statement; } - extern (D) this(const ref Loc loc, Statement statement) @safe + extern (D) this(Loc loc, Statement statement) @safe { auto sym = new ForwardingScopeDsymbol(); sym.symtab = new DsymbolTable(); @@ -698,7 +698,7 @@ extern (C++) final class WhileStatement : Statement Statement _body; Loc endloc; // location of closing curly bracket - extern (D) this(const ref Loc loc, Expression condition, Statement _body, Loc endloc, Parameter param = null) @safe + extern (D) this(Loc loc, Expression condition, Statement _body, Loc endloc, Parameter param = null) @safe { super(loc, STMT.While); this.condition = condition; @@ -740,7 +740,7 @@ extern (C++) final class DoStatement : Statement Expression condition; Loc endloc; // location of ';' after while - extern (D) this(const ref Loc loc, Statement _body, Expression condition, Loc endloc) @safe + extern (D) this(Loc loc, Statement _body, Expression condition, Loc endloc) @safe { super(loc, STMT.Do); this._body = _body; @@ -788,7 +788,7 @@ extern (C++) final class ForStatement : Statement // treat that label as referring to this loop. Statement relatedLabeled; - extern (D) this(const ref Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, Statement _init, Expression condition, Expression increment, Statement _body, Loc endloc) @safe { super(loc, STMT.For); this._init = _init; @@ -849,7 +849,7 @@ extern (C++) final class ForeachStatement : Statement Statements* cases; // put breaks, continues, gotos and returns here ScopeStatements* gotos; // forward referenced goto's go here - extern (D) this(const ref Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, TOK op, Parameters* parameters, Expression aggr, Statement _body, Loc endloc) @safe { super(loc, STMT.Foreach); this.op = op; @@ -890,7 +890,7 @@ extern (C++) final class ForeachStatement : Statement extern (C++) final class ForeachRangeStatement : Statement { TOK op; // TOK.foreach_ or TOK.foreach_reverse_ - Parameter prm; // loop index variable + Parameter param; // loop index variable Expression lwr; Expression upr; Statement _body; @@ -898,11 +898,11 @@ extern (C++) final class ForeachRangeStatement : Statement VarDeclaration key; - extern (D) this(const ref Loc loc, TOK op, Parameter prm, Expression lwr, Expression upr, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, TOK op, Parameter param, Expression lwr, Expression upr, Statement _body, Loc endloc) @safe { super(loc, STMT.ForeachRange); this.op = op; - this.prm = prm; + this.param = param; this.lwr = lwr; this.upr = upr; this._body = _body; @@ -911,7 +911,7 @@ extern (C++) final class ForeachRangeStatement : Statement override ForeachRangeStatement syntaxCopy() { - return new ForeachRangeStatement(loc, op, prm.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); + return new ForeachRangeStatement(loc, op, param.syntaxCopy(), lwr.syntaxCopy(), upr.syntaxCopy(), _body ? _body.syntaxCopy() : null, endloc); } override bool hasBreak() const pure nothrow @@ -935,17 +935,17 @@ extern (C++) final class ForeachRangeStatement : Statement */ extern (C++) final class IfStatement : Statement { - Parameter prm; + Parameter param; Expression condition; Statement ifbody; Statement elsebody; VarDeclaration match; // for MatchExpression results Loc endloc; // location of closing curly bracket - extern (D) this(const ref Loc loc, Parameter prm, Expression condition, Statement ifbody, Statement elsebody, Loc endloc) @safe + extern (D) this(Loc loc, Parameter param, Expression condition, Statement ifbody, Statement elsebody, Loc endloc) @safe { super(loc, STMT.If); - this.prm = prm; + this.param = param; this.condition = condition; this.ifbody = ifbody; this.elsebody = elsebody; @@ -955,7 +955,7 @@ extern (C++) final class IfStatement : Statement override IfStatement syntaxCopy() { return new IfStatement(loc, - prm ? prm.syntaxCopy() : null, + param ? param.syntaxCopy() : null, condition.syntaxCopy(), ifbody ? ifbody.syntaxCopy() : null, elsebody ? elsebody.syntaxCopy() : null, @@ -987,7 +987,7 @@ extern (C++) final class ConditionalStatement : Statement Statement ifbody; Statement elsebody; - extern (D) this(const ref Loc loc, Condition condition, Statement ifbody, Statement elsebody) @safe + extern (D) this(Loc loc, Condition condition, Statement ifbody, Statement elsebody) @safe { super(loc, STMT.Conditional); this.condition = condition; @@ -1022,7 +1022,7 @@ extern (C++) final class StaticForeachStatement : Statement { StaticForeach sfe; - extern (D) this(const ref Loc loc, StaticForeach sfe) @safe + extern (D) this(Loc loc, StaticForeach sfe) @safe { super(loc, STMT.StaticForeach); this.sfe = sfe; @@ -1048,7 +1048,7 @@ extern (C++) final class PragmaStatement : Statement Expressions* args; // array of Expression's Statement _body; - extern (D) this(const ref Loc loc, const Identifier ident, Expressions* args, Statement _body) @safe + extern (D) this(Loc loc, const Identifier ident, Expressions* args, Statement _body) @safe { super(loc, STMT.Pragma); this.ident = ident; @@ -1106,12 +1106,12 @@ extern (C++) final class SwitchStatement : Statement bool hasVars; /// true if has variable case values DefaultStatement sdefault; /// default: Statement tryBody; /// set to TryCatchStatement or TryFinallyStatement if in _body portion - TryFinallyStatement tf; /// set if in the 'finally' block of a TryFinallyStatement + TryFinallyStatement tryFinally; /// set if in the 'finally' block of a TryFinallyStatement GotoCaseStatements gotoCases; /// array of unresolved GotoCaseStatement's CaseStatements* cases; /// array of CaseStatement's VarDeclaration lastVar; /// last observed variable declaration in this statement - extern (D) this(const ref Loc loc, Parameter param, Expression condition, Statement _body, bool isFinal, Loc endloc) + extern (D) this(Loc loc, Parameter param, Expression condition, Statement _body, bool isFinal, Loc endloc) { super(loc, STMT.Switch); this.param = param; @@ -1154,7 +1154,7 @@ extern (C++) final class CaseStatement : Statement VarDeclaration lastVar; void* extra; // for use by Statement_toIR() - extern (D) this(const ref Loc loc, Expression exp, Statement statement) @safe + extern (D) this(Loc loc, Expression exp, Statement statement) @safe { super(loc, STMT.Case); this.exp = exp; @@ -1181,7 +1181,7 @@ extern (C++) final class CaseRangeStatement : Statement Expression last; Statement statement; - extern (D) this(const ref Loc loc, Expression first, Expression last, Statement statement) @safe + extern (D) this(Loc loc, Expression first, Expression last, Statement statement) @safe { super(loc, STMT.CaseRange); this.first = first; @@ -1209,7 +1209,7 @@ extern (C++) final class DefaultStatement : Statement VarDeclaration lastVar; - extern (D) this(const ref Loc loc, Statement statement) @safe + extern (D) this(Loc loc, Statement statement) @safe { super(loc, STMT.Default); this.statement = statement; @@ -1233,7 +1233,7 @@ extern (C++) final class GotoDefaultStatement : Statement { SwitchStatement sw; - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, STMT.GotoDefault); } @@ -1258,7 +1258,7 @@ extern (C++) final class GotoCaseStatement : Statement CaseStatement cs; // case statement it resolves to - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.GotoCase); this.exp = exp; @@ -1281,12 +1281,12 @@ extern (C++) final class SwitchErrorStatement : Statement { Expression exp; - extern (D) this(const ref Loc loc) @safe + extern (D) this(Loc loc) @safe { super(loc, STMT.SwitchError); } - final extern (D) this(const ref Loc loc, Expression exp) @safe + final extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.SwitchError); this.exp = exp; @@ -1306,7 +1306,7 @@ extern (C++) final class ReturnStatement : Statement Expression exp; size_t caseDim; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.Return); this.exp = exp; @@ -1335,7 +1335,7 @@ extern (C++) final class BreakStatement : Statement { Identifier ident; - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, STMT.Break); this.ident = ident; @@ -1359,7 +1359,7 @@ extern (C++) final class ContinueStatement : Statement { Identifier ident; - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, STMT.Continue); this.ident = ident; @@ -1384,7 +1384,7 @@ extern (C++) final class SynchronizedStatement : Statement Expression exp; Statement _body; - extern (D) this(const ref Loc loc, Expression exp, Statement _body) @safe + extern (D) this(Loc loc, Expression exp, Statement _body) @safe { super(loc, STMT.Synchronized); this.exp = exp; @@ -1422,7 +1422,7 @@ extern (C++) final class WithStatement : Statement VarDeclaration wthis; Loc endloc; - extern (D) this(const ref Loc loc, Expression exp, Statement _body, Loc endloc) @safe + extern (D) this(Loc loc, Expression exp, Statement _body, Loc endloc) @safe { super(loc, STMT.With); this.exp = exp; @@ -1451,7 +1451,7 @@ extern (C++) final class TryCatchStatement : Statement Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion - extern (D) this(const ref Loc loc, Statement _body, Catches* catches) @safe + extern (D) this(Loc loc, Statement _body, Catches* catches) @safe { super(loc, STMT.TryCatch); this._body = _body; @@ -1495,7 +1495,7 @@ extern (C++) final class Catch : RootObject // was generated by the compiler, wasn't present in source code bool internalCatch; - extern (D) this(const ref Loc loc, Type type, Identifier ident, Statement handler) @safe + extern (D) this(Loc loc, Type type, Identifier ident, Statement handler) @safe { //printf("Catch(%s, loc = %s)\n", id.toChars(), loc.toChars()); this.loc = loc; @@ -1523,7 +1523,7 @@ extern (C++) final class TryFinallyStatement : Statement Statement tryBody; /// set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion bool bodyFallsThru; /// true if _body falls through to finally - extern (D) this(const ref Loc loc, Statement _body, Statement finalbody) @safe + extern (D) this(Loc loc, Statement _body, Statement finalbody) @safe { super(loc, STMT.TryFinally); this._body = _body; @@ -1531,7 +1531,7 @@ extern (C++) final class TryFinallyStatement : Statement this.bodyFallsThru = true; // assume true until statementSemantic() } - static TryFinallyStatement create(const ref Loc loc, Statement _body, Statement finalbody) @safe + static TryFinallyStatement create(Loc loc, Statement _body, Statement finalbody) @safe { return new TryFinallyStatement(loc, _body, finalbody); } @@ -1565,7 +1565,7 @@ extern (C++) final class ScopeGuardStatement : Statement TOK tok; Statement statement; - extern (D) this(const ref Loc loc, TOK tok, Statement statement) @safe + extern (D) this(Loc loc, TOK tok, Statement statement) @safe { super(loc, STMT.ScopeGuard); this.tok = tok; @@ -1593,7 +1593,7 @@ extern (C++) final class ThrowStatement : Statement // was generated by the compiler, wasn't present in source code bool internalThrow; - extern (D) this(const ref Loc loc, Expression exp) @safe + extern (D) this(Loc loc, Expression exp) @safe { super(loc, STMT.Throw); this.exp = exp; @@ -1618,7 +1618,7 @@ extern (C++) final class DebugStatement : Statement { Statement statement; - extern (D) this(const ref Loc loc, Statement statement) @safe + extern (D) this(Loc loc, Statement statement) @safe { super(loc, STMT.Debug); this.statement = statement; @@ -1648,7 +1648,7 @@ extern (C++) final class GotoStatement : Statement VarDeclaration lastVar; bool inCtfeBlock; /// set if goto is inside an `if (__ctfe)` block - extern (D) this(const ref Loc loc, Identifier ident) @safe + extern (D) this(Loc loc, Identifier ident) @safe { super(loc, STMT.Goto); this.ident = ident; @@ -1682,7 +1682,7 @@ extern (C++) final class LabelStatement : Statement bool breaks; // someone did a 'break ident' bool inCtfeBlock; // inside a block dominated by `if (__ctfe)` - extern (D) this(const ref Loc loc, Identifier ident, Statement statement) @safe + extern (D) this(Loc loc, Identifier ident, Statement statement) @safe { super(loc, STMT.Label); this.ident = ident; @@ -1713,9 +1713,9 @@ extern (C++) final class LabelDsymbol : Dsymbol // can be removed if generic error message deduplication is implemented bool duplicated; - extern (D) this(Identifier ident, const ref Loc loc = Loc.initial) @safe + extern (D) this(Identifier ident, Loc loc = Loc.initial) @safe { - super(loc, ident); + super(DSYM.labelDsymbol, loc, ident); } static LabelDsymbol create(Identifier ident) @safe @@ -1743,13 +1743,13 @@ extern (C++) class AsmStatement : Statement Token* tokens; bool caseSensitive; // for register names - extern (D) this(const ref Loc loc, Token* tokens) @safe + extern (D) this(Loc loc, Token* tokens) @safe { super(loc, STMT.Asm); this.tokens = tokens; } - extern (D) this(const ref Loc loc, Token* tokens, STMT stmt) @safe + extern (D) this(Loc loc, Token* tokens, STMT stmt) @safe { super(loc, stmt); this.tokens = tokens; @@ -1777,7 +1777,7 @@ extern (C++) final class InlineAsmStatement : AsmStatement bool refparam; // true if function parameter is referenced bool naked; // true if function is to be naked - extern (D) this(const ref Loc loc, Token* tokens) @safe + extern (D) this(Loc loc, Token* tokens) @safe { super(loc, tokens, STMT.InlineAsm); } @@ -1799,7 +1799,7 @@ extern (C++) final class InlineAsmStatement : AsmStatement */ extern (C++) final class GccAsmStatement : AsmStatement { - StorageClass stc; // attributes of the asm {} block + STC stc; // attributes of the asm {} block Expression insn; // string expression that is the template for assembler code Expressions* args; // input and output operands of the statement uint outputargs; // of the operands in 'args', the number of output operands @@ -1809,7 +1809,7 @@ extern (C++) final class GccAsmStatement : AsmStatement Identifiers* labels; // list of goto labels GotoStatements* gotos; // of the goto labels, the equivalent statements they represent - extern (D) this(const ref Loc loc, Token* tokens) @safe + extern (D) this(Loc loc, Token* tokens) @safe { super(loc, tokens, STMT.GccAsm); } @@ -1830,9 +1830,9 @@ extern (C++) final class GccAsmStatement : AsmStatement */ extern (C++) final class CompoundAsmStatement : CompoundStatement { - StorageClass stc; // postfix attributes like nothrow/pure/@trusted + STC stc; // postfix attributes like nothrow/pure/@trusted - extern (D) this(const ref Loc loc, Statements* statements, StorageClass stc) @safe + extern (D) this(Loc loc, Statements* statements, STC stc) @safe { super(loc, statements, STMT.CompoundAsm); this.stc = stc; @@ -1856,7 +1856,7 @@ extern (C++) final class ImportStatement : Statement { Dsymbols* imports; // Array of Import's - extern (D) this(const ref Loc loc, Dsymbols* imports) @safe + extern (D) this(Loc loc, Dsymbols* imports) @safe { super(loc, STMT.Import); this.imports = imports; diff --git a/gcc/d/dmd/statement.h b/gcc/d/dmd/statement.h index ea80e51..5f962b2 100644 --- a/gcc/d/dmd/statement.h +++ b/gcc/d/dmd/statement.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -119,40 +119,40 @@ public: bool hasCode(); virtual Statement *last(); - virtual ReturnStatement *endsWithReturnStatement() { return NULL; } - - ErrorStatement *isErrorStatement() { return stmt == STMTerror ? (ErrorStatement*)this : NULL; } - ScopeStatement *isScopeStatement() { return stmt == STMTscope ? (ScopeStatement*)this : NULL; } - ExpStatement *isExpStatement() { return stmt == STMTexp ? (ExpStatement*)this : NULL; } - CompoundStatement *isCompoundStatement() { return stmt == STMTcompound ? (CompoundStatement*)this : NULL; } - ReturnStatement *isReturnStatement() { return stmt == STMTreturn ? (ReturnStatement*)this : NULL; } - IfStatement *isIfStatement() { return stmt == STMTif ? (IfStatement*)this : NULL; } - ConditionalStatement *isConditionalStatement() { return stmt == STMTconditional ? (ConditionalStatement*)this : NULL; } - StaticForeachStatement *isStaticForeachStatement() { return stmt == STMTstaticForeach ? (StaticForeachStatement*)this : NULL; } - CaseStatement *isCaseStatement() { return stmt == STMTcase ? (CaseStatement*)this : NULL; } - DefaultStatement *isDefaultStatement() { return stmt == STMTdefault ? (DefaultStatement*)this : NULL; } - LabelStatement *isLabelStatement() { return stmt == STMTlabel ? (LabelStatement*)this : NULL; } - GotoDefaultStatement *isGotoDefaultStatement() { return stmt == STMTgotoDefault ? (GotoDefaultStatement*)this : NULL; } - GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : NULL; } - BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : NULL; } - DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : NULL; } - MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : NULL; } - ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : NULL; } - DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : NULL; } - ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : NULL; } - ForeachStatement *isForeachStatement() { return stmt == STMTforeach ? (ForeachStatement*)this : NULL; } - SwitchStatement *isSwitchStatement() { return stmt == STMTswitch ? (SwitchStatement*)this : NULL; } - ContinueStatement *isContinueStatement() { return stmt == STMTcontinue ? (ContinueStatement*)this : NULL; } - WithStatement *isWithStatement() { return stmt == STMTwith ? (WithStatement*)this : NULL; } - TryCatchStatement *isTryCatchStatement() { return stmt == STMTtryCatch ? (TryCatchStatement*)this : NULL; } - ThrowStatement *isThrowStatement() { return stmt == STMTthrow ? (ThrowStatement*)this : NULL; } - DebugStatement *isDebugStatement() { return stmt == STMTdebug ? (DebugStatement*)this : NULL; } - TryFinallyStatement *isTryFinallyStatement() { return stmt == STMTtryFinally ? (TryFinallyStatement*)this : NULL; } - ScopeGuardStatement *isScopeGuardStatement() { return stmt == STMTscopeGuard ? (ScopeGuardStatement*)this : NULL; } - SwitchErrorStatement *isSwitchErrorStatement() { return stmt == STMTswitchError ? (SwitchErrorStatement*)this : NULL; } - UnrolledLoopStatement *isUnrolledLoopStatement() { return stmt == STMTunrolledLoop ? (UnrolledLoopStatement*)this : NULL; } - ForeachRangeStatement *isForeachRangeStatement() { return stmt == STMTforeachRange ? (ForeachRangeStatement*)this : NULL; } - CompoundDeclarationStatement *isCompoundDeclarationStatement() { return stmt == STMTcompoundDeclaration ? (CompoundDeclarationStatement*)this : NULL; } + virtual ReturnStatement *endsWithReturnStatement() { return nullptr; } + + ErrorStatement *isErrorStatement() { return stmt == STMTerror ? (ErrorStatement*)this : nullptr; } + ScopeStatement *isScopeStatement() { return stmt == STMTscope ? (ScopeStatement*)this : nullptr; } + ExpStatement *isExpStatement() { return stmt == STMTexp ? (ExpStatement*)this : nullptr; } + CompoundStatement *isCompoundStatement() { return stmt == STMTcompound ? (CompoundStatement*)this : nullptr; } + ReturnStatement *isReturnStatement() { return stmt == STMTreturn ? (ReturnStatement*)this : nullptr; } + IfStatement *isIfStatement() { return stmt == STMTif ? (IfStatement*)this : nullptr; } + ConditionalStatement *isConditionalStatement() { return stmt == STMTconditional ? (ConditionalStatement*)this : nullptr; } + StaticForeachStatement *isStaticForeachStatement() { return stmt == STMTstaticForeach ? (StaticForeachStatement*)this : nullptr; } + CaseStatement *isCaseStatement() { return stmt == STMTcase ? (CaseStatement*)this : nullptr; } + DefaultStatement *isDefaultStatement() { return stmt == STMTdefault ? (DefaultStatement*)this : nullptr; } + LabelStatement *isLabelStatement() { return stmt == STMTlabel ? (LabelStatement*)this : nullptr; } + GotoDefaultStatement *isGotoDefaultStatement() { return stmt == STMTgotoDefault ? (GotoDefaultStatement*)this : nullptr; } + GotoCaseStatement *isGotoCaseStatement() { return stmt == STMTgotoCase ? (GotoCaseStatement*)this : nullptr; } + BreakStatement *isBreakStatement() { return stmt == STMTbreak ? (BreakStatement*)this : nullptr; } + DtorExpStatement *isDtorExpStatement() { return stmt == STMTdtorExp ? (DtorExpStatement*)this : nullptr; } + MixinStatement *isMixinStatement() { return stmt == STMTmixin ? (MixinStatement*)this : nullptr; } + ForwardingStatement *isForwardingStatement() { return stmt == STMTforwarding ? (ForwardingStatement*)this : nullptr; } + DoStatement *isDoStatement() { return stmt == STMTdo ? (DoStatement*)this : nullptr; } + ForStatement *isForStatement() { return stmt == STMTfor ? (ForStatement*)this : nullptr; } + ForeachStatement *isForeachStatement() { return stmt == STMTforeach ? (ForeachStatement*)this : nullptr; } + SwitchStatement *isSwitchStatement() { return stmt == STMTswitch ? (SwitchStatement*)this : nullptr; } + ContinueStatement *isContinueStatement() { return stmt == STMTcontinue ? (ContinueStatement*)this : nullptr; } + WithStatement *isWithStatement() { return stmt == STMTwith ? (WithStatement*)this : nullptr; } + TryCatchStatement *isTryCatchStatement() { return stmt == STMTtryCatch ? (TryCatchStatement*)this : nullptr; } + ThrowStatement *isThrowStatement() { return stmt == STMTthrow ? (ThrowStatement*)this : nullptr; } + DebugStatement *isDebugStatement() { return stmt == STMTdebug ? (DebugStatement*)this : nullptr; } + TryFinallyStatement *isTryFinallyStatement() { return stmt == STMTtryFinally ? (TryFinallyStatement*)this : nullptr; } + ScopeGuardStatement *isScopeGuardStatement() { return stmt == STMTscopeGuard ? (ScopeGuardStatement*)this : nullptr; } + SwitchErrorStatement *isSwitchErrorStatement() { return stmt == STMTswitchError ? (SwitchErrorStatement*)this : nullptr; } + UnrolledLoopStatement *isUnrolledLoopStatement() { return stmt == STMTunrolledLoop ? (UnrolledLoopStatement*)this : nullptr; } + ForeachRangeStatement *isForeachRangeStatement() { return stmt == STMTforeachRange ? (ForeachRangeStatement*)this : nullptr; } + CompoundDeclarationStatement *isCompoundDeclarationStatement() { return stmt == STMTcompoundDeclaration ? (CompoundDeclarationStatement*)this : nullptr; } void accept(Visitor *v) override { v->visit(this); } }; @@ -181,7 +181,7 @@ class ExpStatement : public Statement public: Expression *exp; - static ExpStatement *create(const Loc &loc, Expression *exp); + static ExpStatement *create(Loc loc, Expression *exp); ExpStatement *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -213,7 +213,7 @@ class CompoundStatement : public Statement public: Statements *statements; - static CompoundStatement *create(const Loc &loc, Statement *s1, Statement *s2); + static CompoundStatement *create(Loc loc, Statement *s1, Statement *s2); CompoundStatement *syntaxCopy() override; ReturnStatement *endsWithReturnStatement() override final; Statement *last() override final; @@ -346,7 +346,7 @@ class ForeachRangeStatement final : public Statement { public: TOK op; // TOKforeach or TOKforeach_reverse - Parameter *prm; // loop index variable + Parameter *param; // loop index variable Expression *lwr; Expression *upr; Statement *_body; @@ -364,7 +364,7 @@ public: class IfStatement final : public Statement { public: - Parameter *prm; + Parameter *param; Expression *condition; Statement *ifbody; Statement *elsebody; @@ -613,7 +613,7 @@ public: Statement *tryBody; // set to enclosing TryCatchStatement or TryFinallyStatement if in _body portion d_bool bodyFallsThru; // true if _body falls through to finally - static TryFinallyStatement *create(const Loc &loc, Statement *body, Statement *finalbody); + static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody); TryFinallyStatement *syntaxCopy() override; bool hasBreak() const override; bool hasContinue() const override; diff --git a/gcc/d/dmd/statementsem.d b/gcc/d/dmd/statementsem.d index 1bf36e3..0c9b6be 100644 --- a/gcc/d/dmd/statementsem.d +++ b/gcc/d/dmd/statementsem.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/statement.html, Statements) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d, _statementsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/statementsem.d, _statementsem.d) * Documentation: https://dlang.org/phobos/dmd_statementsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statementsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/statementsem.d */ module dmd.statementsem; @@ -57,6 +57,7 @@ import dmd.opover; import dmd.parse; import dmd.common.outbuffer; import dmd.root.string; +import dmd.safe : isSafe, isSaferD, setUnsafe; import dmd.semantic2; import dmd.sideeffect; import dmd.statement; @@ -82,13 +83,13 @@ version (DMDLIB) */ private Identifier fixupLabelName(Scope* sc, Identifier ident) { - uint flags = (sc.flags & SCOPE.contract); + Contract c = sc.contract; const id = ident.toString(); - if (flags && flags != SCOPE.invariant_ && + if (c != Contract.none && c != Contract.invariant_ && !(id.length >= 2 && id[0] == '_' && id[1] == '_')) // does not start with "__" { OutBuffer buf; - buf.writestring(flags == SCOPE.require ? "__in_" : "__out_"); + buf.writestring(c == Contract.require ? "__in_" : "__out_"); buf.writestring(ident.toString()); ident = Identifier.idPool(buf[]); @@ -123,7 +124,7 @@ private LabelStatement checkLabeledLoop(Scope* sc, Statement statement) @safe */ private Expression checkAssignmentAsCondition(Expression e, Scope* sc) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return e; auto ec = lastComma(e); if (ec.op == EXP.assign) @@ -134,7 +135,16 @@ private Expression checkAssignmentAsCondition(Expression e, Scope* sc) return e; } -// Performs semantic analysis in Statement AST nodes +/** + * Performs semantic analysis in Statement AST nodes + * + * Params: + * s = statement to perform semantic analysis on + * sc = scope in which statement resides + * + * Returns: statement `s` after semantic analysis. + * Can be `null`, for example with `pragma(msg, "")` + */ Statement statementSemantic(Statement s, Scope* sc) { import dmd.compiler; @@ -205,7 +215,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } if (checkMustUse(s.exp, sc)) s.exp = ErrorExp.get(); - if (!(sc.flags & SCOPE.Cfile) && discardValue(s.exp)) + if (!sc.inCfile && discardValue(s.exp)) s.exp = ErrorExp.get(); s.exp = s.exp.optimize(WANTvalue); @@ -663,7 +673,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) const olderrors = global.startGagging(); discardValue(fs.increment); if (global.endGagging(olderrors)) - deprecation(fs.increment.loc, "`%s` has no effect", fs.increment.toChars()); + deprecation(fs.increment.loc, "`%s` has no effect", fs.increment.toErrMsg()); if (checkNonAssignmentArrayOp(fs.increment)) fs.increment = ErrorExp.get(); fs.increment = fs.increment.optimize(WANTvalue); @@ -778,14 +788,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Dsymbol sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors - /* Check for inference errors - */ + /* Check for inference errors and apply mutability checks inline */ if (!inferApplyArgTypes(fs, sc, sapply)) { - /** - Try and extract the parameter count of the opApply callback function, e.g.: - int opApply(int delegate(int, float)) => 2 args - */ bool foundMismatch = false; size_t foreachParamCount = 0; if (sapplyOld) @@ -805,6 +810,19 @@ Statement statementSemanticVisit(Statement s, Scope* sc) auto tf = fparam.type.nextOf().isTypeFunction(); foreachParamCount = tf.parameterList.length; foundMismatch = true; + + // Mutability check + if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst()) + { + // First error: The call site + error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object", + fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars()); + + // Second error: Suggest how to fix + errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + + return setError(); + } } } } @@ -823,6 +841,24 @@ Statement statementSemanticVisit(Statement s, Scope* sc) return setError(); } + // If inference succeeds, proceed with post-checks + if (sapply && sapply.isFuncDeclaration()) + { + FuncDeclaration fd = sapply.isFuncDeclaration(); + + if (fs.aggr && fs.aggr.type && fd.type && fs.aggr.type.isConst() && !fd.type.isConst()) + { + // First error: The call site + error(fs.loc, "mutable method `%s.%s` is not callable using a `const` object", + fd.parent ? fd.parent.toPrettyChars() : "unknown", fd.toChars()); + + // Second error: Suggest how to fix + errorSupplemental(fd.loc, "Consider adding `const` or `inout` here"); + + return setError(); + } + } + Type tab = fs.aggr.type.toBasetype(); if (tab.ty == Ttuple) // don't generate new scope for tuple loops @@ -975,7 +1011,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (dim == 2) { Type tindex = (*fs.parameters)[0].type; - if (!tindex.isintegral()) + if (!tindex.isIntegral()) { error(fs.loc, "foreach: key cannot be of non-integral type `%s`", tindex.toChars()); return retError(); @@ -989,7 +1025,18 @@ Statement statementSemanticVisit(Statement s, Scope* sc) (tn.ty != tv.ty && tn.ty.isSomeChar && tv.ty.isSomeChar)) && !Type.tsize_t.implicitConvTo(tindex)) { - deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`", + bool err = true; + if (tab.isTypeDArray()) + { + // check if overflow is possible + const maxLen = IntRange.fromType(tindex).imax.value + 1; + if (auto ale = fs.aggr.isArrayLiteralExp()) + err = ale.elements.length > maxLen; + else if (auto se = fs.aggr.isSliceExp()) + err = !(se.upr && se.upr.isConst() && se.upr.toInteger() <= maxLen); + } + if (err) + deprecation(fs.loc, "foreach: loop index implicitly converted from `size_t` to `%s`", tindex.toChars()); } } @@ -1253,7 +1300,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else { - r = copyToTemp(0, "__r", fs.aggr); + r = copyToTemp(STC.none, "__r", fs.aggr); r.dsymbolSemantic(sc); _init = new ExpStatement(loc, r); if (vinit) @@ -1298,7 +1345,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (auto ftt = tfront.toBasetype().isTypeFunction()) { tfront = tfront.toBasetype().nextOf(); - if (!ftt.isref) + if (!ftt.isRef) { // .front() does not return a ref. We ignore ref on foreach arg. // see https://issues.dlang.org/show_bug.cgi?id=11934 @@ -1370,7 +1417,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) p.type = p.type.addStorageClass(sc).typeSemantic(loc, sc2); if (!exp.implicitConvTo(p.type)) { - error(fs.loc, "cannot implicilty convert range element of type `%s` to variable `%s` of type `%s`", + error(fs.loc, "cannot implicitly convert tuple element of type `%s` to variable `%s` of type `%s`", exp.type.toChars(), p.toChars(), p.type.toChars()); return retError(); } @@ -1398,7 +1445,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } case Tdelegate: if (fs.op == TOK.foreach_reverse_) - deprecation(fs.loc, "cannot use `foreach_reverse` with a delegate"); + error(fs.loc, "cannot use `foreach_reverse` with a delegate"); return retStmt(apply()); case Terror: return retError(); @@ -1433,25 +1480,25 @@ Statement statementSemanticVisit(Statement s, Scope* sc) return setError(); } - if (fs.prm.type) + if (fs.param.type) { - fs.prm.type = fs.prm.type.typeSemantic(loc, sc); - fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass); - fs.lwr = fs.lwr.implicitCastTo(sc, fs.prm.type); + fs.param.type = fs.param.type.typeSemantic(loc, sc); + fs.param.type = fs.param.type.addStorageClass(fs.param.storageClass); + fs.lwr = fs.lwr.implicitCastTo(sc, fs.param.type); - if (fs.upr.implicitConvTo(fs.prm.type) || (fs.prm.storageClass & STC.ref_)) + if (fs.upr.implicitConvTo(fs.param.type) || (fs.param.storageClass & STC.ref_)) { - fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type); + fs.upr = fs.upr.implicitCastTo(sc, fs.param.type); } else { - // See if upr-1 fits in prm.type + // See if upr-1 fits in param.type Expression limit = new MinExp(loc, fs.upr, IntegerExp.literal!1); limit = limit.expressionSemantic(sc); limit = limit.optimize(WANTvalue); - if (!limit.implicitConvTo(fs.prm.type)) + if (!limit.implicitConvTo(fs.param.type)) { - fs.upr = fs.upr.implicitCastTo(sc, fs.prm.type); + fs.upr = fs.upr.implicitCastTo(sc, fs.param.type); } } } @@ -1464,26 +1511,26 @@ Statement statementSemanticVisit(Statement s, Scope* sc) { /* Just picking the first really isn't good enough. */ - fs.prm.type = fs.lwr.type; + fs.param.type = fs.lwr.type; } else if (fs.lwr.type == fs.upr.type) { /* Same logic as CondExp ?lwr:upr */ - fs.prm.type = fs.lwr.type; + fs.param.type = fs.lwr.type; } else { scope AddExp ea = new AddExp(loc, fs.lwr, fs.upr); if (typeCombine(ea, sc)) return setError(); - fs.prm.type = ea.type; + fs.param.type = ea.type; fs.lwr = ea.e1; fs.upr = ea.e2; } - fs.prm.type = fs.prm.type.addStorageClass(fs.prm.storageClass); + fs.param.type = fs.param.type.addStorageClass(fs.param.storageClass); } - if (fs.prm.type.ty == Terror || fs.lwr.op == EXP.error || fs.upr.op == EXP.error) + if (fs.param.type.ty == Terror || fs.lwr.op == EXP.error || fs.upr.op == EXP.error) { return setError(); } @@ -1528,7 +1575,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (fs.op == TOK.foreach_reverse_) { cond = new PostExp(EXP.minusMinus, loc, new VarExp(loc, fs.key)); - if (fs.prm.type.isscalar()) + if (fs.param.type.isScalar()) { // key-- > tmp cond = new CmpExp(EXP.greaterThan, loc, cond, new VarExp(loc, tmp)); @@ -1541,7 +1588,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else { - if (fs.prm.type.isscalar()) + if (fs.param.type.isScalar()) { // key < tmp cond = new CmpExp(EXP.lessThan, loc, new VarExp(loc, fs.key), new VarExp(loc, tmp)); @@ -1560,30 +1607,30 @@ Statement statementSemanticVisit(Statement s, Scope* sc) //increment = new AddAssignExp(loc, new VarExp(loc, fs.key), IntegerExp.literal!1); increment = new PreExp(EXP.prePlusPlus, loc, new VarExp(loc, fs.key)); } - if ((fs.prm.storageClass & STC.ref_) && fs.prm.type.equals(fs.key.type)) + if ((fs.param.storageClass & STC.ref_) && fs.param.type.equals(fs.key.type)) { fs.key.range = null; - auto v = new AliasDeclaration(loc, fs.prm.ident, fs.key); + auto v = new AliasDeclaration(loc, fs.param.ident, fs.key); fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body); } else { - ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.prm.type)); - auto v = new VarDeclaration(loc, fs.prm.type, fs.prm.ident, ie); - v.storage_class |= STC.temp | STC.foreach_ | (fs.prm.storageClass & STC.ref_); + ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, fs.key), fs.param.type)); + auto v = new VarDeclaration(loc, fs.param.type, fs.param.ident, ie); + v.storage_class |= STC.temp | STC.foreach_ | (fs.param.storageClass & STC.ref_); fs._body = new CompoundStatement(loc, new ExpStatement(loc, v), fs._body); - if (fs.key.range && !fs.prm.type.isMutable()) + if (fs.key.range && !fs.param.type.isMutable()) { /* Limit the range of the key to the specified range */ v.range = new IntRange(fs.key.range.imin, fs.key.range.imax - SignExtendedNumber(1)); } } - if (fs.prm.storageClass & STC.ref_) + if (fs.param.storageClass & STC.ref_) { - if (fs.key.type.constConv(fs.prm.type) == MATCH.nomatch) + if (fs.key.type.constConv(fs.param.type) == MATCH.nomatch) { - error(fs.loc, "argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.prm.type.toChars()); + error(fs.loc, "argument type mismatch, `%s` to `ref %s`", fs.key.type.toChars(), fs.param.type.toChars()); return setError(); } } @@ -1606,15 +1653,15 @@ Statement statementSemanticVisit(Statement s, Scope* sc) sym.parent = sc.scopesym; sym.endlinnum = ifs.endloc.linnum; Scope* scd = sc.push(sym); - if (ifs.prm) + if (ifs.param) { - /* Declare prm, which we will set to be the + /* Declare param, which we will set to be the * result of condition. */ auto ei = new ExpInitializer(ifs.loc, ifs.condition); - ifs.match = new VarDeclaration(ifs.loc, ifs.prm.type, ifs.prm.ident, ei); + ifs.match = new VarDeclaration(ifs.loc, ifs.param.type, ifs.param.ident, ei); ifs.match.parent = scd.func; - ifs.match.storage_class |= ifs.prm.storageClass; + ifs.match.storage_class |= ifs.param.storageClass; ifs.match.dsymbolSemantic(scd); auto de = new DeclarationExp(ifs.loc, ifs.match); @@ -1650,8 +1697,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (checkNonAssignmentArrayOp(ifs.condition)) ifs.condition = ErrorExp.get(); - // Convert to boolean after declaring prm so this works: - // if (S prm = S()) {} + // Convert to boolean after declaring param so this works: + // if (S param = S()) {} // where S is a struct that defines opCast!bool. ifs.condition = ifs.condition.toBoolean(scd); @@ -1686,7 +1733,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (ifs.isIfCtfeBlock()) { Scope* scd2 = scd.push(); - scd2.flags |= SCOPE.ctfeBlock; + scd2.ctfeBlock = true; ifs.ifbody = ifs.ifbody.semanticNoScope(scd2); scd2.pop(); } @@ -1722,11 +1769,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // This feature allows a limited form of conditional compilation. if (cs.condition.include(sc)) { - DebugCondition dc = cs.condition.isDebugCondition(); - if (dc) + if (DebugCondition dc = cs.condition.isDebugCondition()) { sc = sc.push(); - sc.flags |= SCOPE.debug_; + sc.debug_ = true; cs.ifbody = cs.ifbody.statementSemantic(sc); sc.pop(); } @@ -1767,7 +1813,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) //printf("SwitchStatement::semantic(%p)\n", ss); ss.tryBody = sc.tryBody; - ss.tf = sc.tf; + ss.tryFinally = sc.tryFinally; if (ss.cases) { result = ss; // already run @@ -1827,7 +1873,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) break; } ss.condition = integralPromotions(ss.condition, sc); - if (!ss.condition.isErrorExp() && ss.condition.type.isintegral()) + if (!ss.condition.isErrorExp() && ss.condition.type.isIntegral()) break; auto ad = isAggregate(ss.condition.type); @@ -1861,7 +1907,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) sc = sc.push(); sc.sbreak = ss; - sc.sw = ss; + sc.switchStatement = ss; ss.cases = new CaseStatements(); const inLoopSave = sc.inLoop; @@ -1888,9 +1934,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) for (Scope* scx = sc; scx; scx = scx.enclosing) { - if (!scx.sw) + if (!scx.switchStatement) continue; - foreach (cs; *scx.sw.cases) + foreach (cs; *scx.switchStatement.cases) { if (cs.exp.equals(gcs.exp)) { @@ -1949,11 +1995,11 @@ Statement statementSemanticVisit(Statement s, Scope* sc) needswitcherror = true; } - ss.hasDefault = sc.sw.sdefault || - !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe); + ss.hasDefault = sc.switchStatement.sdefault || + !(!ss.isFinal || needswitcherror || global.params.useAssert == CHECKENABLE.on || sc.func.isSafe || sc.func.isSaferD); if (!ss.hasDefault) { - if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !(sc.flags & SCOPE.Cfile)) + if (!ss.isFinal && (!ss._body || !ss._body.isErrorStatement()) && !sc.inCfile) error(ss.loc, "`switch` statement without a `default`; use `final switch` or add `default: assert(0);` or add `default: break;`"); // Generate runtime error if the default is hit @@ -1961,7 +2007,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) CompoundStatement cs; Statement s; - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) { s = new BreakStatement(ss.loc, null); // default for C is `default: break;` } @@ -2003,16 +2049,16 @@ Statement statementSemanticVisit(Statement s, Scope* sc) s = new ExpStatement(ss.loc, new HaltExp(ss.loc)); a.reserve(2); - sc.sw.sdefault = new DefaultStatement(ss.loc, s); + sc.switchStatement.sdefault = new DefaultStatement(ss.loc, s); a.push(ss._body); if (ss._body.blockExit(sc.func, null) & BE.fallthru) a.push(new BreakStatement(Loc.initial, null)); - a.push(sc.sw.sdefault); + a.push(sc.switchStatement.sdefault); cs = new CompoundStatement(ss.loc, a); ss._body = cs; } - if (!(sc.flags & SCOPE.Cfile) && ss.checkLabel()) + if (!sc.inCfile && ss.checkLabel()) { sc.pop(); return setError(); @@ -2052,7 +2098,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // Make a copy of all the cases so that qsort doesn't scramble the actual // data we pass to codegen (the order of the cases in the switch). - CaseStatements *csCopy = (*ss.cases).copy(); + CaseStatements* csCopy = (*ss.cases).copy(); if (numcases) { @@ -2104,7 +2150,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) void visitCase(CaseStatement cs) { - SwitchStatement sw = sc.sw; + SwitchStatement sw = sc.switchStatement; bool errors = false; //printf("CaseStatement::semantic() %s\n", toChars()); @@ -2135,7 +2181,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) { VarDeclaration v = ve.var.isVarDeclaration(); Type t = cs.exp.type.toBasetype(); - if (v && (t.isintegral() || t.ty == Tclass)) + if (v && (t.isIntegral() || t.ty == Tclass)) { /* Flag that we need to do special code generation * for this, i.e. generate a sequence of if-then-else @@ -2160,9 +2206,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ for (Scope* scx = sc; scx; scx = scx.enclosing) { - if (scx.enclosing && scx.enclosing.sw == sw) + if (scx.enclosing && scx.enclosing.switchStatement == sw) continue; - assert(scx.sw == sw); + assert(scx.switchStatement == sw); Dsymbol pscopesym; if (!scx.search(cs.exp.loc, v.ident, pscopesym)) @@ -2217,12 +2263,12 @@ Statement statementSemanticVisit(Statement s, Scope* sc) i++; } - if (sc.sw.tf != sc.tf) + if (sc.switchStatement.tryFinally != sc.tryFinally) { error(cs.loc, "`switch` and `case` are in different `finally` blocks"); errors = true; } - if (sc.sw.tryBody != sc.tryBody) + if (sc.switchStatement.tryBody != sc.tryBody) { error(cs.loc, "case cannot be in different `try` block level from `switch`"); errors = true; @@ -2250,7 +2296,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) void visitCaseRange(CaseRangeStatement crs) { - SwitchStatement sw = sc.sw; + SwitchStatement sw = sc.switchStatement; if (sw is null) { error(crs.loc, "case range not in `switch` statement"); @@ -2288,7 +2334,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) uinteger_t fval = crs.first.toInteger(); uinteger_t lval = crs.last.toInteger(); - if ((crs.first.type.isunsigned() && fval > lval) || (!crs.first.type.isunsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval)) + if ((crs.first.type.isUnsigned() && fval > lval) || (!crs.first.type.isUnsigned() && cast(sinteger_t)fval > cast(sinteger_t)lval)) { error(crs.loc, "first `case %s` is greater than last `case %s`", crs.first.toChars(), crs.last.toChars()); errors = true; @@ -2335,26 +2381,26 @@ Statement statementSemanticVisit(Statement s, Scope* sc) { //printf("DefaultStatement::semantic()\n"); bool errors = false; - if (sc.sw) + if (sc.switchStatement) { - if (sc.sw.sdefault) + if (sc.switchStatement.sdefault) { error(ds.loc, "`switch` statement already has a default"); errors = true; } - sc.sw.sdefault = ds; + sc.switchStatement.sdefault = ds; - if (sc.sw.tf != sc.tf) + if (sc.switchStatement.tryFinally != sc.tryFinally) { error(ds.loc, "`switch` and `default` are in different `finally` blocks"); errors = true; } - if (sc.sw.tryBody != sc.tryBody) + if (sc.switchStatement.tryBody != sc.tryBody) { error(ds.loc, "default cannot be in different `try` block level from `switch`"); errors = true; } - if (sc.sw.isFinal) + if (sc.switchStatement.isFinal) { error(ds.loc, "`default` statement not allowed in `final switch` statement"); errors = true; @@ -2380,7 +2426,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) /* https://dlang.org/spec/statement.html#goto-statement */ - gds.sw = sc.sw; + gds.sw = sc.switchStatement; if (!gds.sw) { error(gds.loc, "`goto default` not in `switch` statement"); @@ -2399,7 +2445,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) /* https://dlang.org/spec/statement.html#goto-statement */ - if (!sc.sw) + if (!sc.switchStatement) { error(gcs.loc, "`goto case` not in `switch` statement"); return setError(); @@ -2408,13 +2454,13 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (gcs.exp) { gcs.exp = gcs.exp.expressionSemantic(sc); - gcs.exp = gcs.exp.implicitCastTo(sc, sc.sw.condition.type); + gcs.exp = gcs.exp.implicitCastTo(sc, sc.switchStatement.condition.type); gcs.exp = gcs.exp.optimize(WANTvalue); if (gcs.exp.op == EXP.error) return setError(); } - sc.sw.gotoCases.push(gcs); + sc.switchStatement.gotoCases.push(gcs); result = gcs; } @@ -2423,7 +2469,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) /* https://dlang.org/spec/statement.html#return-statement */ - //printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars()); + //printf("ReturnStatement.dsymbolSemantic() %s\n", toChars(rs)); FuncDeclaration fd = sc.parent.isFuncDeclaration(); if (fd.fes) @@ -2459,31 +2505,31 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Type tret = tf.next; Type tbret = tret ? tret.toBasetype() : null; - bool inferRef = (tf.isref && (fd.storage_class & STC.auto_)); + bool inferRef = (tf.isRef && (fd.storage_class & STC.auto_)); Expression e0 = null; bool errors = false; - if (sc.flags & SCOPE.contract) + if (sc.contract) { error(rs.loc, "`return` statements cannot be in contracts"); errors = true; } - if (sc.os) + if (sc.scopeGuard) { // @@@DEPRECATED_2.112@@@ // Deprecated in 2.100, transform into an error in 2.112 - if (sc.os.tok == TOK.onScopeFailure) + if (sc.scopeGuard.tok == TOK.onScopeFailure) { deprecation(rs.loc, "`return` statements cannot be in `scope(failure)` bodies."); deprecationSupplemental(rs.loc, "Use try-catch blocks for this purpose"); } else { - error(rs.loc, "`return` statements cannot be in `%s` bodies", Token.toChars(sc.os.tok)); + error(rs.loc, "`return` statements cannot be in `%s` bodies", Token.toChars(sc.scopeGuard.tok)); errors = true; } } - if (sc.tf) + if (sc.tryFinally) { error(rs.loc, "`return` statements cannot be in `finally` bodies"); errors = true; @@ -2504,7 +2550,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else if (rs.exp) { - fd.hasReturnExp |= (fd.hasReturnExp & 1 ? 16 : 1); + fd.hasMultipleReturnExp = fd.hasReturnExp; + fd.hasReturnExp = true; FuncLiteralDeclaration fld = fd.isFuncLiteralDeclaration(); if (tret) @@ -2515,7 +2562,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) rs.exp = rs.exp.expressionSemantic(sc); rs.exp = rs.exp.arrayFuncConv(sc); // If we're returning by ref, allow the expression to be `shared` - const returnSharedRef = (tf.isref && (fd.inferRetType || tret.isShared())); + const returnSharedRef = (tf.isRef && (fd.inferRetType || tret.isShared())); rs.exp.checkSharedAccess(sc, returnSharedRef); // for static alias this: https://issues.dlang.org/show_bug.cgi?id=17684 @@ -2566,7 +2613,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) //errors = true; } if (global.endGagging(olderrors)) - deprecation(rs.exp.loc, "`%s` has no effect", rs.exp.toChars()); + deprecation(rs.exp.loc, "`%s` has no effect", rs.exp.toErrMsg()); /* Replace: * return exp; @@ -2633,13 +2680,13 @@ Statement statementSemanticVisit(Statement s, Scope* sc) void turnOffRef(scope void delegate() supplemental) { - tf.isref = false; // return by value - tf.isreturn = false; // ignore 'return' attribute, whether explicit or inferred + tf.isRef = false; // return by value + tf.isReturn = false; // ignore 'return' attribute, whether explicit or inferred fd.storage_class &= ~STC.return_; // If we previously assumed the function could be ref when // checking for `shared`, make sure we were right - if (global.params.noSharedAccess == FeatureState.enabled && rs.exp.type.isShared()) + if (sc.previews.noSharedAccess && rs.exp.type.isShared()) { .error(fd.loc, "%s `%s` function returns `shared` but cannot be inferred `ref`", fd.kind, fd.toPrettyChars); supplemental(); @@ -2652,11 +2699,11 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ Scope* sc2 = sc.push(); sc2.eSink = global.errorSinkNull; - bool err = checkReturnEscapeRef(sc2, rs.exp, true); + bool err = checkReturnEscapeRef(*sc2, rs.exp, true); sc2.pop(); if (err) - turnOffRef(() { checkReturnEscapeRef(sc, rs.exp, false); }); + turnOffRef(() { checkReturnEscapeRef(*sc, rs.exp, false); }); else if (!rs.exp.type.constConv(tf.next)) turnOffRef( () => rs.loc.errorSupplemental("cannot implicitly convert `%s` of type `%s` to `%s`", @@ -2706,7 +2753,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // https://issues.dlang.org/show_bug.cgi?id=23914 if (inferRef && !resType.isTypeNoreturn()) // deduce 'auto ref' - tf.isref = false; + tf.isRef = false; if (tbret.ty != Tvoid && !resType.isTypeNoreturn()) // if non-void return { @@ -2863,7 +2910,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Statement s = ls.statement; if (!s || !s.hasBreak()) error(bs.loc, "label `%s` has no `break`", bs.ident.toChars()); - else if (ls.tf != sc.tf) + else if (ls.tf != sc.tryFinally) error(bs.loc, "cannot break out of `finally` block"); else { @@ -2879,9 +2926,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else if (!sc.sbreak) { - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { - error(bs.loc, "`break` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok)); + error(bs.loc, "`break` is not allowed inside `%s` bodies", Token.toChars(sc.scopeGuard.tok)); } else if (sc.fes) { @@ -2951,7 +2998,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) Statement s = ls.statement; if (!s || !s.hasContinue()) error(cs.loc, "label `%s` has no `continue`", cs.ident.toChars()); - else if (ls.tf != sc.tf) + else if (ls.tf != sc.tryFinally) error(cs.loc, "cannot continue out of `finally` block"); else { @@ -2966,9 +3013,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } else if (!sc.scontinue) { - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { - error(cs.loc, "`continue` is not allowed inside `%s` bodies", Token.toChars(sc.os.tok)); + error(cs.loc, "`continue` is not allowed inside `%s` bodies", Token.toChars(sc.scopeGuard.tok)); } else if (sc.fes) { @@ -3018,8 +3065,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) */ if (!ClassDeclaration.object) { - error(ss.loc, "missing or corrupt object.d"); - fatal(); + ObjectNotFound(ss.loc, Id.Object); + return setError(); } Type t = ClassDeclaration.object.type; @@ -3036,14 +3083,14 @@ Statement statementSemanticVisit(Statement s, Scope* sc) * _d_monitorenter(tmp); * try { body } finally { _d_monitorexit(tmp); } */ - auto tmp = copyToTemp(0, "__sync", ss.exp); + auto tmp = copyToTemp(STC.none, "__sync", ss.exp); tmp.dsymbolSemantic(sc); auto cs = new Statements(); cs.push(new ExpStatement(ss.loc, tmp)); auto args = new Parameters(); - args.push(new Parameter(Loc.initial, 0, ClassDeclaration.object.type, null, null, null)); + args.push(new Parameter(Loc.initial, STC.none, ClassDeclaration.object.type, null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(args, Type.tvoid, Id.monitorenter); Expression e = new CallExp(ss.loc, fdenter, new VarExp(ss.loc, tmp)); @@ -3085,7 +3132,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) cs.push(new ExpStatement(ss.loc, v)); auto enterArgs = new Parameters(); - enterArgs.push(new Parameter(Loc.initial, 0, t.pointerTo(), null, null, null)); + enterArgs.push(new Parameter(Loc.initial, STC.none, t.pointerTo(), null, null, null)); FuncDeclaration fdenter = FuncDeclaration.genCfunc(enterArgs, Type.tvoid, Id.criticalenter, STC.nothrow_); Expression e = new AddrExp(ss.loc, tmpExp); @@ -3095,7 +3142,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) cs.push(new ExpStatement(ss.loc, e)); auto exitArgs = new Parameters(); - exitArgs.push(new Parameter(Loc.initial, 0, t, null, null, null)); + exitArgs.push(new Parameter(Loc.initial, STC.none, t, null, null, null)); FuncDeclaration fdexit = FuncDeclaration.genCfunc(exitArgs, Type.tvoid, Id.criticalexit, STC.nothrow_); e = new CallExp(ss.loc, fdexit, tmpExp); @@ -3182,7 +3229,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) * } * } */ - auto tmp = copyToTemp(0, "__withtmp", ws.exp); + auto tmp = copyToTemp(STC.none, "__withtmp", ws.exp); tmp.dsymbolSemantic(sc); auto es = new ExpStatement(ws.loc, tmp); ws.exp = new VarExp(ws.loc, tmp); @@ -3320,7 +3367,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) /* If catch exception type is derived from Exception */ if (c.type.toBasetype().implicitConvTo(ClassDeclaration.exception.type) && - (!c.handler || !c.handler.comeFrom()) && !(sc.flags & SCOPE.debug_)) + (!c.handler || !c.handler.comeFrom()) && !sc.debug_) { // Remove c from the array of catches tcs.catches.remove(i); @@ -3344,7 +3391,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) tfs._body = tfs._body.semanticScope(sc, null, null, tfs); sc = sc.push(); - sc.tf = tfs; + sc.tryFinally = tfs; sc.sbreak = null; sc.scontinue = null; // no break or continue out of finally block tfs.finalbody = tfs.finalbody.semanticNoScope(sc); @@ -3397,13 +3444,13 @@ Statement statementSemanticVisit(Statement s, Scope* sc) // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement, // so the generated catch block cannot be placed in finally block. // See also Catch::semantic. - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { // If enclosing is scope(success) or scope(exit), this will be placed in finally block. - error(oss.loc, "cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.os.tok)); + error(oss.loc, "cannot put `%s` statement inside `%s`", Token.toChars(oss.tok), Token.toChars(sc.scopeGuard.tok)); return setError(); } - if (sc.tf) + if (sc.tryFinally) { error(oss.loc, "cannot put `%s` statement inside `finally` block", Token.toChars(oss.tok)); return setError(); @@ -3411,8 +3458,8 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } sc = sc.push(); - sc.tf = null; - sc.os = oss; + sc.tryFinally = null; + sc.scopeGuard = oss; if (oss.tok != TOK.onScopeFailure) { // Jump out from scope(failure) block is allowed. @@ -3448,8 +3495,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) if (ds.statement) { sc = sc.push(); - sc.flags |= SCOPE.debug_; + sc.debug_ = true; ds.statement = ds.statement.statementSemantic(sc); + debugThrowWalker(ds.statement); sc.pop(); } result = ds.statement; @@ -3466,10 +3514,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc) gs.ident = fixupLabelName(sc, gs.ident); gs.label = fd.searchLabel(gs.ident, gs.loc); gs.tryBody = sc.tryBody; - gs.tf = sc.tf; - gs.os = sc.os; + gs.tf = sc.tryFinally; + gs.os = sc.scopeGuard; gs.lastVar = sc.lastVar; - gs.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0; + gs.inCtfeBlock = sc.ctfeBlock; if (!gs.label.statement && sc.fes) { @@ -3493,7 +3541,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) fd.gotos = new GotoStatements(); fd.gotos.push(gs); } - else if (!(sc.flags & SCOPE.Cfile) && gs.checkLabel()) + else if (!sc.inCfile && gs.checkLabel()) return setError(); result = gs; @@ -3506,10 +3554,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc) ls.ident = fixupLabelName(sc, ls.ident); ls.tryBody = sc.tryBody; - ls.tf = sc.tf; - ls.os = sc.os; + ls.tf = sc.tryFinally; + ls.os = sc.scopeGuard; ls.lastVar = sc.lastVar; - ls.inCtfeBlock = (sc.flags & SCOPE.ctfeBlock) != 0; + ls.inCtfeBlock = sc.ctfeBlock; LabelDsymbol ls2 = fd.searchLabel(ls.ident, ls.loc); if (ls2.statement && !ls2.duplicated) @@ -3531,6 +3579,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) ls2.statement = ls; sc = sc.push(); + sc.lastVar = sc.enclosing.lastVar; sc.scopesym = sc.enclosing.scopesym; sc.ctorflow.orCSX(CSX.label); @@ -3538,6 +3587,10 @@ Statement statementSemanticVisit(Statement s, Scope* sc) sc.slabel = ls; if (ls.statement) ls.statement = ls.statement.statementSemantic(sc); + + //issue 24534: lastVar may have been updated in the nested scope + sc.enclosing.lastVar = sc.lastVar; + sc.pop(); result = ls; @@ -3580,9 +3633,9 @@ Statement statementSemanticVisit(Statement s, Scope* sc) } assert(sc.func); - if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not")) + if (!(cas.stc & STC.pure_) && sc.func.setImpure(cas.loc, "executing an `asm` statement without `pure` annotation")) error(cas.loc, "`asm` statement is assumed to be impure - mark it with `pure` if it is not"); - if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "`asm` statement in %s `%s` is assumed to use the GC - mark it with `@nogc` if it does not")) + if (!(cas.stc & STC.nogc) && sc.func.setGC(cas.loc, "executing an `asm` statement without `@nogc` annotation")) error(cas.loc, "`asm` statement is assumed to use the GC - mark it with `@nogc` if it does not"); // @@@DEPRECATED_2.114@@@ // change deprecation() to error(), add `else` and remove `| STC.safe` @@ -3591,7 +3644,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) deprecation(cas.loc, "`asm` statement cannot be marked `@safe`, use `@system` or `@trusted` instead"); if (!(cas.stc & (STC.trusted | STC.safe))) { - sc.setUnsafe(false, cas.loc, "`asm` statement is assumed to be `@system` - mark it with `@trusted` if it is not"); + sc.setUnsafe(false, cas.loc, "executing an `asm` statement without `@trusted` annotation"); } sc.pop(); @@ -3653,7 +3706,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc) * * Returns: true if the `throw` is valid, or false if an error was found */ -public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) +public bool throwSemantic(Loc loc, ref Expression exp, Scope* sc) { if (!global.params.useExceptions) { @@ -3670,9 +3723,6 @@ public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) return false; } - if (FuncDeclaration fd = sc.parent.isFuncDeclaration()) - fd.hasReturnExp |= 2; - if (auto ne = exp.isNewExp()) { ne.thrownew = true; @@ -3690,7 +3740,7 @@ public bool throwSemantic(const ref Loc loc, ref Expression exp, Scope* sc) exp.loc.deprecation("cannot throw object of qualified type `%s`", exp.type.toChars()); //return false; } - checkThrowEscape(sc, exp, false); + checkThrowEscape(*sc, exp, false); ClassDeclaration cd = exp.type.toBasetype().isClassHandle(); if (!cd || ((cd != ClassDeclaration.throwable) && !ClassDeclaration.throwable.isBaseOf(cd, null))) @@ -3807,11 +3857,11 @@ private extern(D) Expression applyArray(ForeachStatement fs, Expression flde, auto params = new Parameters(); params.push(new Parameter(Loc.initial, STC.in_, tn.arrayOf(), null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); dgty = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(Loc.initial, 0, dgty, null, null, null)); + params.push(new Parameter(Loc.initial, STC.none, dgty, null, null, null)); fdapply = FuncDeclaration.genCfunc(params, Type.tint32, fdname.ptr); if (tab.isTypeSArray()) @@ -3872,14 +3922,14 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld if (!fdapply[i]) { auto params = new Parameters(); - params.push(new Parameter(Loc.initial, 0, Type.tvoid.pointerTo(), null, null, null)); + params.push(new Parameter(Loc.initial, STC.none, Type.tvoid.pointerTo(), null, null, null)); params.push(new Parameter(Loc.initial, STC.const_, Type.tsize_t, null, null, null)); auto dgparams = new Parameters(); - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); if (dim == 2) - dgparams.push(new Parameter(Loc.initial, 0, Type.tvoidptr, null, null, null)); + dgparams.push(new Parameter(Loc.initial, STC.none, Type.tvoidptr, null, null, null)); fldeTy[i] = new TypeDelegate(new TypeFunction(ParameterList(dgparams), Type.tint32, LINK.d)); - params.push(new Parameter(Loc.initial, 0, fldeTy[i], null, null, null)); + params.push(new Parameter(Loc.initial, STC.none, fldeTy[i], null, null, null)); fdapply[i] = FuncDeclaration.genCfunc(params, Type.tint32, i ? Id._aaApply2 : Id._aaApply); } @@ -3905,7 +3955,7 @@ private extern(D) Expression applyAssocArray(ForeachStatement fs, Expression fld return ec; } -private extern(D) Statement loopReturn(Expression e, Statements* cases, const ref Loc loc) +private extern(D) Statement loopReturn(Expression e, Statements* cases, Loc loc) { if (!cases.length) { @@ -3950,19 +4000,19 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti auto params = new Parameters(); foreach (i, p; *fs.parameters) { - StorageClass stc = STC.ref_ | (p.storageClass & STC.scope_); + STC stc = STC.ref_ | (p.storageClass & STC.scope_); Identifier id; p.type = p.type.typeSemantic(fs.loc, sc); p.type = p.type.addStorageClass(p.storageClass); if (tfld) { - Parameter prm = tfld.parameterList[i]; - //printf("\tprm = %s%s\n", (prm.storageClass&STC.ref_?"ref ":"").ptr, prm.ident.toChars()); - stc = (prm.storageClass & STC.ref_) | (p.storageClass & STC.scope_); - if ((p.storageClass & STC.ref_) != (prm.storageClass & STC.ref_)) + Parameter param = tfld.parameterList[i]; + //printf("\tparam = %s%s\n", (param.storageClass&STC.ref_?"ref ":"").ptr, param.ident.toChars()); + stc = (param.storageClass & STC.ref_) | (p.storageClass & STC.scope_); + if ((p.storageClass & STC.ref_) != (param.storageClass & STC.ref_)) { - if (!(prm.storageClass & STC.ref_)) + if (!(param.storageClass & STC.ref_)) { error(fs.loc, "`foreach`: cannot make `%s` `ref`", p.ident.toChars()); return null; @@ -3994,7 +4044,7 @@ private FuncExp foreachBodyToFunction(Scope* sc, ForeachStatement fs, TypeFuncti } // https://issues.dlang.org/show_bug.cgi?id=13840 // Throwable nested function inside nothrow function is acceptable. - StorageClass stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); + STC stc = mergeFuncAttrs(STC.safe | STC.pure_ | STC.nogc, fs.func); auto tf = new TypeFunction(ParameterList(params), Type.tint32, LINK.d, stc); fs.cases = new Statements(); fs.gotos = new ScopeStatements(); @@ -4013,13 +4063,13 @@ void catchSemantic(Catch c, Scope* sc) { //printf("Catch::semantic(%s)\n", ident.toChars()); - if (sc.os && sc.os.tok != TOK.onScopeFailure) + if (sc.scopeGuard && sc.scopeGuard.tok != TOK.onScopeFailure) { // If enclosing is scope(success) or scope(exit), this will be placed in finally block. - error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.os.tok)); + error(c.loc, "cannot put `catch` statement inside `%s`", Token.toChars(sc.scopeGuard.tok)); c.errors = true; } - if (sc.tf) + if (sc.tryFinally) { /* This is because the _d_local_unwind() gets the stack munged * up on this. The workaround is to place any try-catches into @@ -4059,7 +4109,7 @@ void catchSemantic(Catch c, Scope* sc) return; } - StorageClass stc; + STC stc; auto cd = c.type.toBasetype().isClassHandle(); if (!cd) { @@ -4075,7 +4125,7 @@ void catchSemantic(Catch c, Scope* sc) } if (!c.internalCatch) { - if (sc.setUnsafe(false, c.loc, "cannot catch C++ class objects in `@safe` code")) + if (sc.setUnsafe(false, c.loc, "catching C++ class objects")) c.errors = true; } } @@ -4087,18 +4137,18 @@ void catchSemantic(Catch c, Scope* sc) else if (!c.internalCatch && ClassDeclaration.exception && cd != ClassDeclaration.exception && !ClassDeclaration.exception.isBaseOf(cd, null) && sc.setUnsafe(false, c.loc, - "can only catch class objects derived from `Exception` in `@safe` code, not `%s`", c.type)) + "catching class objects derived from `%s` instead of `Exception`", c.type)) { c.errors = true; } - else if (global.params.ehnogc) + else if (sc.previews.dip1008) { stc |= STC.scope_; } // DIP1008 requires destruction of the Throwable, even if the user didn't specify an identifier auto ident = c.ident; - if (!ident && global.params.ehnogc) + if (!ident && sc.previews.dip1008) ident = Identifier.generateAnonymousId("var"); if (ident) @@ -4108,7 +4158,7 @@ void catchSemantic(Catch c, Scope* sc) c.var.dsymbolSemantic(sc); sc.insert(c.var); - if (global.params.ehnogc && stc & STC.scope_) + if (sc.previews.dip1008 && stc & STC.scope_) { /* Add a destructor for c.var * try { handler } finally { if (!__ctfe) _d_delThrowable(var); } @@ -4216,7 +4266,7 @@ Statement scopeCode(Statement statement, Scope* sc, out Statement sentry, out St * sexception: x = true; * sfinally: if (!x) statement; */ - auto v = copyToTemp(0, "__os", IntegerExp.createBool(false)); + auto v = copyToTemp(STC.none, "__os", IntegerExp.createBool(false)); v.dsymbolSemantic(sc); sentry = new ExpStatement(statement.loc, v); @@ -4367,7 +4417,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState } p.type = p.type.typeSemantic(loc, sc); - if (!p.type.isintegral()) + if (!p.type.isIntegral()) { error(fs.loc, "foreach: key cannot be of non-integral type `%s`", p.type.toChars()); @@ -4409,7 +4459,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState * Returns: * `true` iff the declaration was successful. */ - bool declareVariable(StorageClass storageClass, Type type, Identifier ident, Expression e, Type t) + bool declareVariable(STC storageClass, Type type, Identifier ident, Expression e, Type t) { if (storageClass & (STC.out_ | STC.lazy_) || storageClass & STC.ref_ && !te) @@ -4550,7 +4600,7 @@ public auto makeTupleForeach(Scope* sc, bool isStatic, bool isDecl, ForeachState { // expand tuples into multiple `static foreach` variables. assert(e && !t); auto ident = Identifier.generateId("__value"); - declareVariable(0, e.type, ident, e, null); + declareVariable(STC.none, e.type, ident, e, null); import dmd.cond: StaticForeach; auto field = Identifier.idPool(StaticForeach.tupleFieldName.ptr,StaticForeach.tupleFieldName.length); Expression access = new DotIdExp(loc, e, field); @@ -4720,7 +4770,6 @@ private Statements* flatten(Statement statement, Scope* sc) if (dc) { s = new DebugStatement(cs.loc, cs.ifbody); - debugThrowWalker(cs.ifbody); } else s = cs.ifbody; @@ -4787,7 +4836,7 @@ private Statements* flatten(Statement statement, Scope* sc) OutBuffer buf; - if (expressionsToString(buf, sc, cs.exps)) + if (expressionsToString(buf, sc, cs.exps, cs.loc, null, true)) return errorStatements(); const errors = global.errors; @@ -4795,9 +4844,9 @@ private Statements* flatten(Statement statement, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto loc = adjustLocForMixin(str, cs.loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(loc, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, cs.loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); auto a = new Statements(); @@ -4892,7 +4941,8 @@ Params: */ private void debugThrowWalker(Statement s) { - + if (!s) + return; extern(C++) final class DebugWalker : SemanticTimeTransitiveVisitor { alias visit = SemanticTimeTransitiveVisitor.visit; diff --git a/gcc/d/dmd/staticassert.d b/gcc/d/dmd/staticassert.d index 08780ca..52ded55 100644 --- a/gcc/d/dmd/staticassert.d +++ b/gcc/d/dmd/staticassert.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/version.html#static-assert, Static Assert) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/staticassert.d, _staticassert.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticassert.d, _staticassert.d) * Documentation: https://dlang.org/phobos/dmd_staticassert.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticassert.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/staticassert.d */ module dmd.staticassert; @@ -28,17 +28,17 @@ extern (C++) final class StaticAssert : Dsymbol Expression exp; Expressions* msgs; - extern (D) this(const ref Loc loc, Expression exp, Expression msg) + extern (D) this(Loc loc, Expression exp, Expression msg) { - super(loc, Id.empty); + super(DSYM.staticAssert, loc, Id.empty); this.exp = exp; this.msgs = new Expressions(1); (*this.msgs)[0] = msg; } - extern (D) this(const ref Loc loc, Expression exp, Expressions* msgs) + extern (D) this(Loc loc, Expression exp, Expressions* msgs) { - super(loc, Id.empty); + super(DSYM.staticAssert, loc, Id.empty); this.exp = exp; this.msgs = msgs; } @@ -49,23 +49,11 @@ extern (C++) final class StaticAssert : Dsymbol return new StaticAssert(loc, exp.syntaxCopy(), msgs ? Expression.arraySyntaxCopy(msgs) : null); } - override bool oneMember(out Dsymbol ps, Identifier ident) - { - //printf("StaticAssert::oneMember())\n"); - ps = null; - return true; - } - override const(char)* kind() const { return "static assert"; } - override inout(StaticAssert) isStaticAssert() inout - { - return this; - } - override void accept(Visitor v) { v.visit(this); diff --git a/gcc/d/dmd/staticassert.h b/gcc/d/dmd/staticassert.h index ed76de0..1ef7285 100644 --- a/gcc/d/dmd/staticassert.h +++ b/gcc/d/dmd/staticassert.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -21,8 +21,6 @@ public: Expressions *msg; StaticAssert *syntaxCopy(Dsymbol *s) override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; const char *kind() const override; - StaticAssert *isStaticAssert() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/staticcond.d b/gcc/d/dmd/staticcond.d index 3ab6885..c2e87c7 100644 --- a/gcc/d/dmd/staticcond.d +++ b/gcc/d/dmd/staticcond.d @@ -1,12 +1,12 @@ /** * Lazily evaluate static conditions for `static if`, `static assert` and template constraints. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/staticcond.d, _staticcond.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/staticcond.d, _staticcond.d) * Documentation: https://dlang.org/phobos/dmd_staticcond.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/staticcond.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/staticcond.d */ module dmd.staticcond; diff --git a/gcc/d/dmd/stmtstate.d b/gcc/d/dmd/stmtstate.d index e1ed165..1c1fd08 100644 --- a/gcc/d/dmd/stmtstate.d +++ b/gcc/d/dmd/stmtstate.d @@ -1,12 +1,12 @@ /** * Used to help transform statement AST into flow graph. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/stmtstate.d, _stmtstate.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/stmtstate.d, _stmtstate.d) * Documentation: https://dlang.org/phobos/dmd_stmtstate.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/stmtstate.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/stmtstate.d */ module dmd.stmtstate; diff --git a/gcc/d/dmd/target.d b/gcc/d/dmd/target.d index cff1d2e..46926cd 100644 --- a/gcc/d/dmd/target.d +++ b/gcc/d/dmd/target.d @@ -15,17 +15,20 @@ * - $(LINK2 https://github.com/ldc-developers/ldc, LDC repository) * - $(LINK2 https://github.com/D-Programming-GDC/gcc, GDC repository) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/target.d, _target.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/target.d, _target.d) * Documentation: https://dlang.org/phobos/dmd_target.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/target.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/target.d */ module dmd.target; -import dmd.globals : Param, CHECKENABLE; +import core.stdc.stdio; + +import dmd.astenums : CHECKENABLE; +import dmd.globals : Param; enum CPU : ubyte { @@ -111,7 +114,9 @@ extern (C++) struct Target /// Architecture name const(char)[] architectureName; CPU cpu; // CPU instruction set to target + bool isAArch64; // generate 64 bit Arm code bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd + bool isX86; // generate 32 bit Intel x86 code bool isLP64; // pointers are 64 bits // Environmental @@ -119,7 +124,6 @@ extern (C++) struct Target const(char)[] lib_ext; /// extension for static library files const(char)[] dll_ext; /// extension for dynamic library files bool run_noext; /// allow -run sources without extensions - bool omfobj; // for Win32: write OMF object files instead of MsCoff /** * Values representing all properties for floating point types */ @@ -190,7 +194,7 @@ extern (C++) struct Target * Returns: * `Type` that represents `va_list`. */ - extern (C++) Type va_listType(const ref Loc loc, Scope* sc); + extern (C++) Type va_listType(Loc loc, Scope* sc); /** * Checks whether the target supports a vector type. @@ -263,7 +267,7 @@ extern (C++) struct Target * Returns: * Expression for the requested targetInfo */ - extern (C++) Expression getTargetInfo(const(char)* name, const ref Loc loc); + extern (C++) Expression getTargetInfo(const(char)* name, Loc loc); /** * Params: @@ -297,11 +301,12 @@ extern (C++) struct Target */ struct TargetC { + import dmd.declaration : BitFieldDeclaration; + enum Runtime : ubyte { Unspecified, Bionic, - DigitalMars, Glibc, Microsoft, Musl, @@ -313,7 +318,6 @@ struct TargetC enum BitFieldStyle : ubyte { Unspecified, - DM, /// Digital Mars 32 bit C compiler MS, /// Microsoft 32 and 64 bit C compilers /// https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160 /// https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160 @@ -329,6 +333,14 @@ struct TargetC ubyte wchar_tsize; /// size of a C `wchar_t` type Runtime runtime; /// vendor of the C runtime to link against BitFieldStyle bitFieldStyle; /// different C compilers do it differently + + /** + * Indicates whether the specified bit-field contributes to the alignment + * of the containing aggregate. + * E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length) + * bit-fields. + */ + extern (C++) bool contributesToAggregateAlignment(BitFieldDeclaration bfd); } //////////////////////////////////////////////////////////////////////////////// @@ -345,9 +357,8 @@ struct TargetCPP enum Runtime : ubyte { Unspecified, - Clang, - DigitalMars, - Gcc, + LLVM, + GNU, Microsoft, Sun } diff --git a/gcc/d/dmd/target.h b/gcc/d/dmd/target.h index 1209505..5965609 100644 --- a/gcc/d/dmd/target.h +++ b/gcc/d/dmd/target.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2013-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2013-2025 by The D Language Foundation, All Rights Reserved * written by Iain Buclaw * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -16,6 +16,7 @@ #include "globals.h" #include "tokens.h" +class BitFieldDeclaration; class ClassDeclaration; class Dsymbol; class Expression; @@ -50,7 +51,6 @@ struct TargetC { Unspecified, Bionic, - DigitalMars, Glibc, Microsoft, Musl, @@ -62,7 +62,6 @@ struct TargetC enum class BitFieldStyle : unsigned char { Unspecified, - DM, // Digital Mars 32 bit C compiler MS, // Microsoft 32 and 64 bit C compilers // https://docs.microsoft.com/en-us/cpp/c-language/c-bit-fields?view=msvc-160 // https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields?view=msvc-160 @@ -79,6 +78,8 @@ struct TargetC uint8_t wchar_tsize; // size of a C 'wchar_t' type Runtime runtime; BitFieldStyle bitFieldStyle; // different C compilers do it differently + + bool contributesToAggregateAlignment(BitFieldDeclaration *bfd); }; struct TargetCPP @@ -86,9 +87,8 @@ struct TargetCPP enum class Runtime : unsigned char { Unspecified, - Clang, - DigitalMars, - Gcc, + LLVM, + GNU, Microsoft, Sun }; @@ -156,7 +156,9 @@ struct Target DString architectureName; // name of the platform architecture (e.g. X86_64) CPU cpu; // CPU instruction set to target + d_bool isAArch64; // generate 64 bit Arm code d_bool isX86_64; // generate 64 bit code for x86_64; true by default for 64 bit dmd + d_bool isX86; // generate 32 bit Intel x86 code d_bool isLP64; // pointers are 64 bits // Environmental @@ -164,7 +166,6 @@ struct Target DString lib_ext; /// extension for static library files DString dll_ext; /// extension for dynamic library files d_bool run_noext; /// allow -run sources without extensions - d_bool omfobj; /// for Win32: write OMF object files instead of COFF template <typename T> struct FPTypeProperties @@ -196,15 +197,15 @@ public: // Type sizes and support. unsigned alignsize(Type *type); unsigned fieldalign(Type *type); - Type *va_listType(const Loc &loc, Scope *sc); // get type of va_list + Type *va_listType(Loc loc, Scope *sc); // get type of va_list int isVectorTypeSupported(int sz, Type *type); - bool isVectorOpSupported(Type *type, EXP op, Type *t2 = NULL); + bool isVectorOpSupported(Type *type, EXP op, Type *t2 = nullptr); // ABI and backend. LINK systemLinkage(); TypeTuple *toArgTypes(Type *t); bool isReturnOnStack(TypeFunction *tf, bool needsThis); bool preferPassByRef(Type *t); - Expression *getTargetInfo(const char* name, const Loc& loc); + Expression *getTargetInfo(const char* name, Loc loc); bool isCalleeDestroyingArgs(TypeFunction* tf); bool libraryObjectMonitors(FuncDeclaration *fd, Statement *fbody); bool supportsLinkerDirective() const; diff --git a/gcc/d/dmd/template.h b/gcc/d/dmd/template.h index bccec1a..5576965 100644 --- a/gcc/d/dmd/template.h +++ b/gcc/d/dmd/template.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -77,13 +77,10 @@ public: TemplateDeclaration *syntaxCopy(Dsymbol *) override; bool overloadInsert(Dsymbol *s) override; - bool hasStaticCtorOrDtor() override; const char *kind() const override; - const char *toChars() const override; Visibility visible() override; - TemplateDeclaration *isTemplateDeclaration() override { return this; } bool isDeprecated() const override; bool isOverloadable() const override; @@ -128,14 +125,11 @@ public: virtual bool declareParameter(Scope *sc) = 0; virtual void print(RootObject *oarg, RootObject *oded) = 0; virtual RootObject *specialization() = 0; - virtual RootObject *defaultArg(const Loc &instLoc, Scope *sc) = 0; + virtual RootObject *defaultArg(Loc instLoc, Scope *sc) = 0; virtual bool hasDefaultArg() = 0; DYNCAST dyncast() const override { return DYNCAST_TEMPLATEPARAMETER; } - /* Create dummy argument based on parameter. - */ - virtual RootObject *dummyArg() = 0; void accept(Visitor *v) override { v->visit(this); } }; @@ -153,9 +147,8 @@ public: bool declareParameter(Scope *sc) override final; void print(RootObject *oarg, RootObject *oded) override final; RootObject *specialization() override final; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override final; + RootObject *defaultArg(Loc instLoc, Scope *sc) override final; bool hasDefaultArg() override final; - RootObject *dummyArg() override final; void accept(Visitor *v) override { v->visit(this); } }; @@ -185,9 +178,8 @@ public: bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; + RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -206,9 +198,8 @@ public: bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; + RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -223,9 +214,8 @@ public: bool declareParameter(Scope *sc) override; void print(RootObject *oarg, RootObject *oded) override; RootObject *specialization() override; - RootObject *defaultArg(const Loc &instLoc, Scope *sc) override; + RootObject *defaultArg(Loc instLoc, Scope *sc) override; bool hasDefaultArg() override; - RootObject *dummyArg() override; void accept(Visitor *v) override { v->visit(this); } }; @@ -278,15 +268,12 @@ public: TemplateInstance *syntaxCopy(Dsymbol *) override; Dsymbol *toAlias() override final; // resolve real symbol const char *kind() const override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; - const char *toChars() const override; const char* toPrettyCharsHelper() override final; Identifier *getIdent() override final; bool isDiscardable(); bool needsCodegen(); - TemplateInstance *isTemplateInstance() override final { return this; } void accept(Visitor *v) override { v->visit(this); } }; @@ -297,11 +284,8 @@ public: TemplateMixin *syntaxCopy(Dsymbol *s) override; const char *kind() const override; - bool oneMember(Dsymbol *&ps, Identifier *ident) override; bool hasPointers() override; - const char *toChars() const override; - TemplateMixin *isTemplateMixin() override { return this; } void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/templateparamsem.d b/gcc/d/dmd/templateparamsem.d index f2dc50e..561181a 100644 --- a/gcc/d/dmd/templateparamsem.d +++ b/gcc/d/dmd/templateparamsem.d @@ -1,12 +1,12 @@ /** * Semantic analysis of template parameters. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/templateparamsem.d, _templateparamsem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templateparamsem.d, _templateparamsem.d) * Documentation: https://dlang.org/phobos/dmd_templateparamsem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/templateparamsem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/templateparamsem.d */ module dmd.templateparamsem; @@ -170,7 +170,7 @@ RootObject aliasParameterSemantic(Loc loc, Scope* sc, RootObject o, TemplatePara Dsymbol s = ta.toDsymbol(sc); if (s) return s; - else if (TypeInstance ti = ta.isTypeInstance()) + if (TypeInstance ti = ta.isTypeInstance()) { Type t; const errors = global.errors; @@ -183,14 +183,13 @@ RootObject aliasParameterSemantic(Loc loc, Scope* sc, RootObject o, TemplatePara // see https://issues.dlang.org/show_bug.cgi?id=16472 if (t) return t.typeSemantic(loc, sc); - else if (ea) + if (ea) { return eaCTFE(); } - else if (s) + if (s) return s; - else - assert(0); + assert(0); } else return ta.typeSemantic(loc, sc); diff --git a/gcc/d/dmd/templatesem.d b/gcc/d/dmd/templatesem.d index bd3cd89..e5efce4 100644 --- a/gcc/d/dmd/templatesem.d +++ b/gcc/d/dmd/templatesem.d @@ -1,12 +1,12 @@ /** * Template semantics. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/templatesem.d, _templatesem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/templatesem.d, _templatesem.d) * Documentation: https://dlang.org/phobos/dmd_templatesem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/templatesem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/templatesem.d */ module dmd.templatesem; @@ -23,7 +23,6 @@ import dmd.dcast; import dmd.dclass; import dmd.declaration; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; @@ -56,6 +55,8 @@ import dmd.tokens; import dmd.typesem; import dmd.visitor; +alias funcLeastAsSpecialized = dmd.funcsem.leastAsSpecialized; + /************************************ * Perform semantic analysis on template. * Params: @@ -107,7 +108,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) tempdecl.isstatic = tempdecl.toParent().isModule() || (tempdecl._scope.stc & STC.static_); tempdecl.deprecated_ = !!(sc.stc & STC.deprecated_); - UserAttributeDeclaration.checkGNUABITag(tempdecl, sc.linkage); + checkGNUABITag(tempdecl, sc.linkage); if (!tempdecl.isstatic) { @@ -119,7 +120,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) auto paramsym = new ScopeDsymbol(); paramsym.parent = tempdecl.parent; Scope* paramscope = sc.push(paramsym); - paramscope.stc = 0; + paramscope.stc = STC.none; if (global.params.ddoc.doOutput) { @@ -152,7 +153,7 @@ void templateDeclarationSemantic(Scope* sc, TemplateDeclaration tempdecl) /* Calculate TemplateParameter.dependent */ - TemplateParameters tparams = TemplateParameters(1); + auto tparams = TemplateParameters(1); for (size_t i = 0; i < tempdecl.parameters.length; i++) { TemplateParameter tp = (*tempdecl.parameters)[i]; @@ -352,7 +353,7 @@ MATCH matchWithInstance(Scope* sc, TemplateDeclaration td, TemplateInstance ti, // Resolve parameter types and 'auto ref's. tf.inferenceArguments = argumentList; - uint olderrors = global.startGagging(); + const olderrors = global.startGagging(); fd.type = tf.typeSemantic(td.loc, paramscope); global.endGagging(olderrors); if (fd.type.ty != Tfunction) @@ -455,7 +456,7 @@ bool evaluateConstraint(TemplateDeclaration td, TemplateInstance ti, Scope* sc, // (previously, this was immediately before calling evalStaticCondition), so the // semantic pass knows not to issue deprecation warnings for these throw-away decls. // https://issues.dlang.org/show_bug.cgi?id=21831 - scx.flags |= SCOPE.constraint; + scx.inTemplateConstraint = true; assert(!ti.symtab); if (fd) @@ -602,7 +603,7 @@ Scope* createScopeForTemplateParameters(TemplateDeclaration td, TemplateInstance paramscope.tinst = ti; paramscope.minst = sc.minst; paramscope.callsc = sc; - paramscope.stc = 0; + paramscope.stc = STC.none; return paramscope; } @@ -622,7 +623,7 @@ MATCH leastAsSpecialized(Scope* sc, TemplateDeclaration td, TemplateDeclaration enum LOG_LEASTAS = 0; static if (LOG_LEASTAS) { - printf("%s.leastAsSpecialized(%s)\n", toChars(), td2.toChars()); + printf("%s.leastAsSpecialized(%s)\n", td.toChars(), td2.toChars()); } /* This works by taking the template parameters to this template @@ -904,7 +905,7 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat // Match attributes of tthis against attributes of fd if (fd.type && !fd.isCtorDeclaration() && !(td._scope.stc & STC.static_)) { - StorageClass stc = td._scope.stc | fd.storage_class2; + STC stc = td._scope.stc | fd.storage_class2; // Propagate parent storage class, https://issues.dlang.org/show_bug.cgi?id=5504 Dsymbol p = td.parent; while (p.isTemplateDeclaration() || p.isTemplateInstance()) @@ -1251,7 +1252,13 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat //printf("farg = %s %s\n", farg.type.toChars(), farg.toChars()); RootObject oarg = farg; - if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) + + if (farg.isFuncExp()) + { + // When assigning an untyped (void) lambda `x => y` to a `(F)(ref F)` parameter, + // we don't want to deduce type void creating a void parameter + } + else if ((fparam.storageClass & STC.ref_) && (!(fparam.storageClass & STC.auto_) || farg.isLvalue())) { /* Allow expressions that have CT-known boundaries and type [] to match with [dim] */ @@ -1335,10 +1342,10 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat * We also save/restore sc.func.flags to avoid messing up * attribute inference in the evaluation. */ - const oldflags = sc.func ? sc.func.flags : 0; + const oldflags = sc.func ? sc.func.saveFlags : 0; auto e = resolveAliasThis(sc, farg, true); if (sc.func) - sc.func.flags = oldflags; + sc.func.restoreFlags(oldflags); if (e) { farg = e; @@ -1355,7 +1362,7 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat { // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] } - else if (global.params.rvalueRefParam == FeatureState.enabled) + else if (sc.previews.rvalueRefParam) { // Allow implicit conversion to ref } @@ -1418,9 +1425,9 @@ extern (D) MATCHpair deduceFunctionTemplateMatch(TemplateDeclaration td, Templat Expression e; Type t; Dsymbol s; - Scope *sco; + Scope* sco; - uint errors = global.startGagging(); + const errors = global.startGagging(); /* ref: https://issues.dlang.org/show_bug.cgi?id=11118 * The parameter isn't part of the template * ones, let's try to find it in the @@ -1727,7 +1734,7 @@ FuncDeclaration doHeaderInstantiation(TemplateDeclaration td, TemplateInstance t { // For constructors, emitting return type is necessary for // isReturnIsolated() in functionResolve. - tf.isctor = true; + tf.isCtor = true; Dsymbol parent = td.toParentDecl(); Type tret; @@ -1745,7 +1752,7 @@ FuncDeclaration doHeaderInstantiation(TemplateDeclaration td, TemplateInstance t } tf.next = tret; if (ad && ad.isStructDeclaration()) - tf.isref = 1; + tf.isRef = 1; //printf("tf = %s\n", tf.toChars()); } else @@ -1883,23 +1890,22 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, { version (none) { - printf("functionResolve() dstart = %s\n", dstart.toChars()); - printf(" tiargs:\n"); - if (tiargs) + printf("functionResolve() dstart: %s (", dstart.toChars()); + for (size_t i = 0; i < (tiargs ? (*tiargs).length : 0); i++) { - for (size_t i = 0; i < tiargs.length; i++) - { - RootObject arg = (*tiargs)[i]; - printf("\t%s\n", arg.toChars()); - } + if (i) printf(", "); + RootObject arg = (*tiargs)[i]; + printf("%s", arg.toChars()); } - printf(" fargs:\n"); - for (size_t i = 0; i < (fargs ? fargs.length : 0); i++) + printf(")("); + for (size_t i = 0; i < (argumentList.arguments ? (*argumentList.arguments).length : 0); i++) { - Expression arg = (*fargs)[i]; - printf("\t%s %s\n", arg.type.toChars(), arg.toChars()); + if (i) printf(", "); + Expression arg = (*argumentList.arguments)[i]; + printf("%s %s", arg.type.toChars(), arg.toChars()); //printf("\tty = %d\n", arg.type.ty); } + printf(")\n"); //printf("stc = %llx\n", dstart._scope.stc); //printf("match:t/f = %d/%d\n", ta_last, m.last); } @@ -1938,7 +1944,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, //printf("fd = %s %s, fargs = %s\n", fd.toChars(), fd.type.toChars(), fargs.toChars()); auto tf = fd.type.isTypeFunction(); - int prop = tf.isproperty ? 1 : 2; + int prop = tf.isProperty ? 1 : 2; if (property == 0) property = prop; else if (property != prop) @@ -1986,7 +1992,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, tf.mod = tthis_fd.mod; } const(char)* failMessage; - MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, errorHelper, sc); + MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, errorHelper, sc); //printf("test1: mfa = %d\n", mfa); if (failMessage) errorHelper(failMessage); @@ -2021,8 +2027,8 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, * This is because f() is "more specialized." */ { - MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names); - MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names); + MATCH c1 = funcLeastAsSpecialized(fd, m.lastf, argumentList.names); + MATCH c2 = funcLeastAsSpecialized(m.lastf, fd, argumentList.names); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) return firstIsBetter(); if (c1 < c2) return 0; @@ -2191,7 +2197,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, Type tthis_fd = fd.needThis() && !fd.isCtorDeclaration() ? tthis : null; auto tf = fd.type.isTypeFunction(); - MATCH mfa = tf.callMatch(tthis_fd, argumentList, 0, null, sc); + MATCH mfa = callMatch(fd, tf, tthis_fd, argumentList, 0, null, sc); if (mfa < m.last) return 0; @@ -2293,16 +2299,16 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, // Disambiguate by tf.callMatch auto tf1 = fd.type.isTypeFunction(); auto tf2 = m.lastf.type.isTypeFunction(); - MATCH c1 = tf1.callMatch(tthis_fd, argumentList, 0, null, sc); - MATCH c2 = tf2.callMatch(tthis_best, argumentList, 0, null, sc); + MATCH c1 = callMatch(fd, tf1, tthis_fd, argumentList, 0, null, sc); + MATCH c2 = callMatch(m.lastf, tf2, tthis_best, argumentList, 0, null, sc); //printf("2: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; } { // Disambiguate by picking the most specialized FunctionDeclaration - MATCH c1 = FuncDeclaration.leastAsSpecialized(fd, m.lastf, argumentList.names); - MATCH c2 = FuncDeclaration.leastAsSpecialized(m.lastf, fd, argumentList.names); + MATCH c1 = funcLeastAsSpecialized(fd, m.lastf, argumentList.names); + MATCH c2 = funcLeastAsSpecialized(m.lastf, fd, argumentList.names); //printf("3: c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto Ltd; if (c1 < c2) goto Ltd_best; @@ -2397,7 +2403,7 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, if (m.lastf.type.ty == Terror) goto Lerror; auto tf = m.lastf.type.isTypeFunction(); - if (!tf.callMatch(tthis_best, argumentList, 0, null, sc)) + if (callMatch(m.lastf, tf, tthis_best, argumentList, 0, null, sc) == MATCH.nomatch) goto Lnomatch; /* As https://issues.dlang.org/show_bug.cgi?id=3682 shows, @@ -2427,3 +2433,70 @@ void functionResolve(ref MatchAccumulator m, Dsymbol dstart, Loc loc, Scope* sc, m.last = MATCH.nomatch; } } +/* Create dummy argument based on parameter. + */ +private RootObject dummyArg(TemplateParameter tp) +{ + scope v = new DummyArgVisitor(); + tp.accept(v); + return v.result; +} +private extern(C++) class DummyArgVisitor : Visitor +{ + RootObject result; + + alias visit = typeof(super).visit; + override void visit(TemplateTypeParameter ttp) + { + Type t = ttp.specType; + if (t) + { + result = t; + return; + } + // Use this for alias-parameter's too (?) + if (!ttp.tdummy) + ttp.tdummy = new TypeIdentifier(ttp.loc, ttp.ident); + result = ttp.tdummy; + } + + override void visit(TemplateValueParameter tvp) + { + Expression e = tvp.specValue; + if (e) + { + result = e; + return; + } + + // Create a dummy value + auto pe = cast(void*)tvp.valType in tvp.edummies; + if (pe) + { + result = *pe; + return; + } + + e = tvp.valType.defaultInit(Loc.initial); + tvp.edummies[cast(void*)tvp.valType] = e; + result = e; + } + + override void visit(TemplateAliasParameter tap) + { + RootObject s = tap.specAlias; + if (s) + { + result = s; + return; + } + if (!tap.sdummy) + tap.sdummy = new Dsymbol(DSYM.dsymbol); + result = tap.sdummy; + } + + override void visit(TemplateTupleParameter tap) + { + result = null; + } +} diff --git a/gcc/d/dmd/timetrace.d b/gcc/d/dmd/timetrace.d new file mode 100644 index 0000000..742dde0 --- /dev/null +++ b/gcc/d/dmd/timetrace.d @@ -0,0 +1,82 @@ +/** +Compilation time tracing, -ftime-trace. + +The time trace profile is output in the Chrome Trace Event Format, described +here: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview + +This file is originally from LDC (the LLVM D compiler). + +Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved +Authors: Johan Engelen, Max Haughton, Dennis Korpel +License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) +Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/timetrace.d, common/_timetrace.d) +Documentation: https://dlang.org/phobos/dmd_common_timetrace.html +Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/timetrace.d +*/ +module dmd.timetrace; + +import dmd.dsymbol; +import dmd.expression; + +/** + * Start a new time trace event + * + * Details of the event will be passed as delegates to `timeTraceEndEvent` so + * they're only generated when the event is actually written. + * + * Params: + * eventType = what compilation stage the event belongs to + * (redundant with the eventType of `timeTraceEndEvent` but used by GDC) + */ +extern (C++) +void timeTraceBeginEvent(TimeTraceEventType eventType) +{ +} + +/** + * End a time tracing event, optionally updating the event name and details + * with a delegate. Delegates are used to prevent spending time on string + * generation when an event is too small to be generated anyway. + * + * Params: + * eventType = what compilation stage the event belongs to + * sym = Dsymbol which was analyzed, used to generate 'name' and 'detail' + * e = Expression which was analyzed, used to generate 'name' and 'detail' + * detail = custom lazy string for 'detail' of event + */ +extern (C++) +void timeTraceEndEvent(TimeTraceEventType eventType) +{ +} + +/// ditto +void timeTraceEndEvent(TimeTraceEventType eventType, Dsymbol sym, scope const(char)[] delegate() detail = null) +{ + return timeTraceEndEvent(eventType); +} + +/// ditto +extern (C++) +void timeTraceEndEvent(TimeTraceEventType eventType, Expression e) +{ + return timeTraceEndEvent(eventType); +} + +/// Identifies which compilation stage the event is associated to +enum TimeTraceEventType +{ + generic, + parseGeneral, + parse, + semaGeneral, + sema1Import, + sema1Module, + sema2, + sema3, + ctfe, + ctfeCall, + codegenGlobal, + codegenModule, + codegenFunction, + link, +} diff --git a/gcc/d/dmd/tokens.d b/gcc/d/dmd/tokens.d index da4a3ee..a106207 100644 --- a/gcc/d/dmd/tokens.d +++ b/gcc/d/dmd/tokens.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/lex.html#tokens, Tokens) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/tokens.d, _tokens.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/tokens.d, _tokens.d) * Documentation: https://dlang.org/phobos/dmd_tokens.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/tokens.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/tokens.d */ module dmd.tokens; @@ -27,6 +27,9 @@ enum TOK : ubyte { reserved, + // if this list changes, update + // tokens.h, ../tests/cxxfrontend.cc and ../../test/unit/lexer/location_offset.d to match + // Other leftParenthesis, rightParenthesis, @@ -45,7 +48,6 @@ enum TOK : ubyte false_, throw_, new_, - delete_, variable, slice, version_, @@ -249,6 +251,7 @@ enum TOK : ubyte wchar_tLiteral, endOfLine, // \n, \r, \u2028, \u2029 whitespace, + rvalue, // C only keywords inline, @@ -425,6 +428,7 @@ enum EXP : ubyte interval, loweredAssignExp, + rvalue, } enum FirstCKeyword = TOK.inline; @@ -432,8 +436,10 @@ enum FirstCKeyword = TOK.inline; // Assert that all token enum members have consecutive values and // that none of them overlap static assert(() { - foreach (idx, enumName; __traits(allMembers, TOK)) { - static if (idx != __traits(getMember, TOK, enumName)) { + foreach (idx, enumName; __traits(allMembers, TOK)) + { + static if (idx != __traits(getMember, TOK, enumName)) + { pragma(msg, "Error: Expected TOK.", enumName, " to be ", idx, " but is ", __traits(getMember, TOK, enumName)); static assert(0); } @@ -454,7 +460,6 @@ private immutable TOK[] keywords = TOK.false_, TOK.cast_, TOK.new_, - TOK.delete_, TOK.throw_, TOK.module_, TOK.pragma_, @@ -556,6 +561,7 @@ private immutable TOK[] keywords = TOK.prettyFunction, TOK.shared_, TOK.immutable_, + TOK.rvalue, // C only keywords TOK.inline, @@ -674,12 +680,12 @@ extern (C++) struct Token TOK.false_: "false", TOK.cast_: "cast", TOK.new_: "new", - TOK.delete_: "delete", TOK.throw_: "throw", TOK.module_: "module", TOK.pragma_: "pragma", TOK.typeof_: "typeof", TOK.typeid_: "typeid", + TOK.rvalue: "__rvalue", TOK.template_: "template", TOK.void_: "void", TOK.int8: "byte", @@ -921,13 +927,18 @@ nothrow: return 0; } - extern(D) void appendInterpolatedPart(const ref OutBuffer buf) { + extern(D) void appendInterpolatedPart(const ref OutBuffer buf) + { appendInterpolatedPart(cast(const(char)*)buf[].ptr, buf.length); } - extern(D) void appendInterpolatedPart(const(char)[] str) { + + extern(D) void appendInterpolatedPart(const(char)[] str) + { appendInterpolatedPart(str.ptr, str.length); } - extern(D) void appendInterpolatedPart(const(char)* ptr, size_t length) { + + extern(D) void appendInterpolatedPart(const(char)* ptr, size_t length) + { assert(value == TOK.interpolated); if (interpolatedSet is null) interpolatedSet = new InterpolatedSet; diff --git a/gcc/d/dmd/tokens.h b/gcc/d/dmd/tokens.h index ef91001..ecbcc02 100644 --- a/gcc/d/dmd/tokens.h +++ b/gcc/d/dmd/tokens.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -54,7 +54,6 @@ enum class TOK : unsigned char false_, throw_, new_, - delete_, variable, slice, version_, @@ -258,6 +257,7 @@ enum class TOK : unsigned char wchar_tLiteral, endOfLine, // \n, \r, \u2028, \u2029 whitespace, + rvalue, // C only keywords inline_, @@ -476,7 +476,7 @@ struct Token Identifier *ident; }; - Token() : next(NULL) {} + Token() : next(nullptr) {} const char *toChars() const; static const char *toChars(TOK value); diff --git a/gcc/d/dmd/traits.d b/gcc/d/dmd/traits.d index be7aa99..127a89d 100644 --- a/gcc/d/dmd/traits.d +++ b/gcc/d/dmd/traits.d @@ -3,12 +3,12 @@ * * Specification: $(LINK2 https://dlang.org/spec/traits.html, Traits) * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/traits.d, _traits.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/traits.d, _traits.d) * Documentation: https://dlang.org/phobos/dmd_traits.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/traits.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/traits.d */ module dmd.traits; @@ -20,12 +20,12 @@ import dmd.arraytypes; import dmd.astcodegen; import dmd.astenums; import dmd.attrib; +import dmd.attribsem; import dmd.canthrow; import dmd.dclass; import dmd.declaration; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dsymbol; @@ -42,6 +42,7 @@ import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.location; +import dmd.mangle : decoToType; import dmd.mtype; import dmd.nogc; import dmd.optimize; @@ -337,7 +338,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) const save = sc.stc; if (e.ident == Id.isDeprecated) sc.stc |= STC.deprecated_; - if (!TemplateInstance.semanticTiargs(e.loc, sc, e.args, 1)) + Scope* sc2 = sc.startCTFE(); + scope(exit) { sc2.endCTFE(); } + if (!TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1)) { sc.stc = save; return ErrorExp.get(); @@ -443,23 +446,23 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (e.ident == Id.isArithmetic) { - return isTypeX(t => t.isintegral() || t.isfloating()); + return isTypeX(t => t.isIntegral() || t.isFloating()); } if (e.ident == Id.isFloating) { - return isTypeX(t => t.isfloating()); + return isTypeX(t => t.isFloating()); } if (e.ident == Id.isIntegral) { - return isTypeX(t => t.isintegral()); + return isTypeX(t => t.isIntegral()); } if (e.ident == Id.isScalar) { - return isTypeX(t => t.isscalar()); + return isTypeX(t => t.isScalar()); } if (e.ident == Id.isUnsigned) { - return isTypeX(t => t.isunsigned()); + return isTypeX(t => t.isUnsigned()); } if (e.ident == Id.isAssociativeArray) { @@ -467,7 +470,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.isDeprecated) { - if (isTypeX(t => t.iscomplex() || t.isimaginary()).toBool().hasValue(true)) + if (isTypeX(t => t.isComplex() || t.isImaginary()).toBool().hasValue(true)) return True(); return isDsymX(t => t.isDeprecated()); } @@ -481,13 +484,19 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } if (e.ident == Id.isAbstractClass) { - return isTypeX(t => t.toBasetype().isTypeClass() && - t.toBasetype().isTypeClass().sym.isAbstract()); + return isTypeX((t) + { + auto c = t.toBasetype().isTypeClass(); + return c && c.sym.isAbstract(); + }); } if (e.ident == Id.isFinalClass) { - return isTypeX(t => t.toBasetype().isTypeClass() && - (t.toBasetype().isTypeClass().sym.storage_class & STC.final_) != 0); + return isTypeX((t) + { + const c = t.toBasetype().isTypeClass(); + return c && (c.sym.storage_class & STC.final_) != 0; + }); } if (e.ident == Id.isTemplate) { @@ -502,6 +511,17 @@ Expression semanticTraits(TraitsExp e, Scope* sc) sm => sm.isTemplateDeclaration() !is null) != 0; }); } + if (e.ident == Id.isBitfield) + { + if (dim != 1) + return dimError(1); + + return isDsymX((s) + { + s = s.toAlias(); + return s.isBitFieldDeclaration() !is null; + }); + } if (e.ident == Id.isPOD) { if (dim != 1) @@ -524,7 +544,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) } return True(); } - if (e.ident == Id.hasCopyConstructor || e.ident == Id.hasPostblit) + if (e.ident == Id.hasCopyConstructor || + e.ident == Id.hasMoveConstructor || + e.ident == Id.hasPostblit) { if (dim != 1) return dimError(1); @@ -542,8 +564,14 @@ Expression semanticTraits(TraitsExp e, Scope* sc) auto ts = tb.isTypeStruct(); if (auto sd = ts ? ts.sym : null) { - return (e.ident == Id.hasPostblit) ? (sd.postblit ? True() : False()) - : (sd.hasCopyCtor ? True() : False()); + bool result; + if (e.ident == Id.hasPostblit) + result = sd.postblit !is null; + else if (e.ident == Id. hasCopyConstructor) + result = sd.hasCopyCtor; + else + result = sd.hasMoveCtor; + return result ? True() : False(); } return False(); } @@ -681,6 +709,25 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return isDeclX(d => (d.storage_class & STC.lazy_) != 0); } + if (e.ident == Id.isCOMClass) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + auto s = getDsymbol(o); + AggregateDeclaration agg; + + if (!s || ((agg = s.isAggregateDeclaration()) is null)) + { + error(e.loc, "argument to `__traits(isCOMClass, %s)` is not a declaration", o.toChars()); + return ErrorExp.get(); + } + + if (ClassDeclaration cd = agg.isClassDeclaration()) + return cd.com ? True() : False(); + return False(); + } if (e.ident == Id.identifier) { // Get identifier for symbol as a string literal @@ -724,7 +771,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); Scope* sc2 = sc.push(); - sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; + sc2.copyFlagsFrom(sc); + sc2.noAccessCheck = true; + sc2.ignoresymbolvisibility = true; bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) @@ -754,13 +803,52 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return se.expressionSemantic(sc); } + if (e.ident == Id.getBitfieldOffset || e.ident == Id.getBitfieldWidth) + { + if (dim != 1) + return dimError(1); + + auto o = (*e.args)[0]; + auto s = getDsymbolWithoutExpCtx(o); + if (!s) + { + error(e.loc, "bitfield symbol expected not `%s`", o.toChars()); + return ErrorExp.get(); + } + + auto vd = s.toAlias.isVarDeclaration(); + if (!vd || !(vd.storage_class & STC.field)) + { + error(e.loc, "bitfield symbol expected not %s `%s`", s.kind, s.toPrettyChars); + return ErrorExp.get(); + } + + uint fieldWidth; + uint bitOffset; + if (auto bf = vd.isBitFieldDeclaration()) + { + fieldWidth = bf.fieldWidth; + bitOffset = bf.bitOffset; + } + else // just a regular field + { + const sz = size(vd.type); + assert(sz < uint.max / 8); // overflow check + fieldWidth = cast(uint)sz * 8; + bitOffset = 0; + } + uint value = e.ident == Id.getBitfieldOffset ? bitOffset : fieldWidth; + return new IntegerExp(e.loc, value, Type.tuns32); + } if (e.ident == Id.getProtection || e.ident == Id.getVisibility) { if (dim != 1) return dimError(1); Scope* sc2 = sc.push(); - sc2.flags = sc.flags | SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility; + sc2.copyFlagsFrom(sc); + sc2.noAccessCheck = true; + sc2.ignoresymbolvisibility = true; bool ok = TemplateInstance.semanticTiargs(e.loc, sc2, e.args, 1); sc2.pop(); if (!ok) @@ -991,7 +1079,8 @@ Expression semanticTraits(TraitsExp e, Scope* sc) doSemantic: // ignore symbol visibility and disable access checks for these traits Scope* scx = sc.push(); - scx.flags |= SCOPE.ignoresymbolvisibility | SCOPE.noaccesscheck; + scx.ignoresymbolvisibility = true; + scx.noAccessCheck = true; scope (exit) scx.pop(); if (e.ident == Id.hasMember) @@ -1013,7 +1102,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) e.ident == Id.getVirtualMethods || e.ident == Id.getOverloads) { - uint errors = global.errors; + const errors = global.errors; Expression eorig = ex; ex = ex.expressionSemantic(scx); if (errors < global.errors) @@ -1245,7 +1334,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) // @@@DEPRECATION 2.100.2 if (auto td = s.isTemplateDeclaration()) { - if (td.overnext || td.overroot) + if (td.overnext) { deprecation(e.loc, "`__traits(getAttributes)` may only be used for individual functions, not the overload set `%s`", td.ident.toChars()); deprecationSupplemental(e.loc, "the result of `__traits(getOverloads)` may be used to select the desired function to extract attributes from"); @@ -1434,7 +1523,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (!fparams.parameters) return ErrorExp.get(); - StorageClass stc; + STC stc; // Set stc to storage class of the ith parameter auto ex = isExpression((*e.args)[1]); @@ -1721,11 +1810,15 @@ Expression semanticTraits(TraitsExp e, Scope* sc) foreach (o; *e.args) { - uint errors = global.startGagging(); + const errors = global.startGagging(); Scope* sc2 = sc.push(); sc2.tinst = null; sc2.minst = null; // this is why code for these are not emitted to object file - sc2.flags = (sc.flags & ~(SCOPE.ctfe | SCOPE.condition)) | SCOPE.compile | SCOPE.fullinst; + sc2.copyFlagsFrom(sc); + sc2.ctfe = false; + sc2.condition = false; + sc2.traitsCompiles = true; + sc2.fullinst = true; bool err = false; @@ -1752,7 +1845,7 @@ Expression semanticTraits(TraitsExp e, Scope* sc) if (sc2.func && sc2.func.type.isTypeFunction()) { const tf = sc2.func.type.isTypeFunction(); - err |= tf.isnothrow && canThrow(ex, sc2.func, null); + err |= tf.isNothrow && canThrow(ex, sc2.func, null); } ex = checkGC(sc2, ex); if (ex.op == EXP.error) @@ -1962,9 +2055,9 @@ Expression semanticTraits(TraitsExp e, Scope* sc) return dimError(1); auto arg0 = (*e.args)[0]; Dsymbol s = getDsymbolWithoutExpCtx(arg0); - if (!s || !s.loc.isValid()) + if (!s || !s.loc.isValid() || s.isModule()) { - error(e.loc, "can only get the location of a symbol, not `%s`", arg0.toChars()); + error(e.loc, "can only get the location of a symbol, not `%s`", s ? s.toPrettyChars() : arg0.toChars()); return ErrorExp.get(); } diff --git a/gcc/d/dmd/typesem.d b/gcc/d/dmd/typesem.d index b2b9e38..d4c7a58 100644 --- a/gcc/d/dmd/typesem.d +++ b/gcc/d/dmd/typesem.d @@ -1,12 +1,12 @@ /** * Semantic analysis for D types. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typesem.d, _typesem.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typesem.d, _typesem.d) * Documentation: https://dlang.org/phobos/dmd_typesem.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typesem.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/typesem.d */ module dmd.typesem; @@ -28,7 +28,6 @@ import dmd.declaration; import dmd.denum; import dmd.dimport; import dmd.dinterpret; -import dmd.dmangle; import dmd.dmodule; import dmd.dscope; import dmd.dstruct; @@ -53,6 +52,8 @@ import dmd.initsem; import dmd.location; import dmd.visitor; import dmd.mtype; +import dmd.mangle; +import dmd.nogc; import dmd.objc; import dmd.opover; import dmd.optimize; @@ -82,7 +83,7 @@ import dmd.tokens; * ps = set if s[oindex] is a Dsymbol, otherwise null * oindex = index into s */ -private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex) +private void resolveTupleIndex(Loc loc, Scope* sc, Dsymbol s, out Expression pe, out Type pt, out Dsymbol ps, RootObject oindex) { auto tup = s.isTupleDeclaration(); @@ -152,7 +153,7 @@ private void resolveTupleIndex(const ref Loc loc, Scope* sc, Dsymbol s, out Expr * ps = set if symbol otherwise null * typeid = set if in TypeidExpression https://dlang.org/spec/expression.html#TypeidExpression */ -private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, +private void resolveHelper(TypeQualified mt, Loc loc, Scope* sc, Dsymbol s, Dsymbol scopesym, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) { version (none) @@ -229,13 +230,13 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb } Type t = s.getType(); // type symbol, type alias, or type tuple? - uint errorsave = global.errors; + const errorsave = global.errors; SearchOptFlags flags = t is null ? SearchOpt.localsOnly : SearchOpt.ignorePrivateImports; Dsymbol sm = s.searchX(loc, sc, id, flags); if (sm) { - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, sm)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, sm)) { .error(loc, "`%s` is not visible from module `%s`", sm.toPrettyChars(), sc._module.toChars()); sm = null; @@ -387,7 +388,7 @@ private void resolveHelper(TypeQualified mt, const ref Loc loc, Scope* sc, Dsymb * Returns: * symbol found, NULL if not */ -private Dsymbol searchX(Dsymbol dsym, const ref Loc loc, Scope* sc, RootObject id, SearchOptFlags flags) +private Dsymbol searchX(Dsymbol dsym, Loc loc, Scope* sc, RootObject id, SearchOptFlags flags) { //printf("Dsymbol::searchX(this=%p,%s, ident='%s')\n", this, toChars(), ident.toChars()); Dsymbol s = dsym.toAlias(); @@ -473,12 +474,12 @@ bool isCopyable(Type t) * Otherwise, when the type has const/inout indirections, returns 1. * * Params: - * isref = if true, check `ref t`; otherwise, check just `t` + * isRef = if true, check `ref t`; otherwise, check just `t` * t = the type that is being checked */ -int mutabilityOfType(bool isref, Type t) +int mutabilityOfType(bool isRef, Type t) { - if (isref) + if (isRef) { if (t.mod & MODFlags.immutable_) return 2; @@ -614,13 +615,13 @@ Expression typeToExpression(Type t) * loc = The source location. * sc = scope of the type */ -extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) +extern (D) bool checkComplexTransition(Type type, Loc loc, Scope* sc) { if (sc.isDeprecated()) return false; // Don't complain if we're inside a template constraint // https://issues.dlang.org/show_bug.cgi?id=21831 - if (sc.flags & SCOPE.constraint) + if (sc.inTemplateConstraint) return false; Type t = type.baseElemOf(); @@ -631,9 +632,9 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) if (t.ty == Tenum && !(cast(TypeEnum)t).sym.memtype) return false; - if (t.isimaginary() || t.iscomplex()) + if (t.isImaginary() || t.isComplex()) { - if (sc.flags & SCOPE.Cfile) + if (sc.inCfile) return true; // complex/imaginary not deprecated in C code Type rt; switch (t.ty) @@ -660,7 +661,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) // Deprecated in 2.097 - Can be made an error from 2.117. // The deprecation period is longer than usual as `cfloat`, // `cdouble`, and `creal` were quite widely used. - if (t.iscomplex()) + if (t.isComplex()) { deprecation(loc, "use of complex type `%s` is deprecated, use `std.complex.Complex!(%s)` instead", type.toChars(), rt.toChars()); @@ -680,6 +681,7 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) * 'args' are being matched to function type 'tf' * Determine match level. * Params: + * fd = function being called, if a symbol * tf = function type * tthis = type of `this` pointer, null if not member function * argumentList = arguments to function call @@ -689,9 +691,10 @@ extern (D) bool checkComplexTransition(Type type, const ref Loc loc, Scope* sc) * Returns: * MATCHxxxx */ -extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentList, int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null) +extern (D) MATCH callMatch(FuncDeclaration fd, TypeFunction tf, Type tthis, ArgumentList argumentList, + int flag = 0, void delegate(const(char)*) scope errorHelper = null, Scope* sc = null) { - //printf("TypeFunction::callMatch() %s\n", tf.toChars()); + //printf("callMatch() fd: %s, tf: %s\n", fd ? fd.ident.toChars() : "null", toChars(tf)); MATCH match = MATCH.exact; // assume exact match ubyte wildmatch = 0; @@ -752,13 +755,14 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis } const(char)* failMessage; const(char)** pMessage = errorHelper ? &failMessage : null; - auto resolvedArgs = tf.resolveNamedArgs(argumentList, pMessage); + OutBuffer buf; + auto resolvedArgs = tf.resolveNamedArgs(argumentList, errorHelper ? &buf : null); Expression[] args; if (!resolvedArgs) { - if (failMessage) + if (buf.length) { - errorHelper(failMessage); + errorHelper(buf.peekChars()); return MATCH.nomatch; } @@ -818,7 +822,12 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis Expression arg = args[u]; if (!arg) continue; // default argument - m = argumentMatchParameter(tf, p, arg, wildmatch, flag, sc, pMessage); + m = argumentMatchParameter(fd, tf, p, arg, wildmatch, flag, sc, pMessage); + if (failMessage) + { + buf.reset(); + buf.writestring(failMessage); + } } else if (p.defaultArg) continue; @@ -835,7 +844,9 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis L1: if (parameterList.varargs == VarArg.typesafe && u + 1 == nparams) // if last varargs param { - auto trailingArgs = args[u .. $]; + Expression[] trailingArgs; + if (args.length >= u) + trailingArgs = args[u .. $]; if (auto vmatch = matchTypeSafeVarArgs(tf, p, trailingArgs, pMessage)) return vmatch < match ? vmatch : match; // Error message was already generated in `matchTypeSafeVarArgs` @@ -843,15 +854,17 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis errorHelper(failMessage); return MATCH.nomatch; } - if (pMessage && u >= args.length) - *pMessage = tf.getMatchError("missing argument for parameter #%d: `%s`", - u + 1, parameterToChars(p, tf, false)); - // If an error happened previously, `pMessage` was already filled - else if (pMessage && !*pMessage) - *pMessage = tf.getParamError(args[u], p); - if (errorHelper) - errorHelper(*pMessage); + { + if (u >= args.length) + TypeFunction.getMatchError(buf, "missing argument for parameter #%d: `%s`", + u + 1, parameterToChars(p, tf, false)); + // If an error happened previously, `pMessage` was already filled + else if (buf.length == 0) + buf.writestring(tf.getParamError(args[u], p)); + + errorHelper(buf.peekChars()); + } return MATCH.nomatch; } if (m < match) @@ -861,7 +874,9 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis if (errorHelper && !parameterList.varargs && args.length > nparams) { // all parameters had a match, but there are surplus args - errorHelper(tf.getMatchError("expected %d argument(s), not %d", nparams, args.length)); + OutBuffer buf2; + TypeFunction.getMatchError(buf2, "expected %d argument(s), not %d", nparams, args.length); + errorHelper(buf2.extractChars()); return MATCH.nomatch; } //printf("match = %d\n", match); @@ -874,14 +889,15 @@ extern (D) MATCH callMatch(TypeFunction tf, Type tthis, ArgumentList argumentLis * * This is done by seeing if a call to the copy constructor can be made: * ``` - * typeof(tprm) __copytmp; - * copytmp.__copyCtor(arg); + * typeof(tprm) __copytemp; + * copytemp.__copyCtor(arg); * ``` */ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, Expression arg, Type tprm, Scope* sc, const(char)** pMessage) { - auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytmp"), null); + //printf("isCopyConstructorCallable() argStruct: %s arg: %s tprm: %s\n", argStruct.toChars(), toChars(arg), toChars(tprm)); + auto tmp = new VarDeclaration(arg.loc, tprm, Identifier.generateId("__copytemp"), null); tmp.storage_class = STC.rvalue | STC.temp | STC.ctfe; tmp.dsymbolSemantic(sc); Expression ve = new VarExp(arg.loc, tmp); @@ -891,66 +907,74 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, if (dmd.expressionsem.trySemantic(e, sc)) return true; - if (pMessage) + if (!pMessage) + return false; + + /* https://issues.dlang.org/show_bug.cgi?id=22202 + * + * If a function was deduced by semantic on the CallExp, + * it means that resolveFuncCall completed succesfully. + * Therefore, there exists a callable copy constructor, + * however, it cannot be called because scope constraints + * such as purity, safety or nogc. + */ + OutBuffer buf; + auto callExp = e.isCallExp(); + + bool nocpctor() { - /* https://issues.dlang.org/show_bug.cgi?id=22202 - * - * If a function was deduced by semantic on the CallExp, - * it means that resolveFuncCall completed succesfully. - * Therefore, there exists a callable copy constructor, - * however, it cannot be called because scope constraints - * such as purity, safety or nogc. - */ - OutBuffer buf; - auto callExp = e.isCallExp(); - if (auto f = callExp.f) - { - char[] s; - if (!f.isPure && sc.func.setImpure()) - s ~= "pure "; - if (!f.isSafe() && !f.isTrusted() && sc.setUnsafe()) - s ~= "@safe "; - if (!f.isNogc && sc.func.setGC(arg.loc, null)) - s ~= "nogc "; - if (f.isDisabled() && !f.isGenerated()) - { - /* https://issues.dlang.org/show_bug.cgi?id=24301 - * Copy constructor is explicitly disabled - */ - buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", - f.type.toChars()); - } - else if (s) - { - s[$-1] = '\0'; - buf.printf("`%s` copy constructor cannot be called from a `%s` context", f.type.toChars(), s.ptr); - } - else if (f.isGenerated() && f.isDisabled()) - { - /* https://issues.dlang.org/show_bug.cgi?id=23097 - * Compiler generated copy constructor failed. - */ - buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", - argStruct.toChars()); - } - else - { - /* Although a copy constructor may exist, no suitable match was found. - * i.e: `inout` constructor creates `const` object, not mutable. - * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. - */ - goto Lnocpctor; - } - } - else - { - Lnocpctor: - buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", - argStruct.toChars(), arg.type.toChars(), tprm.toChars()); - } + buf.printf("`struct %s` does not define a copy constructor for `%s` to `%s` copies", + argStruct.toChars(), arg.type.toChars(), tprm.toChars()); + *pMessage = buf.extractChars(); + return false; + } + auto f = callExp.f; + if (!f) + return nocpctor(); + + if (f.isDisabled() && !f.isGenerated()) + { + /* https://issues.dlang.org/show_bug.cgi?id=24301 + * Copy constructor is explicitly disabled + */ + buf.printf("`%s` copy constructor cannot be used because it is annotated with `@disable`", + f.type.toChars()); *pMessage = buf.extractChars(); + return false; } + + bool bpure = !f.isPure && sc.func.setImpure(arg.loc, null); + bool bsafe = !f.isSafe() && !f.isTrusted() && sc.setUnsafe(false, arg.loc, null); + bool bnogc = !f.isNogc && sc.func.setGC(arg.loc, null); + if (bpure | bsafe | bnogc) + { + const nullptr = "".ptr; + buf.printf("`%s` copy constructor cannot be called from a `%s%s%s` context", + f.type.toChars(), + bpure ? "pure " .ptr : nullptr, + bsafe ? "@safe ".ptr : nullptr, + bnogc ? "nogc" .ptr : nullptr); + } + else if (f.isGenerated() && f.isDisabled()) + { + /* https://issues.dlang.org/show_bug.cgi?id=23097 + * Compiler generated copy constructor failed. + */ + buf.printf("generating a copy constructor for `struct %s` failed, therefore instances of it are uncopyable", + argStruct.toChars()); + } + else + { + /* Although a copy constructor may exist, no suitable match was found. + * i.e: `inout` constructor creates `const` object, not mutable. + * Fallback to using the original generic error before https://issues.dlang.org/show_bug.cgi?id=22202. + */ + return nocpctor(); + } + + *pMessage = buf.extractChars(); + return false; } @@ -959,25 +983,28 @@ private extern(D) bool isCopyConstructorCallable (StructDeclaration argStruct, * * This function is called by `TypeFunction.callMatch` while iterating over * the list of parameter. Here we check if `arg` is a match for `p`, - * which is mostly about checking if `arg.type` converts to `p`'s type + * which is mostly about checking if `arg.type` converts to type of `p` * and some check about value reference. * * Params: + * fd = the function being called if symbol, null if not * tf = The `TypeFunction`, only used for error reporting * p = The parameter of `tf` being matched * arg = Argument being passed (bound) to `p` * wildmatch = Wild (`inout`) matching level, derived from the full argument list - * flag = A non-zero value means we're doing a partial ordering check + * flag = A non-zero value means we are doing a partial ordering check * (no value semantic check) * sc = Scope we are in * pMessage = A buffer to write the error in, or `null` * * Returns: Whether `trailingArgs` match `p`. */ -private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, +private extern(D) MATCH argumentMatchParameter (FuncDeclaration fd, TypeFunction tf, Parameter p, Expression arg, ubyte wildmatch, int flag, Scope* sc, const(char)** pMessage) { - //printf("arg: %s, type: %s\n", arg.toChars(), arg.type.toChars()); + static if (0) + printf("argumentMatchParameter() sc: %p, fd: %s, tf: %s, p: %s, arg: %s, arg.type: %s\n", + sc, fd ? fd.ident.toChars() : "null", tf.toChars(), parameterToChars(p, tf, false), arg.toChars(), arg.type.toChars()); MATCH m; Type targ = arg.type; Type tprm = wildmatch ? p.type.substWildTo(wildmatch) : p.type; @@ -992,18 +1019,47 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, else { const isRef = p.isReference(); - StructDeclaration argStruct, prmStruct; - // first look for a copy constructor - if (arg.isLvalue() && !isRef && targ.ty == Tstruct && tprm.ty == Tstruct) + StructDeclaration argStruct, prmStruct; + if (targ.ty == Tstruct && tprm.ty == Tstruct) { // if the argument and the parameter are of the same unqualified struct type argStruct = (cast(TypeStruct)targ).sym; prmStruct = (cast(TypeStruct)tprm).sym; + + /* if both a copy constructor and move constructor exist, then match + * the lvalue to the copy constructor only and the rvalue to the move constructor + * only + */ + if (argStruct == prmStruct && fd) + { + if (auto cfd = fd.isCtorDeclaration()) + { + /* Get struct that constructor is making + */ + + auto t1 = cfd.type.toBasetype(); + auto t2 = t1.nextOf(); + auto t3 = t2.isTypeStruct(); + if (t3) + { + auto ctorStruct = t3.sym; +// StructDeclaration ctorStruct = cfd.type.toBasetype().nextOf().isTypeStruct().sym; + + if (prmStruct == ctorStruct && ctorStruct.hasCopyCtor && ctorStruct.hasMoveCtor) + { + if (cfd.isCpCtor && !arg.isLvalue()) + return MATCH.nomatch; // copy constructor is only for lvalues + if (cfd.isMoveCtor && arg.isLvalue()) + return MATCH.nomatch; // move constructor is only for rvalues + } + } + } + } } // check if the copy constructor may be called to copy the argument - if (argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) + if (arg.isLvalue() && !isRef && argStruct && argStruct == prmStruct && argStruct.hasCopyCtor) { if (!isCopyConstructorCallable(argStruct, arg, tprm, sc, pMessage)) return MATCH.nomatch; @@ -1012,106 +1068,107 @@ private extern(D) MATCH argumentMatchParameter (TypeFunction tf, Parameter p, else { import dmd.dcast : cimplicitConvTo; - m = (sc && sc.flags & SCOPE.Cfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); + m = (sc && sc.inCfile) ? arg.cimplicitConvTo(tprm) : arg.implicitConvTo(tprm); } } // Non-lvalues do not match ref or out parameters - if (p.isReference()) - { - // https://issues.dlang.org/show_bug.cgi?id=13783 - // Don't use toBasetype() to handle enum types. - Type ta = targ; - Type tp = tprm; - //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); + if (!p.isReference()) + return m; - if (m && !arg.isLvalue()) - { - if (p.storageClass & STC.out_) - { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; - } + // https://issues.dlang.org/show_bug.cgi?id=13783 + // Don't use toBasetype() to handle enum types. + Type ta = targ; + Type tp = tprm; + //printf("fparam[%d] ta = %s, tp = %s\n", u, ta.toChars(), tp.toChars()); - if (arg.op == EXP.string_ && tp.ty == Tsarray) - { - if (ta.ty != Tsarray) - { - Type tn = tp.nextOf().castMod(ta.nextOf().mod); - dinteger_t dim = (cast(StringExp)arg).len; - ta = tn.sarrayOf(dim); - } - } - else if (arg.op == EXP.slice && tp.ty == Tsarray) - { - // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] - if (ta.ty != Tsarray) - { - Type tn = ta.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } - } - else if (p.storageClass & STC.constscoperef) - { - // Allow converting a literal to an `in` which is `ref` - if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) - { - Type tn = tp.nextOf(); - dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); - ta = tn.sarrayOf(dim); - } + if (m && !arg.isLvalue()) + { + if (p.storageClass & STC.out_) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } - // Need to make this a rvalue through a temporary - m = MATCH.convert; - } - else if (global.params.rvalueRefParam != FeatureState.enabled || - p.storageClass & STC.out_ || - !arg.type.isCopyable()) // can't copy to temp for ref parameter + if (arg.op == EXP.string_ && tp.ty == Tsarray) + { + if (ta.ty != Tsarray) { - if (pMessage) *pMessage = tf.getParamError(arg, p); - return MATCH.nomatch; + Type tn = tp.nextOf().castMod(ta.nextOf().mod); + dinteger_t dim = (cast(StringExp)arg).len; + ta = tn.sarrayOf(dim); } - else + } + else if (arg.op == EXP.slice && tp.ty == Tsarray) + { + // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] + if (ta.ty != Tsarray) { - /* in functionParameters() we'll convert this - * rvalue into a temporary - */ - m = MATCH.convert; + Type tn = ta.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); } } - - /* If the match is not already perfect or if the arg - is not a lvalue then try the `alias this` chain - see https://issues.dlang.org/show_bug.cgi?id=15674 - and https://issues.dlang.org/show_bug.cgi?id=21905 - */ - if (ta != tp || !arg.isLvalue()) + else if (p.storageClass & STC.constscoperef) { - Type firsttab = ta.toBasetype(); - while (1) + // Allow converting a literal to an `in` which is `ref` + if (arg.op == EXP.arrayLiteral && tp.ty == Tsarray) { - Type tab = ta.toBasetype(); - Type tat = tab.aliasthisOf(); - if (!tat || !tat.implicitConvTo(tprm)) - break; - if (tat == tab || tat == firsttab) - break; - ta = tat; + Type tn = tp.nextOf(); + dinteger_t dim = (cast(TypeSArray)tp).dim.toUInteger(); + ta = tn.sarrayOf(dim); } - } - /* A ref variable should work like a head-const reference. - * e.g. disallows: - * ref T <- an lvalue of const(T) argument - * ref T[dim] <- an lvalue of const(T[dim]) argument - */ - if (!ta.constConv(tp)) + // Need to make this a rvalue through a temporary + m = MATCH.convert; + } + else if (!(sc && sc.previews.rvalueRefParam) || + p.storageClass & STC.out_ || + !arg.type.isCopyable()) // can't copy to temp for ref parameter { if (pMessage) *pMessage = tf.getParamError(arg, p); return MATCH.nomatch; } + else + { + /* in functionParameters() we'll convert this + * rvalue into a temporary + */ + m = MATCH.convert; + } } + + /* If the match is not already perfect or if the arg + is not a lvalue then try the `alias this` chain + see https://issues.dlang.org/show_bug.cgi?id=15674 + and https://issues.dlang.org/show_bug.cgi?id=21905 + */ + if (ta != tp || !arg.isLvalue()) + { + Type firsttab = ta.toBasetype(); + while (1) + { + Type tab = ta.toBasetype(); + Type tat = tab.aliasthisOf(); + if (!tat || !tat.implicitConvTo(tprm)) + break; + if (tat == tab || tat == firsttab) + break; + ta = tat; + } + } + + /* A ref variable should work like a head-const reference. + * e.g. disallows: + * ref T <- an lvalue of const(T) argument + * ref T[dim] <- an lvalue of const(T[dim]) argument + */ + if (!ta.constConv(tp)) + { + if (pMessage) *pMessage = tf.getParamError(arg, p); + return MATCH.nomatch; + } + return m; } @@ -1130,7 +1187,7 @@ private const(char)* getParamError(TypeFunction tf, Expression arg, Parameter pa // only mention rvalue if it's relevant const rv = !arg.isLvalue() && par.isReference(); buf.printf("cannot pass %sargument `%s` of type `%s` to parameter `%s`", - rv ? "rvalue ".ptr : "".ptr, arg.toChars(), at, + rv ? "rvalue ".ptr : "".ptr, arg.toErrMsg(), at, parameterToChars(par, tf, qual)); return buf.extractChars(); } @@ -1162,8 +1219,12 @@ private extern(D) MATCH matchTypeSafeVarArgs(TypeFunction tf, Parameter p, if (sz != trailingArgs.length) { if (pMessage) - *pMessage = tf.getMatchError("expected %llu variadic argument(s), not %zu", + { + OutBuffer buf; + TypeFunction.getMatchError(buf, "expected %llu variadic argument(s), not %zu", sz, trailingArgs.length); + *pMessage = buf.extractChars(); + } return MATCH.nomatch; } goto case Tarray; @@ -1282,6 +1343,326 @@ bool hasPointers(Type t) } } +/************************************** + * Returns an indirect type one step from t. + */ +Type getIndirection(Type t) +{ + t = t.baseElemOf(); + if (t.ty == Tarray || t.ty == Tpointer) + return t.nextOf().toBasetype(); + if (t.ty == Taarray || t.ty == Tclass) + return t; + if (t.ty == Tstruct) + return t.hasPointers() ? t : null; // TODO + + // should consider TypeDelegate? + return null; +} + +uinteger_t size(Type t) +{ + return size(t, Loc.initial); +} + +uinteger_t size(Type t, Loc loc) +{ + + uinteger_t visitType(Type t) + { + error(loc, "no size for type `%s`", t.toChars()); + return SIZE_INVALID; + } + + uinteger_t visitBasic(TypeBasic t) + { + uint size; + //printf("TypeBasic::size()\n"); + switch (t.ty) + { + case Tint8: + case Tuns8: + size = 1; + break; + + case Tint16: + case Tuns16: + size = 2; + break; + + case Tint32: + case Tuns32: + case Tfloat32: + case Timaginary32: + size = 4; + break; + + case Tint64: + case Tuns64: + case Tfloat64: + case Timaginary64: + size = 8; + break; + + case Tfloat80: + case Timaginary80: + size = target.realsize; + break; + + case Tcomplex32: + size = 8; + break; + + case Tcomplex64: + case Tint128: + case Tuns128: + size = 16; + break; + + case Tcomplex80: + size = target.realsize * 2; + break; + + case Tvoid: + //size = Type::size(); // error message + size = 1; + break; + + case Tbool: + size = 1; + break; + + case Tchar: + size = 1; + break; + + case Twchar: + size = 2; + break; + + case Tdchar: + size = 4; + break; + + default: + assert(0); + } + + //printf("TypeBasic::size() = %d\n", size); + return size; + } + + uinteger_t visitSArray(TypeSArray t) + { + //printf("TypeSArray::size()\n"); + const n = t.numberOfElems(loc); + const elemsize = t.baseElemOf().size(loc); + bool overflow = false; + const sz = mulu(n, elemsize, overflow); + if (overflow || sz >= uint.max) + { + if (elemsize != SIZE_INVALID && n != uint.max) + error(loc, "static array `%s` size overflowed to %lld", t.toChars(), cast(long)sz); + return SIZE_INVALID; + } + return sz; + + } + + uinteger_t visitTypeQualified(TypeQualified t) + { + if (t.ty == Ttypeof) + { + auto type = (cast(TypeTypeof)t).exp.type; + if (type) + return type.size(loc); + } + + error(t.loc, "size of type `%s` is not known", t.toChars()); + return SIZE_INVALID; + } + + switch(t.ty) + { + default: return t.isTypeBasic() ? visitBasic(t.isTypeBasic()) : visitType(t); + case Ttraits: + case Terror: return SIZE_INVALID; + case Tvector: return t.isTypeVector().basetype.size(); + case Tsarray: return visitSArray(t.isTypeSArray()); + case Tdelegate: + case Tarray: return target.ptrsize * 2; + case Tpointer: + case Treference: + case Tclass: + case Taarray: return target.ptrsize; + case Tident: + case Tinstance: + case Ttypeof: + case Treturn: return visitTypeQualified(cast(TypeQualified)t); + case Tstruct: return t.isTypeStruct().sym.size(loc); + case Tenum: return t.isTypeEnum().sym.getMemtype(loc).size(loc); + case Tnull: return t.tvoidptr.size(loc); + case Tnoreturn: return 0; + } +} + +/******************************* + * Determine if converting 'this' to 'to' is an identity operation, + * a conversion to const operation, or the types aren't the same. + * Returns: + * MATCH.exact 'this' == 'to' + * MATCH.constant 'to' is const + * MATCH.nomatch conversion to mutable or invariant + */ +MATCH constConv(Type from, Type to) +{ + MATCH visitType(Type from) + { + //printf("Type::constConv(this = %s, to = %s)\n", from.toChars(), to.toChars()); + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitNext(TypeNext from) + { + //printf("TypeNext::constConv from = %s, to = %s\n", from.toChars(), to.toChars()); + if (from.equals(to)) + return MATCH.exact; + + if (!(from.ty == to.ty && MODimplicitConv(from.mod, to.mod))) + return MATCH.nomatch; + + Type tn = to.nextOf(); + if (!(tn && from.next.ty == tn.ty)) + return MATCH.nomatch; + + MATCH m; + if (to.isConst()) // whole tail const conversion + { + // Recursive shared level check + m = from.next.constConv(tn); + if (m == MATCH.exact) + m = MATCH.constant; + } + else + { + //printf("\tnext => %s, to.next => %s\n", from.next.toChars(), tn.toChars()); + m = from.next.equals(tn) ? MATCH.constant : MATCH.nomatch; + } + return m; + } + + MATCH visitSArray(TypeSArray from) + { + if (auto tsa = to.isTypeSArray()) + { + if (!from.dim.equals(tsa.dim)) + return MATCH.nomatch; + } + return visitNext(from); + } + + MATCH visitAArray(TypeAArray from) + { + if (auto taa = to.isTypeAArray()) + { + MATCH mindex = from.index.constConv(taa.index); + MATCH mkey = from.next.constConv(taa.next); + // Pick the worst match + return mkey < mindex ? mkey : mindex; + } + return visitType(from); + } + + MATCH visitPointer(TypePointer from) + { + if (from.next.ty == Tfunction) + { + if (to.nextOf() && from.next.equals((cast(TypeNext)to).next)) + return visitType(from); + else + return MATCH.nomatch; + } + return visitNext(from); + } + + MATCH visitFunction(TypeFunction from) + { + // Attributes need to match exactly, otherwise it's an implicit conversion + if (from.ty != to.ty || !from.attributesEqual(cast(TypeFunction) to)) + return MATCH.nomatch; + + return visitNext(from); + } + + MATCH visitStruct(TypeStruct from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeStruct)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitEnum(TypeEnum from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeEnum)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + return MATCH.nomatch; + } + + MATCH visitClass(TypeClass from) + { + if (from.equals(to)) + return MATCH.exact; + if (from.ty == to.ty && from.sym == (cast(TypeClass)to).sym && MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + + /* Conversion derived to const(base) + */ + int offset = 0; + if (to.isBaseOf(from, &offset) && offset == 0 && MODimplicitConv(from.mod, to.mod)) + { + // Disallow: + // derived to base + // inout(derived) to inout(base) + if (!to.isMutable() && !to.isWild()) + return MATCH.convert; + } + + return MATCH.nomatch; + } + + MATCH visitNoreturn(TypeNoreturn from) + { + // Either another noreturn or conversion to any type + return from.implicitConvTo(to); + } + + switch(from.ty) + { + default: return visitType(from); + case Tsarray: return visitSArray(from.isTypeSArray()); + case Taarray: return visitAArray(from.isTypeAArray()); + case Treference: + case Tdelegate: + case Tslice: + case Tarray: return visitNext(cast(TypeNext)from); + case Tpointer: return visitPointer(from.isTypePointer()); + case Tfunction: return visitFunction(from.isTypeFunction()); + case Tstruct: return visitStruct(from.isTypeStruct()); + case Tenum: return visitEnum(from.isTypeEnum()); + case Tclass: return visitClass(from.isTypeClass()); + case Tnoreturn: return visitNoreturn(from.isTypeNoreturn()); + } +} + + /****************************************** * Perform semantic analysis on a type. * Params: @@ -1292,7 +1673,7 @@ bool hasPointers(Type t) * `Type` with completed semantic analysis, `Terror` if errors * were encountered */ -Type typeSemantic(Type type, const ref Loc loc, Scope* sc) +Type typeSemantic(Type type, Loc loc, Scope* sc) { static Type error() { @@ -1316,7 +1697,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) Type visitComplex(TypeBasic t) { - if (!(sc.flags & SCOPE.Cfile)) + if (!sc.inCfile) return visitType(t); auto tc = getComplexLibraryType(loc, sc, t.ty); @@ -1490,7 +1871,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (tbn.isscope()) + if (tbn.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", tbn.toChars()); return error(); @@ -1524,7 +1905,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (tn.isscope()) + if (tn.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", tn.toChars()); return error(); @@ -1644,7 +2025,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } else if (sd.xeq == sd.xerreq) { - if (search_function(sd, Id.eq)) + if (search_function(sd, Id.opEquals)) { .error(loc, "%sAA key type `%s` does not have `bool opEquals(ref const %s) const`", s, sd.toChars(), sd.toChars()); } @@ -1656,7 +2037,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } else if (!sd.xhash) { - if (search_function(sd, Id.eq)) + if (search_function(sd, Id.opEquals)) { .error(loc, "%sAA key type `%s` should have `extern (D) size_t toHash() const nothrow @safe` if `opEquals` defined", s, sd.toChars()); } @@ -1686,7 +2067,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (!ClassDeclaration.object) { - .error(Loc.initial, "missing or corrupt object.d"); + ObjectNotFound(Loc.initial, cd.ident); return error(); } @@ -1694,9 +2075,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) __gshared FuncDeclaration fcmp = null; __gshared FuncDeclaration fhash = null; if (!feq) - feq = search_function(ClassDeclaration.object, Id.eq).isFuncDeclaration(); + feq = search_function(ClassDeclaration.object, Id.opEquals).isFuncDeclaration(); if (!fcmp) - fcmp = search_function(ClassDeclaration.object, Id.cmp).isFuncDeclaration(); + fcmp = search_function(ClassDeclaration.object, Id.opCmp).isFuncDeclaration(); if (!fhash) fhash = search_function(ClassDeclaration.object, Id.tohash).isFuncDeclaration(); assert(fcmp && feq && fhash); @@ -1730,7 +2111,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) default: break; } - if (mtype.next.isscope()) + if (mtype.next.isScopeClass()) { .error(loc, "cannot have array of scope `%s`", mtype.next.toChars()); return error(); @@ -1830,23 +2211,23 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (sc.stc & STC.pure_) tf.purity = PURE.fwdref; if (sc.stc & STC.nothrow_) - tf.isnothrow = true; + tf.isNothrow = true; if (sc.stc & STC.nogc) - tf.isnogc = true; + tf.isNogc = true; if (sc.stc & STC.ref_) - tf.isref = true; + tf.isRef = true; if (sc.stc & STC.return_) - tf.isreturn = true; + tf.isReturn = true; if (sc.stc & STC.returnScope) - tf.isreturnscope = true; + tf.isReturnScope = true; if (sc.stc & STC.returninferred) - tf.isreturninferred = true; + tf.isReturnInferred = true; if (sc.stc & STC.scope_) tf.isScopeQual = true; if (sc.stc & STC.scopeinferred) - tf.isscopeinferred = true; + tf.isScopeInferred = true; -// if (tf.isreturn && !tf.isref) +// if (tf.isReturn && !tf.isRef) // tf.isScopeQual = true; // return by itself means 'return scope' if (tf.trust == TRUST.default_) @@ -1860,9 +2241,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } if (sc.stc & STC.property) - tf.isproperty = true; + tf.isProperty = true; if (sc.stc & STC.live) - tf.islive = true; + tf.isLive = true; tf.linkage = sc.linkage; if (tf.linkage == LINK.system) @@ -1896,7 +2277,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) tf.next = tf.next.typeSemantic(loc, sc); sc = sc.pop(); errors |= tf.checkRetType(loc); - if (tf.next.isscope() && !tf.isctor) + if (tf.next.isScopeClass() && !tf.isCtor) { .error(loc, "functions cannot return `scope %s`", tf.next.toChars()); errors = true; @@ -1904,9 +2285,9 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (tf.next.hasWild()) wildreturn = true; - if (tf.isreturn && !tf.isref && !tf.next.hasPointers()) + if (tf.isReturn && !tf.isRef && !tf.next.hasPointers()) { - tf.isreturn = false; + tf.isReturn = false; } } @@ -1948,14 +2329,13 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { const(char)* errTxt = fparam.storageClass & STC.ref_ ? "ref" : "out"; .error(e.loc, "expression `%s` of type `%s` is not implicitly convertible to type `%s %s` of parameter `%s`", - e.toChars(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars()); + e.toErrMsg(), e.type.toChars(), errTxt, fparam.type.toChars(), fparam.toChars()); } e = e.implicitCastTo(sc, fparam.type); // default arg must be an lvalue if (isRefOrOut && !isAuto && - !(fparam.storageClass & STC.constscoperef) && - global.params.rvalueRefParam != FeatureState.enabled) + !(fparam.storageClass & STC.constscoperef) && !sc.previews.rvalueRefParam) e = e.toLvalue(sc, "create default argument for `ref` / `out` parameter from"); fparam.defaultArg = e; @@ -1968,7 +2348,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) /* Create a scope for evaluating the default arguments for the parameters */ Scope* argsc = sc.push(); - argsc.stc = 0; // don't inherit storage class + argsc.stc = STC.none; // don't inherit storage class argsc.visibility = Visibility(Visibility.Kind.public_); argsc.func = null; @@ -2024,12 +2404,12 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) // https://issues.dlang.org/show_bug.cgi?id=12744 // If the storage classes of narg // conflict with the ones in fparam, it's ignored. - StorageClass stc = fparam.storageClass | narg.storageClass; - StorageClass stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); - StorageClass stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); + STC stc = fparam.storageClass | narg.storageClass; + STC stc1 = fparam.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); + STC stc2 = narg.storageClass & (STC.ref_ | STC.out_ | STC.lazy_); if (stc1 && stc2 && stc1 != stc2) { - OutBuffer buf1; stcToBuffer(buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : 0)); + OutBuffer buf1; stcToBuffer(buf1, stc1 | ((stc1 & STC.ref_) ? (fparam.storageClass & STC.auto_) : STC.none)); OutBuffer buf2; stcToBuffer(buf2, stc2); .error(loc, "incompatible parameter storage classes `%s` and `%s`", @@ -2113,10 +2493,17 @@ 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) + const bool isTypesafeVariadic = i + 1 == dim && tf.parameterList.varargs == VarArg.typesafe; + const bool isStackAllocatedVariadic = isTypesafeVariadic && (t.isTypeDArray() || t.isTypeClass()); + + if (isTypesafeVariadic && t.isTypeClass()) + { + // Deprecated in 2.111, kept as a legacy feature for compatibility (currently no plan to turn it into an error) + .deprecation(loc, "typesafe variadic parameters with a `class` type (`%s %s...`) are deprecated", + t.isTypeClass().sym.ident.toChars(), fparam.toChars()); + } + + if (isStackAllocatedVariadic) { /* typesafe variadic arguments are constructed on the stack, so must be `scope` */ @@ -2129,7 +2516,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { if (!(fparam.storageClass & STC.scope_)) fparam.storageClass |= STC.scope_ | STC.scopeinferred; // 'return' implies 'scope' - if (tf.isref) + if (tf.isRef) { } else if (tf.next && !tf.next.hasPointers() && tf.next.toBasetype().ty != Tvoid) @@ -2138,7 +2525,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } } - if (isTypesafeVariadic) + if (isStackAllocatedVariadic) { /* This is because they can be constructed on the stack * https://dlang.org/spec/function.html#typesafe_variadic_functions @@ -2287,14 +2674,14 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) tf.isInOutParam = (wildparams & 1) != 0; tf.isInOutQual = (wildparams & 2) != 0; - if (tf.isproperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2)) + if (tf.isProperty && (tf.parameterList.varargs != VarArg.none || tf.parameterList.length > 2)) { .error(loc, "properties can only have zero, one, or two parameter"); errors = true; } if (tf.parameterList.varargs == VarArg.variadic && tf.linkage != LINK.d && tf.parameterList.length == 0 && - !(sc.flags & SCOPE.Cfile)) + !sc.inCfile) { .error(loc, "variadic functions with non-D linkage must have at least one parameter"); errors = true; @@ -2356,53 +2743,49 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) //printf("\tit's a type %d, %s, %s\n", t.ty, t.toChars(), t.deco); return t.addMod(mtype.mod); } - else - { - if (s) - { - auto td = s.isTemplateDeclaration; - if (td && td.onemember && td.onemember.isAggregateDeclaration) - .error(loc, "template %s `%s` is used as a type without instantiation" - ~ "; to instantiate it use `%s!(arguments)`", - s.kind, s.toPrettyChars, s.ident.toChars); - else - .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); - //assert(0); - } - else if (e.op == EXP.variable) // special case: variable is used as a type - { - /* - N.B. This branch currently triggers for the following code - template test(x* x) - { - } - i.e. the compiler prints "variable x is used as a type" - which isn't a particularly good error message (x is a variable?). - */ - Dsymbol varDecl = mtype.toDsymbol(sc); - const(Loc) varDeclLoc = varDecl.getLoc(); - Module varDeclModule = varDecl.getModule(); //This can be null - - .error(loc, "variable `%s` is used as a type", mtype.toChars()); - //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 - if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported + if (s) + { + auto td = s.isTemplateDeclaration; + if (td && td.onemember && td.onemember.isAggregateDeclaration) + .error(loc, "template %s `%s` is used as a type without instantiation" + ~ "; to instantiate it use `%s!(arguments)`", + s.kind, s.toPrettyChars, s.ident.toChars); + else + .error(loc, "%s `%s` is used as a type", s.kind, s.toPrettyChars); + //assert(0); + } + else if (e.op == EXP.variable) // special case: variable is used as a type + { + /* + N.B. This branch currently triggers for the following code + template test(x* x) { - const(Loc) varDeclModuleImportLoc = varDeclModule.getLoc(); - .errorSupplemental( - varDeclModuleImportLoc, - "variable `%s` is imported here from: `%s`", - varDecl.toChars, - varDeclModule.toPrettyChars, - ); + } + i.e. the compiler prints "variable x is used as a type" + which isn't a particularly good error message (x is a variable?). + */ + Dsymbol varDecl = mtype.toDsymbol(sc); + Module varDeclModule = varDecl.getModule(); //This can be null - .errorSupplemental(varDeclLoc, "variable `%s` is declared here", varDecl.toChars); + .error(loc, "variable `%s` is used as a type", mtype.toChars()); + //Check for null to avoid https://issues.dlang.org/show_bug.cgi?id=22574 + if ((varDeclModule !is null) && varDeclModule != sc._module) // variable is imported + { + .errorSupplemental( + varDeclModule.loc, + "variable `%s` is imported here from: `%s`", + varDecl.toChars, + varDeclModule.toPrettyChars, + ); } - else - .error(loc, "`%s` is used as a type", mtype.toChars()); - return error(); + + .errorSupplemental(varDecl.loc, "variable `%s` is declared here", varDecl.toChars); } + else + .error(loc, "`%s` is used as a type", mtype.toChars()); + return error(); } Type visitInstance(TypeInstance mtype) @@ -2602,8 +2985,14 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) Type visitTag(TypeTag mtype) { //printf("TypeTag.semantic() %s\n", mtype.toChars()); - Type returnType(Type t) - { + Type returnType(TypeTag tt) + { + Type t = tt.resolved; + // To make const checking work, the const STC needs to be added: + // t = t.resolved.addSTC(mtype.mod.ModToStc); + // However, this currently fails compilable/test22875.i + // Apparently there's some aliasing going on, where mutable + // versions of the type also get const applied to them. return t.deco ? t : t.merge(); } @@ -2611,7 +3000,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { /* struct S s, *p; */ - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } /* Find the current scope by skipping tag scopes. @@ -2684,7 +3073,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) { mtype.id = Identifier.generateId("__tag"[]); declareTag(); - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } /* look for pre-existing declaration @@ -2697,7 +3086,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) if (mtype.tok == TOK.enum_ && !mtype.members) .error(mtype.loc, "`enum %s` is incomplete without members", mtype.id.toChars()); // C11 6.7.2.3-3 declareTag(); - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } /* A redeclaration only happens if both declarations are in @@ -2752,7 +3141,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) else { /* struct S { int a; }; - * struct S *s; + * struct S* s; */ } mtype.resolved = sd.type; @@ -2783,21 +3172,21 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) mtype.tok == TOK.struct_ && s.isStructDeclaration()) { /* struct S; - * { struct S *s; } + * { struct S* s; } */ mtype.resolved = s.isStructDeclaration().type; } else { /* union S; - * { struct S *s; } + * { struct S* s; } */ .error(mtype.loc, "redeclaring `%s %s` as `%s %s`", s.kind(), s.toChars(), Token.toChars(mtype.tok), mtype.id.toChars()); declareTag(); } } - return returnType(mtype.resolved.addSTC(mtype.mod)); + return returnType(mtype); } switch (type.ty) @@ -2829,7 +3218,7 @@ Type typeSemantic(Type type, const ref Loc loc, Scope* sc) } } -Type trySemantic(Type type, const ref Loc loc, Scope* sc) +Type trySemantic(Type type, Loc loc, Scope* sc) { //printf("+trySemantic(%s) %d\n", toChars(), global.errors); @@ -2877,9 +3266,19 @@ Type merge(Type type) case Tsarray: // prevents generating the mangle if the array dim is not yet known - if (!type.isTypeSArray().dim.isIntegerExp()) - return type; - goto default; + if (auto ie = type.isTypeSArray().dim.isIntegerExp()) + { + // After TypeSemantic, the length is always converted to size_t, but the parser + // usually generates regular integer types (e.g. in cast(const ubyte[2])) which + // it may try to merge, which then leads to failing implicit conversions as 2LU != 2 + // according to Expression.equals. Only merge array types with size_t lengths for now. + // https://github.com/dlang/dmd/issues/21179 + if (ie.type != Type.tsize_t) + return type; + + goto default; + } + return type; case Tenum: break; @@ -2896,37 +3295,36 @@ Type merge(Type type) } //printf("merge(%s)\n", toChars()); - if (!type.deco) - { - OutBuffer buf; - buf.reserve(32); + if (type.deco) + return type; - mangleToBuffer(type, buf); + OutBuffer buf; + buf.reserve(32); - auto sv = type.stringtable.update(buf[]); - if (sv.value) - { - Type t = sv.value; - debug - { - import core.stdc.stdio; - if (!t.deco) - printf("t = %s\n", t.toChars()); - } - assert(t.deco); - //printf("old value, deco = '%s' %p\n", t.deco, t.deco); - return t; - } - else + mangleToBuffer(type, buf); + + auto sv = type.stringtable.update(buf[]); + if (sv.value) + { + Type t = sv.value; + debug { - Type t = stripDefaultArgs(type); - sv.value = t; - type.deco = t.deco = cast(char*)sv.toDchars(); - //printf("new value, deco = '%s' %p\n", t.deco, t.deco); - return t; + import core.stdc.stdio; + if (!t.deco) + printf("t = %s\n", t.toChars()); } + assert(t.deco); + //printf("old value, deco = '%s' %p\n", t.deco, t.deco); + return t; + } + else + { + Type t = stripDefaultArgs(type); + sv.value = t; + type.deco = t.deco = cast(char*)sv.toDchars(); + //printf("new value, deco = '%s' %p\n", t.deco, t.deco); + return t; } - return type; } /************************************* @@ -2965,7 +3363,7 @@ Type merge2(Type type) * Returns: * expression representing the property, or null if not a property and (flag & 1) */ -Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier ident, int flag, +Expression getProperty(Type t, Scope* scope_, Loc loc, Identifier ident, int flag, Expression src = null) { Expression visitType(Type mt) @@ -2980,14 +3378,14 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden const sz = mt.size(loc); if (sz == SIZE_INVALID) return ErrorExp.get(); - e = new IntegerExp(loc, sz, Type.tsize_t); + return new IntegerExp(loc, sz, Type.tsize_t); } else if (ident == Id.__xalignof) { const explicitAlignment = mt.alignment(); const naturalAlignment = mt.alignsize(); const actualAlignment = (explicitAlignment.isDefault() ? naturalAlignment : explicitAlignment.get()); - e = new IntegerExp(loc, actualAlignment, Type.tsize_t); + return new IntegerExp(loc, actualAlignment, Type.tsize_t); } else if (ident == Id._init) { @@ -2997,13 +3395,14 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden { e.isStructLiteralExp().useStaticInit = true; } + return e; } else if (ident == Id._mangleof) { if (!mt.deco) { error(loc, "forward reference of type `%s.mangleof`", mt.toChars()); - e = ErrorExp.get(); + return ErrorExp.get(); } else { @@ -3012,6 +3411,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden sc.eSink = global.errorSink; e = e.expressionSemantic(&sc); } + return e; } else if (ident == Id.stringof) { @@ -3020,53 +3420,98 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden Scope sc; sc.eSink = global.errorSink; e = e.expressionSemantic(&sc); + return e; } else if (flag && mt != Type.terror) { return null; } + + Dsymbol s = null; + if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) + s = mt.toDsymbol(null); + if (s) + s = s.search_correct(ident); + if (s && !symbolIsVisible(scope_, s)) + s = null; + + if (mt == Type.terror) + return ErrorExp.get(); + + if (s) + error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); + else if (ident == Id.opCall && mt.ty == Tclass) + error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); + + else if (const n = importHint(ident.toString())) + error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); else { - Dsymbol s = null; - if (mt.ty == Tstruct || mt.ty == Tclass || mt.ty == Tenum) - s = mt.toDsymbol(null); - if (s) - s = s.search_correct(ident); - if (s && !symbolIsVisible(scope_, s)) - s = null; - if (mt != Type.terror) - { - if (s) - error(loc, "no property `%s` for type `%s`, did you mean `%s`?", ident.toChars(), mt.toChars(), s.toPrettyChars()); - else if (ident == Id.call && mt.ty == Tclass) - error(loc, "no property `%s` for type `%s`, did you mean `new %s`?", ident.toChars(), mt.toChars(), mt.toPrettyChars()); - - else if (const n = importHint(ident.toString())) - error(loc, "no property `%s` for type `%s`, perhaps `import %.*s;` is needed?", ident.toChars(), mt.toChars(), cast(int)n.length, n.ptr); - else + if (src) + { + error(loc, "no property `%s` for `%s` of type `%s`", + ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + auto s2 = scope_.search_correct(ident); + // UFCS + if (s2 && s2.isFuncDeclaration) { - if (src) - error(loc, "no property `%s` for `%s` of type `%s`", ident.toChars(), src.toChars(), mt.toPrettyChars(true)); + if (s2.ident == ident) + { + errorSupplemental(s2.loc, "cannot call %s `%s` with UFCS because it is not declared at module scope", + s2.kind(), s2.toChars()); + } else - error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); + errorSupplemental(s2.loc, "did you mean %s `%s`?", + s2.kind(), s2.toChars()); + } + else if (src.type.ty == Tpointer) + { + // structPtr.field + auto tn = (cast(TypeNext) src.type).nextOf(); + if (auto as = tn.isAggregate()) + { + if (auto s3 = as.search_correct(ident)) + { + errorSupplemental(s3.loc, "did you mean %s `%s`?", + s3.kind(), s3.toChars()); + } + } + } + } + else + error(loc, "no property `%s` for type `%s`", ident.toChars(), mt.toPrettyChars(true)); - if (auto dsym = mt.toDsymbol(scope_)) + if (auto dsym = mt.toDsymbol(scope_)) + { + if (auto sym = dsym.isAggregateDeclaration()) + { + if (!sym.members) { - if (auto sym = dsym.isAggregateDeclaration()) + errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); + return ErrorExp.get(); + } + + if (auto fd = search_function(sym, Id.opDispatch)) + { + if (auto td = fd.isTemplateDeclaration()) { - if (auto fd = search_function(sym, Id.opDispatch)) - errorSupplemental(loc, "potentially malformed `opDispatch`. Use an explicit instantiation to get a better error message"); - else if (!sym.members) - errorSupplemental(sym.loc, "`%s %s` is opaque and has no members.", sym.kind, mt.toPrettyChars(true)); + e = mt.defaultInitLiteral(loc); + auto se = new StringExp(e.loc, ident.toString()); + auto tiargs = new Objects(); + tiargs.push(se); + auto dti = new DotTemplateInstanceExp(e.loc, e, Id.opDispatch, tiargs); + dti.ti.tempdecl = td; + dti.dotTemplateSemanticProp(scope_, DotExpFlag.none); + return ErrorExp.get(); } - errorSupplemental(dsym.loc, "%s `%s` defined here", - dsym.kind, dsym.toChars()); } } + errorSupplemental(dsym.loc, "%s `%s` defined here", + dsym.kind, dsym.toChars()); } - e = ErrorExp.get(); } - return e; + + return ErrorExp.get(); } Expression visitError(TypeError) @@ -3088,7 +3533,7 @@ Expression getProperty(Type t, Scope* scope_, const ref Loc loc, Identifier iden Expression floatValue(real_t r) { - if (mt.isreal() || mt.isimaginary()) + if (mt.isReal() || mt.isImaginary()) return new RealExp(loc, r, mt); else { @@ -3427,7 +3872,7 @@ private void resolveExp(Expression exp, out Type t, out Expression e, out Dsymbo * ps = is set if t is a symbol * intypeid = true if in type id */ -void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) +void resolve(Type mt, Loc loc, Scope* sc, out Expression pe, out Type pt, out Dsymbol ps, bool intypeid = false) { void returnExp(Expression e) { @@ -3734,7 +4179,7 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type // compile time sequences are valid types !mt.exp.type.isTypeTuple()) { - if (!(sc.flags & SCOPE.Cfile) && // in (extended) C typeof may be used on types as with sizeof + if (!sc.inCfile && // in (extended) C typeof may be used on types as with sizeof mt.exp.checkType()) goto Lerr; @@ -4063,6 +4508,10 @@ void resolve(Type mt, const ref Loc loc, Scope* sc, out Expression pe, out Type */ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag) { + enum LOGDOTEXP = false; + if (LOGDOTEXP) + printf("dotExp()\n"); + Expression visitType(Type mt) { VarDeclaration v = null; @@ -4083,7 +4532,9 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } if (v) { - if (ident == Id.offsetof) + if (ident == Id.offsetof || + ident == Id.bitoffsetof || + ident == Id.bitwidth) { v.dsymbolSemantic(null); if (v.isField()) @@ -4093,7 +4544,20 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ad.size(e.loc); if (ad.sizeok != Sizeok.done) return ErrorExp.get(); - return new IntegerExp(e.loc, v.offset, Type.tsize_t); + uint value; + if (ident == Id.offsetof) + value = v.offset; + else // Id.bitoffsetof || Id.bitwidth + { + auto bf = v.isBitFieldDeclaration(); + if (bf) + { + value = ident == Id.bitoffsetof ? bf.bitOffset : bf.fieldWidth; + } + else + error(v.loc, "`%s` is not a bitfield, cannot apply `%s`", v.toChars(), ident.toChars()); + } + return new IntegerExp(e.loc, value, Type.tsize_t); } } else if (ident == Id._init) @@ -4314,7 +4778,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag { if (e.op == EXP.type) { - error(e.loc, "`%s` is not an expression", e.toChars()); + error(e.loc, "`%s` is not an expression", e.toErrMsg()); return ErrorExp.get(); } else if (mt.dim.toUInteger() < 1 && checkUnsafeDotExp(sc, e, ident, flag)) @@ -4363,7 +4827,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } if (e.op == EXP.type && (ident == Id.length || ident == Id.ptr)) { - error(e.loc, "`%s` is not an expression", e.toChars()); + error(e.loc, "`%s` is not an expression", e.toErrMsg()); return ErrorExp.get(); } if (ident == Id.length) @@ -4413,8 +4877,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag fd_aaLen = FuncDeclaration.genCfunc(fparams, Type.tsize_t, Id.aaLen); TypeFunction tf = fd_aaLen.type.toTypeFunction(); tf.purity = PURE.const_; - tf.isnothrow = true; - tf.isnogc = false; + tf.isNothrow = true; + tf.isNogc = false; } Expression ev = new VarExp(e.loc, fd_aaLen, false); e = new CallExp(e.loc, ev, e); @@ -4466,7 +4930,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /*************************************** * `ident` was not found as a member of `mt`. - * Attempt to use overloaded opDot(), overloaded opDispatch(), or `alias this`. + * Attempt to use overloaded opDispatch() or `alias this`. * If that fails, forward to visitType(). * Params: * mt = class or struct @@ -4512,6 +4976,8 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ident != Id._mangleof && ident != Id.stringof && ident != Id.offsetof && + ident != Id.bitoffsetof && + ident != Id.bitwidth && // https://issues.dlang.org/show_bug.cgi?id=15045 // Don't forward special built-in member functions. ident != Id.ctor && @@ -4520,21 +4986,6 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag ident != Id.postblit && ident != Id.__xpostblit) { - /* Look for overloaded opDot() to see if we should forward request - * to it. - */ - if (auto fd = search_function(sym, Id.opDot)) - { - /* Rewrite e.ident as: - * e.opDot().ident - */ - e = build_overload(e.loc, sc, e, null, fd); - // @@@DEPRECATED_2.110@@@. - // Deprecated in 2.082, made an error in 2.100. - error(e.loc, "`opDot` is obsolete. Use `alias this`"); - return ErrorExp.get(); - } - /* Look for overloaded opDispatch to see if we should forward request * to it. */ @@ -4558,7 +5009,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag * e.g. * template opDispatch(name) if (isValid!name) { ... } */ - uint errors = gagError ? global.startGagging() : 0; + const errors = gagError ? global.startGagging() : 0; e = dti.dotTemplateSemanticProp(sc, DotExpFlag.none); if (gagError && global.endGagging(errors)) e = null; @@ -4576,7 +5027,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag */ auto die = new DotIdExp(e.loc, alias_e, ident); - auto errors = gagError ? 0 : global.startGagging(); + const errors = gagError ? 0 : global.startGagging(); auto exp = die.dotIdSemanticProp(sc, gagError); if (!gagError) { @@ -4606,7 +5057,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag assert(e.op != EXP.dot); // https://issues.dlang.org/show_bug.cgi?id=14010 - if (!(sc.flags & SCOPE.Cfile) && ident == Id._mangleof) + if (!sc.inCfile && ident == Id._mangleof) { return mt.getProperty(sc, e.loc, ident, flag & 1); } @@ -4618,7 +5069,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Create a TupleExp out of the fields of the struct e: * (e.field0, e.field1, e.field2, ...) */ - e = e.expressionSemantic(sc); // do this before turning on noaccesscheck + e = e.expressionSemantic(sc); // do this before turning on noAccessCheck if (!mt.sym.determineFields()) { @@ -4648,26 +5099,59 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; e = e.expressionSemantic(sc2); sc2.pop(); return e; } - immutable flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; + immutable flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : 0; s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: if (!s) { return noMember(mt, sc, e, ident, flag); } - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s)) { return noMember(mt, sc, e, ident, flag); } // check before alias resolution; the alias itself might be deprecated! - if (s.isAliasDeclaration) + if (auto ad = s.isAliasDeclaration) + { s.checkDeprecated(e.loc, sc); + + // Fix for https://github.com/dlang/dmd/issues/20610 + if (ad.originalType) + { + if (auto tid = ad.originalType.isTypeIdentifier()) + { + if (tid.idents.length) + { + static if (0) + { + printf("TypeStruct::dotExp(e = '%s', ident = '%s')\n", e.toChars(), ident.toChars()); + printf("AliasDeclaration: %s\n", ad.toChars()); + if (ad.aliassym) + printf("aliassym: %s\n", ad.aliassym.toChars()); + printf("tid type: %s\n", toChars(tid)); + } + /* Rewrite e.s as e.(tid.ident).(tid.idents) + */ + Expression die = new DotIdExp(e.loc, e, tid.ident); + foreach (id; tid.idents) // maybe use typeToExpressionHelper() + die = new DotIdExp(e.loc, die, cast(Identifier)id); + /* Ambiguous syntax, only way to disambiguate it to try it + */ + die = dmd.expressionsem.trySemantic(die, sc); + if (die && die.isDotVarExp()) // shrink wrap around DotVarExp() + { + return die; + } + } + } + } + } s = s.toAlias(); if (auto em = s.isEnumMember()) @@ -4770,7 +5254,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag Declaration d = s.isDeclaration(); if (!d) { - error(e.loc, "`%s.%s` is not a declaration", e.toChars(), ident.toChars()); + error(e.loc, "`%s.%s` is not a declaration", e.toErrMsg(), ident.toChars()); return ErrorExp.get(); } @@ -4898,7 +5382,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag /* Create a TupleExp */ - e = e.expressionSemantic(sc); // do this before turning on noaccesscheck + e = e.expressionSemantic(sc); // do this before turning on noAccessCheck mt.sym.size(e.loc); // do semantic of type @@ -4928,13 +5412,13 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag e = new TupleExp(e.loc, e0, exps); Scope* sc2 = sc.push(); - sc2.flags |= SCOPE.noaccesscheck; + sc2.noAccessCheck = true; e = e.expressionSemantic(sc2); sc2.pop(); return e; } - SearchOptFlags flags = sc.flags & SCOPE.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; + SearchOptFlags flags = sc.ignoresymbolvisibility ? SearchOpt.ignoreVisibility : SearchOpt.all; s = mt.sym.search(e.loc, ident, flags | SearchOpt.ignorePrivateImports); L1: @@ -5087,7 +5571,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag return noMember(mt, sc, e, ident, flag & 1); } - if (!(sc.flags & SCOPE.ignoresymbolvisibility) && !symbolIsVisible(sc, s)) + if (!sc.ignoresymbolvisibility && !symbolIsVisible(sc, s)) { return noMember(mt, sc, e, ident, flag); } @@ -5203,7 +5687,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag Declaration d = s.isDeclaration(); if (!d) { - error(e.loc, "`%s.%s` is not a declaration", e.toChars(), ident.toChars()); + error(e.loc, "`%s.%s` is not a declaration", e.toErrMsg(), ident.toChars()); return ErrorExp.get(); } @@ -5362,6 +5846,74 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag } } +// if initializer is 0 +bool isZeroInit(Type t, Loc loc) +{ + bool visitType(Type _) + { + return false; // assume not + } + + bool visitBasic(TypeBasic t) + { + switch (t.ty) + { + case Tchar: + case Twchar: + case Tdchar: + case Timaginary32: + case Timaginary64: + case Timaginary80: + case Tfloat32: + case Tfloat64: + case Tfloat80: + case Tcomplex32: + case Tcomplex64: + case Tcomplex80: + return false; // no + default: + return true; // yes + } + } + + bool visitVector(TypeVector t) + { + return t.basetype.isZeroInit(loc); + } + + bool visitSArray(TypeSArray t) + { + return t.next.isZeroInit(loc); + } + + bool visitStruct(TypeStruct t) + { + // Determine zeroInit here, as this can be called before semantic2 + t.sym.determineSize(t.sym.loc); + return t.sym.zeroInit; + } + + bool visitEnum(TypeEnum t) + { + return t.sym.getDefaultValue(loc).toBool().hasValue(false); + } + + switch(t.ty) + { + default: return t.isTypeBasic() ? visitBasic(cast(TypeBasic)t) : visitType(t); + case Tvector: return visitVector(t.isTypeVector()); + case Tsarray: return visitSArray(t.isTypeSArray()); + case Taarray: + case Tarray: + case Treference: + case Tdelegate: + case Tclass: + case Tpointer: return true; + case Tstruct: return visitStruct(t.isTypeStruct()); + case Tenum: return visitEnum(t.isTypeEnum()); + } +} + /************************ * Get the default initialization expression for a type. @@ -5373,7 +5925,7 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag * Returns: * The initialization expression for the type. */ -Expression defaultInit(Type mt, const ref Loc loc, const bool isCfile = false) +Expression defaultInit(Type mt, Loc loc, const bool isCfile = false) { Expression visitBasic(TypeBasic mt) { @@ -5590,7 +6142,7 @@ Dsymbol toDsymbol(Type type, Scope* sc) Dsymbol visitIdentifier(TypeIdentifier type) { - //printf("TypeIdentifier::toDsymbol('%s')\n", toChars()); + //printf("TypeIdentifier::toDsymbol('%s')\n", toChars(type)); if (!sc) return null; @@ -5602,7 +6154,6 @@ Dsymbol toDsymbol(Type type, Scope* sc) s = t.toDsymbol(sc); if (e) s = getDsymbol(e); - return s; } @@ -5655,7 +6206,7 @@ Dsymbol toDsymbol(Type type, Scope* sc) /************************************ * Add storage class modifiers to type. */ -Type addStorageClass(Type type, StorageClass stc) +Type addStorageClass(Type type, STC stc) { Type visitType(Type t) { @@ -5681,43 +6232,43 @@ Type addStorageClass(Type type, StorageClass stc) //printf("addStorageClass(%llx) %d\n", stc, (stc & STC.scope_) != 0); TypeFunction t = visitType(tf_src).toTypeFunction(); if ((stc & STC.pure_ && !t.purity) || - (stc & STC.nothrow_ && !t.isnothrow) || - (stc & STC.nogc && !t.isnogc) || + (stc & STC.nothrow_ && !t.isNothrow) || + (stc & STC.nogc && !t.isNogc) || (stc & STC.scope_ && !t.isScopeQual) || (stc & STC.safe && t.trust < TRUST.trusted)) { // Klunky to change these - auto tf = new TypeFunction(t.parameterList, t.next, t.linkage, 0); + auto tf = new TypeFunction(t.parameterList, t.next, t.linkage, STC.none); tf.mod = t.mod; tf.inferenceArguments = tf_src.inferenceArguments; tf.purity = t.purity; - tf.isnothrow = t.isnothrow; - tf.isnogc = t.isnogc; - tf.isproperty = t.isproperty; - tf.isref = t.isref; - tf.isreturn = t.isreturn; - tf.isreturnscope = t.isreturnscope; + tf.isNothrow = t.isNothrow; + tf.isNogc = t.isNogc; + tf.isProperty = t.isProperty; + tf.isRef = t.isRef; + tf.isReturn = t.isReturn; + tf.isReturnScope = t.isReturnScope; tf.isScopeQual = t.isScopeQual; - tf.isreturninferred = t.isreturninferred; - tf.isscopeinferred = t.isscopeinferred; + tf.isReturnInferred = t.isReturnInferred; + tf.isScopeInferred = t.isScopeInferred; tf.trust = t.trust; tf.isInOutParam = t.isInOutParam; tf.isInOutQual = t.isInOutQual; - tf.isctor = t.isctor; + tf.isCtor = t.isCtor; if (stc & STC.pure_) tf.purity = PURE.fwdref; if (stc & STC.nothrow_) - tf.isnothrow = true; + tf.isNothrow = true; if (stc & STC.nogc) - tf.isnogc = true; + tf.isNogc = true; if (stc & STC.safe) tf.trust = TRUST.safe; if (stc & STC.scope_) { tf.isScopeQual = true; if (stc & STC.scopeinferred) - tf.isscopeinferred = true; + tf.isScopeInferred = true; } tf.deco = tf.merge().deco; @@ -5750,7 +6301,7 @@ Type addStorageClass(Type type, StorageClass stc) * Complex!float, Complex!double, Complex!real or null for error */ -Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) +Type getComplexLibraryType(Loc loc, Scope* sc, TY ty) { // singleton __gshared Type complex_float; @@ -5818,7 +6369,7 @@ Type getComplexLibraryType(const ref Loc loc, Scope* sc, TY ty) * Returns: * An enum value of either `Covariant.yes` or a reason it's not covariant. */ -Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovariant = false) +Covariant covariant(Type src, Type t, STC* pstc = null, bool cppCovariant = false) { version (none) { @@ -5828,8 +6379,8 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria printf("mod = %x, %x\n", src.mod, t.mod); } if (pstc) - *pstc = 0; - StorageClass stc = 0; + *pstc = STC.none; + STC stc = STC.none; bool notcovariant = false; @@ -5889,7 +6440,7 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria goto Ldistinct; } Lcov: - notcovariant |= !fparam1.isCovariant(t1.isref, fparam2); + notcovariant |= !fparam1.isCovariant(t1.isRef, fparam2); /* https://issues.dlang.org/show_bug.cgi?id=23135 * extern(C++) mutable parameters are not covariant with const. @@ -5949,7 +6500,7 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria } else if (t1n.ty == t2n.ty && t1n.implicitConvTo(t2n)) { - if (t1.isref && t2.isref) + if (t1.isRef && t2.isRef) { // Treat like pointers to t1n and t2n if (t1n.constConv(t2n) < MATCH.constant) @@ -5974,31 +6525,31 @@ Covariant covariant(Type src, Type t, StorageClass* pstc = null, bool cppCovaria goto Lnotcovariant; Lcovariant: - if (t1.isref != t2.isref) + if (t1.isRef != t2.isRef) goto Lnotcovariant; - if (!t1.isref && (t1.isScopeQual || t2.isScopeQual)) + if (!t1.isRef && (t1.isScopeQual || t2.isScopeQual)) { - StorageClass stc1 = t1.isScopeQual ? STC.scope_ : 0; - StorageClass stc2 = t2.isScopeQual ? STC.scope_ : 0; - if (t1.isreturn) + STC stc1 = t1.isScopeQual ? STC.scope_ : STC.none; + STC stc2 = t2.isScopeQual ? STC.scope_ : STC.none; + if (t1.isReturn) { stc1 |= STC.return_; if (!t1.isScopeQual) stc1 |= STC.ref_; } - if (t2.isreturn) + if (t2.isReturn) { stc2 |= STC.return_; if (!t2.isScopeQual) stc2 |= STC.ref_; } - if (!Parameter.isCovariantScope(t1.isref, stc1, stc2)) + if (!Parameter.isCovariantScope(t1.isRef, stc1, stc2)) goto Lnotcovariant; } // We can subtract 'return ref' from 'this', but cannot add it - else if (t1.isreturn && !t2.isreturn) + else if (t1.isReturn && !t2.isReturn) goto Lnotcovariant; /* https://issues.dlang.org/show_bug.cgi?id=23135 @@ -6031,10 +6582,10 @@ Lcovariant: if (!t1.purity && t2.purity) stc |= STC.pure_; - if (!t1.isnothrow && t2.isnothrow) + if (!t1.isNothrow && t2.isNothrow) stc |= STC.nothrow_; - if (!t1.isnogc && t2.isnogc) + if (!t1.isNogc && t2.isNogc) stc |= STC.nogc; /* Can convert safe/trusted to system @@ -6078,7 +6629,7 @@ Lnotcovariant: * Returns: * storage class with STC.scope_ or STC.return_ OR'd in */ -StorageClass parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarations* outerVars = null, +STC parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, VarDeclarations* outerVars = null, bool indirect = false) { //printf("parameterStorageClass(p: %s)\n", p.toChars()); @@ -6093,7 +6644,7 @@ StorageClass parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, Var /* If haven't inferred the return type yet, can't infer storage classes */ - if (!tf.nextOf() || !tf.isnothrow()) + if (!tf.nextOf() || !tf.isNothrow()) return stc; tf.purityLevel(); @@ -6173,7 +6724,7 @@ StorageClass parameterStorageClass(TypeFunction tf, Type tthis, Parameter p, Var // Check escaping through return value Type tret = tf.nextOf().toBasetype(); - if (tf.isref || tret.hasPointers()) + if (tf.isRef || tret.hasPointers()) { return stc | STC.scope_ | STC.return_ | STC.returnScope; } @@ -6211,30 +6762,32 @@ Type pointerTo(Type type) { if (type.ty == Terror) return type; - if (!type.pto) + auto mcache = type.getMcache(); + if (!mcache.pto) { Type t = new TypePointer(type); if (type.ty == Tfunction) { t.deco = t.merge().deco; - type.pto = t; + mcache.pto = t; } else - type.pto = t.merge(); + mcache.pto = t.merge(); } - return type.pto; + return mcache.pto; } Type referenceTo(Type type) { if (type.ty == Terror) return type; - if (!type.rto) + auto mcache = type.getMcache(); + if (!mcache.rto) { Type t = new TypeReference(type); - type.rto = t.merge(); + mcache.rto = t.merge(); } - return type.rto; + return mcache.rto; } // Make corresponding static array type without semantic @@ -6252,12 +6805,13 @@ Type arrayOf(Type type) { if (type.ty == Terror) return type; - if (!type.arrayof) + auto mcache = type.getMcache(); + if (!mcache.arrayof) { Type t = new TypeDArray(type); - type.arrayof = t.merge(); + mcache.arrayof = t.merge(); } - return type.arrayof; + return mcache.arrayof; } /******************************** @@ -6509,6 +7063,39 @@ Type sharedWildConstOf(Type type) return t; } +Type nakedOf(Type type) +{ + //printf("Type::nakedOf() %p, %s\n", type, type.toChars()); + if (type.mod == 0) + return type; + if (type.mcache) with(type.mcache) + { + // the cache has the naked type at the "identity" position, try to find it + if (cto && cto.mod == 0) + return cto; + if (ito && ito.mod == 0) + return ito; + if (sto && sto.mod == 0) + return sto; + if (scto && scto.mod == 0) + return scto; + if (wto && wto.mod == 0) + return wto; + if (wcto && wcto.mod == 0) + return wcto; + if (swto && swto.mod == 0) + return swto; + if (swcto && swcto.mod == 0) + return swcto; + } + Type t = type.nullAttributes(); + t.mod = 0; + t = t.merge(); + t.fixTo(type); + //printf("\t%p %s\n", t, t.toChars()); + return t; +} + Type unqualify(Type type, uint m) { Type t = type.mutableOf().unSharedOf(); @@ -6687,7 +7274,7 @@ Type substWildTo(Type type, uint mod) t = new TypeSArray(t, (cast(TypeSArray)type).dim.syntaxCopy()); else if (type.ty == Taarray) { - t = new TypeAArray(t, (cast(TypeAArray)type).index.syntaxCopy()); + t = new TypeAArray(t, (cast(TypeAArray)type).index.substWildTo(mod)); } else if (type.ty == Tdelegate) { @@ -6770,21 +7357,21 @@ Type substWildTo(Type type, uint mod) // Similar to TypeFunction.syntaxCopy; auto t = new TypeFunction(ParameterList(params, tf.parameterList.varargs), tret, tf.linkage); t.mod = ((tf.mod & MODFlags.wild) ? (tf.mod & ~MODFlags.wild) | MODFlags.const_ : tf.mod); - t.isnothrow = tf.isnothrow; - t.isnogc = tf.isnogc; + t.isNothrow = tf.isNothrow; + t.isNogc = tf.isNogc; t.purity = tf.purity; - t.isproperty = tf.isproperty; - t.isref = tf.isref; - t.isreturn = tf.isreturn; - t.isreturnscope = tf.isreturnscope; + t.isProperty = tf.isProperty; + t.isRef = tf.isRef; + t.isReturn = tf.isReturn; + t.isReturnScope = tf.isReturnScope; t.isScopeQual = tf.isScopeQual; - t.isreturninferred = tf.isreturninferred; - t.isscopeinferred = tf.isscopeinferred; + t.isReturnInferred = tf.isReturnInferred; + t.isScopeInferred = tf.isScopeInferred; t.isInOutParam = false; t.isInOutQual = false; t.trust = tf.trust; t.inferenceArguments = tf.inferenceArguments; - t.isctor = tf.isctor; + t.isCtor = tf.isCtor; return t.merge(); } @@ -6951,11 +7538,173 @@ bool isRecursiveAliasThis(ref Type att, Type t) auto tb = t.toBasetype(); if (att && tb.equivalent(att)) return true; - else if (!att && tb.checkAliasThisRec()) + if (!att && tb.checkAliasThisRec()) att = tb; return false; } +MATCH implicitConvToWithoutAliasThis(TypeStruct from, Type to) +{ + //printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars()); + + auto tos = to.isTypeStruct(); + if (!(tos && from.sym == tos.sym)) + return MATCH.nomatch; + + if (from.mod == to.mod) + return MATCH.exact; + + if (MODimplicitConv(from.mod, to.mod)) + return MATCH.constant; + + /* Check all the fields. If they can all be converted, + * allow the conversion. + */ + MATCH m = MATCH.constant; + uint offset = ~0; // must never match a field offset + foreach (v; from.sym.fields[]) + { + /* Why are we only looking at the first member of a union? + * The check should check for overlap of v with the previous field, + * not just starting at the same point + */ + if (!global.params.fixImmutableConv && v.offset == offset) // v is at same offset as previous field + continue; // ignore + + Type tvf = v.type.addMod(from.mod); // from type + Type tvt = v.type.addMod(to.mod); // to type + + // field match + MATCH mf = tvf.implicitConvTo(tvt); + //printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf); + + if (mf == MATCH.nomatch) + return MATCH.nomatch; + if (mf < m) // if field match is worse + m = mf; + offset = v.offset; + } + return m; +} + +MATCH implicitConvToWithoutAliasThis(TypeClass from, Type to) +{ + ClassDeclaration cdto = to.isClassHandle(); + MATCH m = constConv(from, to); + if (m > MATCH.nomatch) + return m; + + if (cdto && cdto.isBaseOf(from.sym, null) && MODimplicitConv(from.mod, to.mod)) + { + //printf("'to' is base\n"); + return MATCH.convert; + } + return MATCH.nomatch; +} + +MATCH implicitConvToThroughAliasThis(TypeClass from, Type to) +{ + MATCH m; + if (from.sym.aliasthis && !(from.att & AliasThisRec.tracing)) + { + if (auto ato = aliasthisOf(from)) + { + from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing); + m = ato.implicitConvTo(to); + from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing); + } + } + return m; +} + +MATCH implicitConvToThroughAliasThis(TypeStruct from, Type to) +{ + auto tos = to.isTypeStruct(); + if (!(tos && from.sym == tos.sym) && + from.sym.aliasthis && + !(from.att & AliasThisRec.tracing)) + { + if (auto ato = aliasthisOf(from)) + { + from.att = cast(AliasThisRec)(from.att | AliasThisRec.tracing); + MATCH m = ato.implicitConvTo(to); + from.att = cast(AliasThisRec)(from.att & ~AliasThisRec.tracing); + return m; + } + } + return MATCH.nomatch; +} + +/******************************************* + * Compute number of elements for a (possibly multidimensional) static array, + * or 1 for other types. + * Params: + * t = static array type + * loc = for error message + * Returns: + * number of elements, uint.max on overflow + */ +uint numberOfElems(Type t, Loc loc) +{ + //printf("Type::numberOfElems()\n"); + uinteger_t n = 1; + Type tb = t; + while ((tb = tb.toBasetype()).ty == Tsarray) + { + bool overflow = false; + n = mulu(n, (cast(TypeSArray)tb).dim.toUInteger(), overflow); + if (overflow || n >= uint.max) + { + error(loc, "static array `%s` size overflowed to %llu", t.toChars(), cast(ulong)n); + return uint.max; + } + tb = (cast(TypeSArray)tb).next; + } + return cast(uint)n; +} + +bool checkRetType(TypeFunction tf, Loc loc) +{ + Type tb = tf.next.toBasetype(); + if (tb.ty == Tfunction) + { + error(loc, "functions cannot return a function"); + tf.next = Type.terror; + } + if (tb.ty == Ttuple) + { + error(loc, "functions cannot return a sequence (use `std.typecons.Tuple`)"); + tf.next = Type.terror; + } + if (!tf.isRef && (tb.ty == Tstruct || tb.ty == Tsarray)) + { + if (auto ts = tb.baseElemOf().isTypeStruct()) + { + if (!ts.sym.members) + { + error(loc, "functions cannot return opaque type `%s` by value", tb.toChars()); + tf.next = Type.terror; + } + } + } + if (tb.ty == Terror) + return true; + return false; +} + +/// Returns: whether `t` is a struct/class/enum without a body +bool isOpaqueType(Type t) +{ + if (auto te = t.isTypeEnum()) + return te.sym.members is null; + if (auto ts = t.isTypeStruct()) + return ts.sym.members is null; + if (auto tc = t.isTypeClass()) + return tc.sym.members is null; + return false; +} + + /******************************* Private *****************************************/ private: @@ -7077,8 +7826,7 @@ Type stripDefaultArgs(Type t) { foreach (i, p; *parameters) { - Parameter ps = stripParameter(p); - if (ps) + if (Parameter ps = stripParameter(p)) { // Replace params with a copy we can modify Parameters* nparams = new Parameters(parameters.length); @@ -7155,11 +7903,11 @@ Type stripDefaultArgs(Type t) * Returns: * corresponding value of .max/.min */ -Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) +Expression getMaxMinValue(EnumDeclaration ed, Loc loc, Identifier id) { //printf("EnumDeclaration::getMaxValue()\n"); - static Expression pvalToResult(Expression e, const ref Loc loc) + static Expression pvalToResult(Expression e, Loc loc) { if (e.op != EXP.error) { @@ -7194,7 +7942,7 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) .error(loc, "%s `%s` is opaque and has no `.%s`", ed.kind, ed.toPrettyChars, id.toChars(), id.toChars()); return errorReturn(); } - if (!(ed.memtype && ed.memtype.isintegral())) + if (!(ed.memtype && ed.memtype.isIntegral())) { .error(loc, "%s `%s` has no `.%s` property because base type `%s` is not an integral type", ed.kind, ed.toPrettyChars, id.toChars(), id.toChars(), ed.memtype ? ed.memtype.toChars() : ""); @@ -7266,10 +8014,10 @@ Expression getMaxMinValue(EnumDeclaration ed, const ref Loc loc, Identifier id) * Return: * null if error, else RootObject AST as parsed */ -RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) +RootObject compileTypeMixin(TypeMixin tm, Loc loc, Scope* sc) { OutBuffer buf; - if (expressionsToString(buf, sc, tm.exps)) + if (expressionsToString(buf, sc, tm.exps, tm.loc, null, true)) return null; const errors = global.errors; @@ -7277,9 +8025,9 @@ RootObject compileTypeMixin(TypeMixin tm, ref const Loc loc, Scope* sc) buf.writeByte(0); const str = buf.extractSlice()[0 .. len]; const bool doUnittests = global.params.parsingUnittestsRequired(); - auto locm = adjustLocForMixin(str, loc, global.params.mixinOut); - scope p = new Parser!ASTCodegen(locm, sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); - p.transitionIn = global.params.v.vin; + scope p = new Parser!ASTCodegen(sc._module, str, false, global.errorSink, &global.compileEnv, doUnittests); + adjustLocForMixin(str, loc, *p.baseLoc, global.params.mixinOut); + p.linnum = p.baseLoc.startLine; p.nextToken(); //printf("p.loc.linnum = %d\n", p.loc.linnum); diff --git a/gcc/d/dmd/typinf.d b/gcc/d/dmd/typinf.d index a319832..1c7ed32 100644 --- a/gcc/d/dmd/typinf.d +++ b/gcc/d/dmd/typinf.d @@ -1,12 +1,12 @@ /** * Generate `TypeInfo` objects, which are needed for run-time introspection of types. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/typinf.d, _typinf.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/typinf.d, _typinf.d) * Documentation: https://dlang.org/phobos/dmd_typinf.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/typinf.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/typinf.d */ module dmd.typinf; @@ -22,6 +22,7 @@ import dmd.expression; import dmd.globals; import dmd.location; import dmd.mtype; +import dmd.typesem; import core.stdc.stdio; /**************************************************** @@ -35,14 +36,14 @@ import core.stdc.stdio; * Returns: * true if `TypeInfo` was generated and needs compiling to object file */ -bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) +bool genTypeInfo(Expression e, Loc loc, Type torig, Scope* sc) { // printf("genTypeInfo() %s\n", torig.toChars()); // Even when compiling without `useTypeInfo` (e.g. -betterC) we should // still be able to evaluate `TypeInfo` at compile-time, just not at runtime. // https://issues.dlang.org/show_bug.cgi?id=18472 - if (!sc || !(sc.flags & SCOPE.ctfe)) + if (!sc || !sc.ctfe) { if (!global.params.useTypeInfo) { @@ -67,6 +68,9 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) import dmd.typesem : merge2; Type t = torig.merge2(); // do this since not all Type's are merge'd + if (t.ty == Taarray) + t = makeNakedAssociativeArray(cast(TypeAArray)t); + bool needsCodegen = false; if (!t.vtinfo) { @@ -79,7 +83,7 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) else if (t.isWild()) t.vtinfo = TypeInfoWildDeclaration.create(t); else - t.vtinfo = getTypeInfoDeclaration(t); + t.vtinfo = getTypeInfoDeclaration(t, sc); assert(t.vtinfo); // ClassInfos are generated as part of ClassDeclaration codegen @@ -100,13 +104,12 @@ bool genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope* sc) * loc = the location for reporting line nunbers in errors * t = the type to get the type of the `TypeInfo` object for * sc = the scope - * genObjCode = if true, object code will be generated for the obtained TypeInfo * Returns: * The type of the `TypeInfo` object associated with `t` */ -extern (C++) Type getTypeInfoType(const ref Loc loc, Type t, Scope* sc, bool genObjCode = true); +extern (C++) Type getTypeInfoType(Loc loc, Type t, Scope* sc); -private TypeInfoDeclaration getTypeInfoDeclaration(Type t) +private TypeInfoDeclaration getTypeInfoDeclaration(Type t, Scope* sc) { //printf("Type::getTypeInfoDeclaration() %s\n", t.toChars()); switch (t.ty) @@ -118,7 +121,7 @@ private TypeInfoDeclaration getTypeInfoDeclaration(Type t) case Tsarray: return TypeInfoStaticArrayDeclaration.create(t); case Taarray: - return TypeInfoAssociativeArrayDeclaration.create(t); + return getTypeInfoAssocArrayDeclaration(cast(TypeAArray)t, sc); case Tstruct: return TypeInfoStructDeclaration.create(t); case Tvector: @@ -142,6 +145,70 @@ private TypeInfoDeclaration getTypeInfoDeclaration(Type t) } } +/****************************************** + * Instantiate TypeInfoAssociativeArrayDeclaration and fill + * the entry with TypeInfo_AssociativeArray.Entry!(t.index, t.next) + * + * Params: + * t = TypeAArray to generate TypeInfo_AssociativeArray for + * sc = context + * Returns: + * a TypeInfoAssociativeArrayDeclaration with field entry initialized + */ +TypeInfoDeclaration getTypeInfoAssocArrayDeclaration(TypeAArray t, Scope* sc) +{ + import dmd.arraytypes; + import dmd.expressionsem; + import dmd.id; + + assert(sc); // must not be called in the code generation phase + + auto ti = TypeInfoAssociativeArrayDeclaration.create(t); + t.vtinfo = ti; // assign it early to avoid recursion in expressionSemantic + Loc loc = t.loc; + auto tiargs = new Objects(); + tiargs.push(t.index); // always called with naked types + tiargs.push(t.next); + + Expression id = new IdentifierExp(loc, Id.empty); + id = new DotIdExp(loc, id, Id.object); + id = new DotIdExp(loc, id, Id.TypeInfo_AssociativeArray); + auto tempinst = new DotTemplateInstanceExp(loc, id, Id.Entry, tiargs); + auto e = expressionSemantic(tempinst, sc); + assert(e.type); + ti.entry = e.type; + if (auto ts = ti.entry.isTypeStruct()) + { + ts.sym.requestTypeInfo = true; + if (auto tmpl = ts.sym.isInstantiated()) + tmpl.minst = sc._module.importedFrom; // ensure it get's emitted + } + getTypeInfoType(loc, ti.entry, sc); + assert(ti.entry.vtinfo); + + return ti; +} + +/****************************************** + * Find or create a TypeAArray with index and next without + * any head modifiers, tail `inout` is replaced with `const` + * + * Params: + * t = TypeAArray to convert + * Returns: + * t = found type + */ +Type makeNakedAssociativeArray(TypeAArray t) +{ + Type tindex = t.index.toBasetype().nakedOf().substWildTo(MODFlags.const_); + Type tnext = t.next.toBasetype().nakedOf().substWildTo(MODFlags.const_); + if (tindex == t.index && tnext == t.next) + return t; + + t = new TypeAArray(tnext, tindex); + return t.merge(); +} + /************************************************** * Returns: * true if any part of type t is speculative. diff --git a/gcc/d/dmd/typinf.h b/gcc/d/dmd/typinf.h index dd9572a..c34494d 100644 --- a/gcc/d/dmd/typinf.h +++ b/gcc/d/dmd/typinf.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -14,12 +14,16 @@ class Expression; class Type; +class TypeAArray; +class TypeInfoDeclaration; struct Scope; namespace dmd { - bool genTypeInfo(Expression *e, const Loc &loc, Type *torig, Scope *sc); + bool genTypeInfo(Expression *e, Loc loc, Type *torig, Scope *sc); bool isSpeculativeType(Type *t); bool builtinTypeInfo(Type *t); + Type *makeNakedAssociativeArray(TypeAArray *t); + TypeInfoDeclaration *getTypeInfoAssocArrayDeclaration(TypeAArray *t, Scope *sc); } -Type *getTypeInfoType(const Loc &loc, Type *t, Scope *sc, bool genObjCode = true); +Type *getTypeInfoType(Loc loc, Type *t, Scope *sc); diff --git a/gcc/d/dmd/utils.d b/gcc/d/dmd/utils.d index 72d8036..bfa197a 100644 --- a/gcc/d/dmd/utils.d +++ b/gcc/d/dmd/utils.d @@ -1,12 +1,12 @@ /** * This module defines some utility functions for DMD. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/utils.d, _utils.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/utils.d, _utils.d) * Documentation: https://dlang.org/phobos/dmd_utils.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/utils.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/utils.d */ module dmd.utils; @@ -52,16 +52,18 @@ const(char)* toWinPath(const(char)* src) * Params: * loc = The line number information from where the call originates * filename = Path to file + * buf = append contents of file to + * Returns: + * true on failure */ -Buffer readFile(Loc loc, const(char)[] filename) +bool readFile(Loc loc, const(char)[] filename, ref OutBuffer buf) { - auto result = File.read(filename); - if (!result.success) + if (File.read(filename, buf)) { error(loc, "error reading file `%.*s`", cast(int)filename.length, filename.ptr); - fatal(); + return true; } - return Buffer(result.extractSlice()); + return false; } @@ -122,7 +124,7 @@ bool ensurePathToNameExists(Loc loc, const(char)[] name) * buf = Buffer to write the escaped path to * fname = Path to escape */ -void escapePath(OutBuffer* buf, const(char)* fname) +void escapePath(OutBuffer* buf, const(char)* fname) pure { while (1) { @@ -144,78 +146,6 @@ void escapePath(OutBuffer* buf, const(char)* fname) } /** - * Takes a path, and make it compatible with GNU Makefile format. - * - * GNU make uses a weird quoting scheme for white space. - * A space or tab preceded by 2N+1 backslashes represents N backslashes followed by space; - * a space or tab preceded by 2N backslashes represents N backslashes at the end of a file name; - * and backslashes in other contexts should not be doubled. - * - * Params: - * buf = Buffer to write the escaped path to - * fname = Path to escape - */ -void writeEscapedMakePath(ref OutBuffer buf, const(char)* fname) -{ - uint slashes; - - while (*fname) - { - switch (*fname) - { - case '\\': - slashes++; - break; - case '$': - buf.writeByte('$'); - goto default; - case ' ': - case '\t': - while (slashes--) - buf.writeByte('\\'); - goto case; - case '#': - buf.writeByte('\\'); - goto default; - case ':': - // ':' not escaped on Windows because it can - // create problems with absolute paths (e.g. C:\Project) - version (Windows) {} - else - { - buf.writeByte('\\'); - } - goto default; - default: - slashes = 0; - break; - } - - buf.writeByte(*fname); - fname++; - } -} - -/// -unittest -{ - version (Windows) - { - enum input = `C:\My Project\file#4$.ext`; - enum expected = `C:\My\ Project\file\#4$$.ext`; - } - else - { - enum input = `/foo\bar/weird$.:name#\ with spaces.ext`; - enum expected = `/foo\bar/weird$$.\:name\#\\\ with\ spaces.ext`; - } - - OutBuffer buf; - buf.writeEscapedMakePath(input); - assert(buf[] == expected); -} - -/** * Convert string to integer. * * Params: @@ -307,7 +237,7 @@ bool parseDigits(T)(ref T val, const(char)[] p, const T max = T.max) * size = 1 for ubyte[], 2 for ushort[], 4 for uint[], 8 for ulong[] * Returns: copy of `data`, with bytes shuffled if compiled for `version(LittleEndian)` */ -ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) +ubyte[] arrayCastBigEndian(const ubyte[] data, size_t size) @safe { ubyte[] impl(T)() { diff --git a/gcc/d/dmd/version.h b/gcc/d/dmd/version.h index dd83fd6..1200b2e 100644 --- a/gcc/d/dmd/version.h +++ b/gcc/d/dmd/version.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * written by Walter Bright * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. @@ -15,25 +15,17 @@ class DebugSymbol final : public Dsymbol { public: - unsigned level; - DebugSymbol *syntaxCopy(Dsymbol *) override; - const char *toChars() const override; const char *kind() const override; - DebugSymbol *isDebugSymbol() override; void accept(Visitor *v) override { v->visit(this); } }; class VersionSymbol final : public Dsymbol { public: - unsigned level; - VersionSymbol *syntaxCopy(Dsymbol *) override; - const char *toChars() const override; const char *kind() const override; - VersionSymbol *isVersionSymbol() override; void accept(Visitor *v) override { v->visit(this); } }; diff --git a/gcc/d/dmd/visitor.h b/gcc/d/dmd/visitor.h index ab5cba6..94fd294 100644 --- a/gcc/d/dmd/visitor.h +++ b/gcc/d/dmd/visitor.h @@ -1,6 +1,6 @@ /* Compiler implementation of the D programming language - * Copyright (C) 2013-2024 by The D Language Foundation, All Rights Reserved + * Copyright (C) 2013-2025 by The D Language Foundation, All Rights Reserved * https://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * https://www.boost.org/LICENSE_1_0.txt diff --git a/gcc/d/dmd/foreachvar.d b/gcc/d/dmd/visitor/foreachvar.d index 53b3c04..80611d6 100644 --- a/gcc/d/dmd/foreachvar.d +++ b/gcc/d/dmd/visitor/foreachvar.d @@ -1,15 +1,15 @@ /** * Utility to visit every variable in an expression. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/foreachvar.d, _foreachvar.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/foreachvar.d, _foreachvar.d) * Documentation: https://dlang.org/phobos/dmd_foreachvar.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/foreachvar.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/foreachvar.d */ -module dmd.foreachvar; +module dmd.visitor.foreachvar; import core.stdc.stdio; import core.stdc.stdlib; @@ -24,7 +24,6 @@ import dmd.dstruct; import dmd.dsymbol; import dmd.dsymbolsem; import dmd.dtemplate; -import dmd.errors; import dmd.expression; import dmd.func; import dmd.id; @@ -32,13 +31,13 @@ import dmd.identifier; import dmd.init; import dmd.initsem; import dmd.mtype; -import dmd.postordervisitor; import dmd.printast; import dmd.root.array; import dmd.rootobject; import dmd.statement; import dmd.tokens; import dmd.visitor; +import dmd.visitor.postorder; /********************************************* * Visit each Expression in e, and call dgVar() on each variable declared in it. diff --git a/gcc/d/dmd/visitor.d b/gcc/d/dmd/visitor/package.d index abfd8ca..50b5a54 100644 --- a/gcc/d/dmd/visitor.d +++ b/gcc/d/dmd/visitor/package.d @@ -1,23 +1,21 @@ /** * Provides a visitor class visiting all AST nodes present in the compiler. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/visitor.d, _visitor.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/package.d, _visitor.d) * Documentation: https://dlang.org/phobos/dmd_visitor.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/visitor.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/package.d */ module dmd.visitor; import dmd.astcodegen; -import dmd.astenums; -import dmd.parsetimevisitor; import dmd.tokens; -import dmd.transitivevisitor; -import dmd.expression; import dmd.rootobject; +import dmd.visitor.parsetime; +import dmd.visitor.transitive; /** * Classic Visitor class which implements visit methods for all the AST @@ -151,10 +149,11 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor { // CTFE can generate struct literals that contain an AddrExp pointing to themselves, // need to avoid infinite recursion. - if (!(e.stageflags & stageToCBuffer)) + alias flag = ASTCodegen.StructLiteralExp.StageFlags.toCBuffer; + if (!(e.stageflags & flag)) { const old = e.stageflags; - e.stageflags |= stageToCBuffer; + e.stageflags |= flag; foreach (el; *e.elements) if (el) el.accept(this); @@ -162,6 +161,11 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor } } + override void visit(ASTCodegen.ClassReferenceExp e) + { + e.value.accept(this); + } + override void visit(ASTCodegen.CompoundLiteralExp e) { if (e.initializer) @@ -245,7 +249,7 @@ extern (C++) class SemanticTimeTransitiveVisitor : SemanticTimePermissiveVisitor override void visit(ASTCodegen.LoweredAssignExp e) { e.lowering.accept(this); - visit(cast(AssignExp)e); + visit(cast(ASTCodegen.AssignExp)e); } } diff --git a/gcc/d/dmd/parsetimevisitor.d b/gcc/d/dmd/visitor/parsetime.d index c03f78d..914ca41 100644 --- a/gcc/d/dmd/parsetimevisitor.d +++ b/gcc/d/dmd/visitor/parsetime.d @@ -7,7 +7,7 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/parsetimevisitor.d */ -module dmd.parsetimevisitor; +module dmd.visitor.parsetime; /** Basic and dumm visitor which implements a visit method for each AST node * implemented in AST. This visitor is the parent of strict, transitive diff --git a/gcc/d/dmd/permissivevisitor.d b/gcc/d/dmd/visitor/permissive.d index 5d7f3fc..ef1f279 100644 --- a/gcc/d/dmd/permissivevisitor.d +++ b/gcc/d/dmd/visitor/permissive.d @@ -5,9 +5,9 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/permissivevisitor.d */ -module dmd.permissivevisitor; +module dmd.visitor.permissive; -import dmd.parsetimevisitor; +import dmd.visitor.parsetime; /** PermissiveVisitor overrides all the visit methods in the parent class * that assert(0) in order to facilitate the traversal of subsets of the AST. diff --git a/gcc/d/dmd/visitor/postorder.d b/gcc/d/dmd/visitor/postorder.d new file mode 100644 index 0000000..22549da --- /dev/null +++ b/gcc/d/dmd/visitor/postorder.d @@ -0,0 +1,318 @@ +/** + * A depth-first visitor for expressions and statements. + * + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved + * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/postorder.d, _apply.d) + * Documentation: https://dlang.org/phobos/dmd_apply.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/postorder.d + */ + +module dmd.visitor.postorder; + +import dmd.dtemplate; +import dmd.expression; +import dmd.root.array; +import dmd.statement; +import dmd.visitor; + +bool walkPostorder(Expression e, StoppableVisitor v) +{ + scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v); + e.accept(pv); + return v.stop; +} + +bool walkPostorder(Statement s, StoppableVisitor v) +{ + scope PostorderStatementVisitor pv = new PostorderStatementVisitor(v); + s.accept(pv); + return v.stop; +} + +private: +/************************************** + * An Expression tree walker that will visit each Expression e in the tree, + * in depth-first evaluation order, and call fp(e,param) on it. + * fp() signals whether the walking continues with its return value: + * Returns: + * 0 continue + * 1 done + * It's a bit slower than using virtual functions, but more encapsulated and less brittle. + * Creating an iterator for this would be much more complex. + */ +extern (C++) final class PostorderExpressionVisitor : StoppableVisitor +{ + alias visit = typeof(super).visit; +public: + StoppableVisitor v; + + extern (D) this(StoppableVisitor v) scope @safe + { + this.v = v; + } + + bool doCond(Expression e) + { + if (!stop && e) + e.accept(this); + return stop; + } + + extern(D) bool doCond(Expression[] e) + { + for (size_t i = 0; i < e.length && !stop; i++) + doCond(e[i]); + return stop; + } + + bool applyTo(Expression e) + { + e.accept(v); + stop = v.stop; + return true; + } + + override void visit(Expression e) + { + applyTo(e); + } + + override void visit(NewExp e) + { + //printf("NewExp::apply(): %s\n", toChars()); + doCond(e.placement) || doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(NewAnonClassExp e) + { + //printf("NewAnonClassExp::apply(): %s\n", toChars()); + doCond(e.placement) || doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(TypeidExp e) + { + doCond(isExpression(e.obj)) || applyTo(e); + } + + override void visit(UnaExp e) + { + doCond(e.e1) || applyTo(e); + } + + override void visit(BinExp e) + { + doCond(e.e1) || doCond(e.e2) || applyTo(e); + } + + override void visit(AssertExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.msg) || applyTo(e); + } + + override void visit(CallExp e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(ArrayExp e) + { + //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e.e1) || doCond(e.arguments.peekSlice()) || applyTo(e); + } + + override void visit(SliceExp e) + { + doCond(e.e1) || doCond(e.lwr) || doCond(e.upr) || applyTo(e); + } + + override void visit(ArrayLiteralExp e) + { + doCond(e.basis) || doCond(e.elements.peekSlice()) || applyTo(e); + } + + override void visit(AssocArrayLiteralExp e) + { + doCond(e.keys.peekSlice()) || doCond(e.values.peekSlice()) || applyTo(e); + } + + override void visit(StructLiteralExp e) + { + if (e.stageflags & StructLiteralExp.StageFlags.apply) + return; + const old = e.stageflags; + e.stageflags |= StructLiteralExp.StageFlags.apply; + doCond(e.elements.peekSlice()) || applyTo(e); + e.stageflags = old; + } + + override void visit(TupleExp e) + { + doCond(e.e0) || doCond(e.exps.peekSlice()) || applyTo(e); + } + + override void visit(CondExp e) + { + doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e); + } +} + +/************************************** + * A Statement tree walker that will visit each Statement s in the tree, + * in depth-first evaluation order, and call fp(s,param) on it. + * fp() signals whether the walking continues with its return value: + * Returns: + * 0 continue + * 1 done + * It's a bit slower than using virtual functions, but more encapsulated and less brittle. + * Creating an iterator for this would be much more complex. + */ +extern (C++) final class PostorderStatementVisitor : StoppableVisitor +{ + alias visit = typeof(super).visit; +public: + StoppableVisitor v; + + extern (D) this(StoppableVisitor v) scope @safe + { + this.v = v; + } + + bool doCond(Statement s) + { + if (!stop && s) + s.accept(this); + return stop; + } + + bool applyTo(Statement s) + { + s.accept(v); + stop = v.stop; + return true; + } + + override void visit(Statement s) + { + applyTo(s); + } + + override void visit(PeelStatement s) + { + doCond(s.s) || applyTo(s); + } + + override void visit(CompoundStatement s) + { + for (size_t i = 0; i < s.statements.length; i++) + if (doCond((*s.statements)[i])) + return; + applyTo(s); + } + + override void visit(UnrolledLoopStatement s) + { + for (size_t i = 0; i < s.statements.length; i++) + if (doCond((*s.statements)[i])) + return; + applyTo(s); + } + + override void visit(ScopeStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(WhileStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(DoStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(ForStatement s) + { + doCond(s._init) || doCond(s._body) || applyTo(s); + } + + override void visit(ForeachStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(ForeachRangeStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(IfStatement s) + { + doCond(s.ifbody) || doCond(s.elsebody) || applyTo(s); + } + + override void visit(PragmaStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(SwitchStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(CaseStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(DefaultStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(SynchronizedStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(WithStatement s) + { + doCond(s._body) || applyTo(s); + } + + override void visit(TryCatchStatement s) + { + if (doCond(s._body)) + return; + for (size_t i = 0; i < s.catches.length; i++) + if (doCond((*s.catches)[i].handler)) + return; + applyTo(s); + } + + override void visit(TryFinallyStatement s) + { + doCond(s._body) || doCond(s.finalbody) || applyTo(s); + } + + override void visit(ScopeGuardStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(DebugStatement s) + { + doCond(s.statement) || applyTo(s); + } + + override void visit(LabelStatement s) + { + doCond(s.statement) || applyTo(s); + } +} diff --git a/gcc/d/dmd/statement_rewrite_walker.d b/gcc/d/dmd/visitor/statement_rewrite_walker.d index 221c502..25e4c73 100644 --- a/gcc/d/dmd/statement_rewrite_walker.d +++ b/gcc/d/dmd/visitor/statement_rewrite_walker.d @@ -1,15 +1,15 @@ /** * Provides a visitor for statements that allows rewriting the currently visited node. * - * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved + * Copyright: Copyright (C) 1999-2025 by The D Language Foundation, All Rights Reserved * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/statement_rewrite_walker.d, _statement_rewrite_walker.d) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/compiler/src/dmd/visitor/statement_rewrite_walker.d, _statement_rewrite_walker.d) * Documentation: https://dlang.org/phobos/dmd_statement_rewrite_walker.html - * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/statement_rewrite_walker.d + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/compiler/src/dmd/visitor/statement_rewrite_walker.d */ -module dmd.statement_rewrite_walker; +module dmd.visitor.statement_rewrite_walker; import core.stdc.stdio; diff --git a/gcc/d/dmd/transitivevisitor.d b/gcc/d/dmd/visitor/transitive.d index bf1d38e..89c2332 100644 --- a/gcc/d/dmd/transitivevisitor.d +++ b/gcc/d/dmd/visitor/transitive.d @@ -3,10 +3,10 @@ * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/transitivevisitor.d */ -module dmd.transitivevisitor; +module dmd.visitor.transitive; import dmd.astenums; -import dmd.permissivevisitor; +import dmd.visitor.permissive; import dmd.tokens; import dmd.rootobject; @@ -26,7 +26,7 @@ extern(C++) class ParseTimeTransitiveVisitor(AST) : PermissiveVisitor!AST * is used for semantic time AST node traversal, so in order to not duplicate the code, * the template mixin is used. */ -package mixin template ParseVisitMethods(AST) +package(dmd.visitor) mixin template ParseVisitMethods(AST) { import dmd.root.array; @@ -153,8 +153,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.ForeachRangeStatement s) { //printf("Visiting ForeachRangeStatement\n"); - if (s.prm.type) - visitType(s.prm.type); + if (s.param.type) + visitType(s.param.type); s.lwr.accept(this); s.upr.accept(this); if (s._body) @@ -174,8 +174,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.IfStatement s) { //printf("Visiting IfStatement\n"); - if (s.prm && s.prm.type) - visitType(s.prm.type); + if (s.param && s.param.type) + visitType(s.param.type); s.condition.accept(this); s.ifbody.accept(this); if (s.elsebody) @@ -854,7 +854,7 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.FuncLiteralDeclaration f) { //printf("Visiting FuncLiteralDeclaration\n"); - if (f.type.ty == Terror) + if (f.type.isTypeError()) return; auto tf = f.type.isTypeFunction(); if (!f.inferRetType && tf.next) @@ -994,6 +994,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.NewExp e) { //printf("Visiting NewExp\n"); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); visitType(e.newtype); @@ -1003,6 +1005,8 @@ package mixin template ParseVisitMethods(AST) override void visit(AST.NewAnonClassExp e) { //printf("Visiting NewAnonClassExp\n"); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); visitArgs(e.arguments.peekSlice()); diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index d055e0b..268a176 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -1,5 +1,5 @@ /* expr.cc -- Lower D frontend expressions to GCC trees. - Copyright (C) 2015-2024 Free Software Foundation, Inc. + Copyright (C) 2015-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -295,14 +295,14 @@ public: this->result_ = d_convert (build_ctype (e->type), build_boolop (code, t1, t2)); } - else if (tb1->isfloating () && tb1->ty != TY::Tvector) + else if (tb1->isFloating () && tb1->ty != TY::Tvector) { /* For floating-point values, identity is defined as the bits in the operands being identical. */ tree t1 = d_save_expr (build_expr (e->e1)); tree t2 = d_save_expr (build_expr (e->e2)); - if (!tb1->iscomplex ()) + if (!tb1->isComplex ()) this->result_ = build_float_identity (code, t1, t2); else { @@ -388,7 +388,7 @@ public: e1.length == e2.length && memcmp(e1.ptr, e2.ptr, size) == 0; Or when generating a NE expression: e1.length != e2.length || memcmp(e1.ptr, e2.ptr, size) != 0; */ - if ((t1elem->isintegral () || t1elem->ty == TY::Tvoid + if ((t1elem->isIntegral () || t1elem->ty == TY::Tvoid || (t1elem->ty == TY::Tstruct && !t1elem->isTypeStruct ()->sym->xeq)) && t1elem->ty == t2elem->ty) @@ -414,7 +414,8 @@ public: if (t1elem->ty != TY::Tstruct || identity_compare_p (t1elem->isTypeStruct ()->sym)) { - tree size = size_mult_expr (t1len, size_int (t1elem->size ())); + tree size = + size_mult_expr (t1len, size_int (dmd::size (t1elem))); result = build_memcmp_call (t1ptr, t2ptr, size); result = build_boolop (code, result, integer_zero_node); @@ -442,7 +443,7 @@ public: The frontend should have already guaranteed that static arrays have same size. */ if (tb1->ty == TY::Tsarray && tb2->ty == TY::Tsarray) - gcc_assert (tb1->size () == tb2->size ()); + gcc_assert (dmd::size (tb1) == dmd::size (tb2)); else { tree tlencmp = build_boolop (code, t1len, t2len); @@ -620,8 +621,8 @@ public: { case EXP::add: case EXP::min: - if ((e->e1->type->isreal () && e->e2->type->isimaginary ()) - || (e->e1->type->isimaginary () && e->e2->type->isreal ())) + if ((e->e1->type->isReal () && e->e2->type->isImaginary ()) + || (e->e1->type->isImaginary () && e->e2->type->isReal ())) { /* If the result is complex, then we can shortcut binary_op. Frontend should have already validated types and sizes. */ @@ -631,7 +632,7 @@ public: if (e->op == EXP::min) t2 = build1 (NEGATE_EXPR, TREE_TYPE (t2), t2); - if (e->e1->type->isreal ()) + if (e->e1->type->isReal ()) this->result_ = complex_expr (build_ctype (e->type), t1, t2); else this->result_ = complex_expr (build_ctype (e->type), t2, t1); @@ -661,12 +662,12 @@ public: } } - code = e->e1->type->isintegral () + code = e->e1->type->isIntegral () ? TRUNC_DIV_EXPR : RDIV_EXPR; break; case EXP::mod: - code = e->e1->type->isfloating () + code = e->e1->type->isFloating () ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR; break; @@ -750,12 +751,12 @@ public: break; case EXP::divAssign: - code = e->e1->type->isintegral () + code = e->e1->type->isIntegral () ? TRUNC_DIV_EXPR : RDIV_EXPR; break; case EXP::modAssign: - code = e->e1->type->isfloating () + code = e->e1->type->isFloating () ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR; break; @@ -917,7 +918,7 @@ public: if (integer_zerop (t2)) { tree size = size_mult_expr (d_array_length (t1), - size_int (etype->size ())); + size_int (dmd::size (etype))); result = build_memset_call (d_array_ptr (t1), size); } else @@ -943,7 +944,8 @@ public: tree t2ptr = d_array_ptr (t2); /* Generate: memcpy(to, from, size) */ - tree size = size_mult_expr (t1len, size_int (etype->size ())); + tree size = + size_mult_expr (t1len, size_int (dmd::size (etype))); tree result = build_memcpy_call (t1ptr, t2ptr, size); /* Insert check that array lengths match and do not overlap. */ @@ -986,7 +988,7 @@ public: { /* Generate: _d_arraycopy() */ this->result_ = build_libcall (LIBCALL_ARRAYCOPY, e->type, 3, - size_int (etype->size ()), + size_int (dmd::size (etype)), d_array_convert (e->e2), d_array_convert (e->e1)); } @@ -1024,7 +1026,6 @@ public: if (tb1->ty == TY::Tstruct) { tree t1 = build_expr (e->e1); - tree t2 = convert_for_assignment (e->e2, e->e1->type, true); StructDeclaration *sd = tb1->isTypeStruct ()->sym; /* Look for struct = 0. */ @@ -1049,25 +1050,8 @@ public: else { /* Simple struct literal assignment. */ - tree init = NULL_TREE; - - /* Fill any alignment holes in the struct using memset. */ - if ((e->op == EXP::construct - || (e->e2->op == EXP::structLiteral && e->op == EXP::blit)) - && (sd->isUnionDeclaration () || !identity_compare_p (sd))) - { - t1 = stabilize_reference (t1); - init = build_memset_call (t1); - } - - /* Elide generating assignment if init is all zeroes. */ - if (init != NULL_TREE && initializer_zerop (t2)) - this->result_ = compound_expr (init, t1); - else - { - tree result = build_assign (modifycode, t1, t2); - this->result_ = compound_expr (init, result); - } + tree t2 = convert_for_assignment (e->e2, e->e1->type, true); + this->result_ = build_assign (modifycode, t1, t2); } return; @@ -1100,7 +1084,7 @@ public: || (e->op == EXP::construct && e->e2->op == EXP::arrayLiteral) || (e->op == EXP::construct && e->e2->op == EXP::call) || (e->op == EXP::construct && !lvalue && postblit) - || (e->op == EXP::blit || e->e1->type->size () == 0)) + || (e->op == EXP::blit || dmd::size (e->e1->type) == 0)) { tree t1 = build_expr (e->e1); tree t2 = convert_for_assignment (e->e2, e->e1->type); @@ -1190,7 +1174,7 @@ public: /* Index the associative array. */ tree result = build_libcall (libcall, dmd::pointerTo (e->type), 4, ptr, tinfo, - size_int (tb1->nextOf ()->size ()), + size_int (dmd::size (tb1->nextOf ())), build_address (key)); if (!e->indexIsInBounds && array_bounds_check ()) @@ -1497,14 +1481,14 @@ public: void visit (PtrExp *e) final override { Type *tnext = NULL; - size_t offset; + dinteger_t offset; tree result; if (e->e1->op == EXP::add) { AddExp *ae = e->e1->isAddExp (); if (ae->e1->op == EXP::address - && ae->e2->isConst () && ae->e2->type->isintegral ()) + && ae->e2->isConst () && ae->e2->type->isIntegral ()) { Expression *ex = ae->e1->isAddrExp ()->e1; tnext = ex->type->toBasetype (); @@ -1620,7 +1604,7 @@ public: if (dve->e1->op == EXP::structLiteral) { StructLiteralExp *sle = dve->e1->isStructLiteralExp (); - sle->useStaticInit = false; + sle->useStaticInit (false); } FuncDeclaration *fd = dve->var->isFuncDeclaration (); @@ -1752,7 +1736,7 @@ public: if (returnvalue != NULL_TREE) exp = compound_expr (exp, returnvalue); - if (tf->isref ()) + if (tf->isRef ()) exp = build_deref (exp); /* Some library calls are defined to return a generic type. @@ -1781,7 +1765,7 @@ public: void visit (DelegateExp *e) final override { - if (e->func->semanticRun == PASS::semantic3done) + if (e->func->semanticRun () == PASS::semantic3done) { /* Add the function as nested function if it belongs to this module. ie: it is a member of this module, or it is a template instance. */ @@ -2072,7 +2056,7 @@ public: void visit (SymOffExp *e) final override { /* Build the address and offset of the symbol. */ - size_t soffset = e->isSymOffExp ()->offset; + dinteger_t soffset = e->isSymOffExp ()->offset; tree result = get_decl_tree (e->var); TREE_USED (result) = 1; @@ -2178,7 +2162,7 @@ public: tree type = build_ctype (e->type); tree length = size_int (sd->dsym->structsize); tree ptr = (sd->dsym->isStructDeclaration () - && sd->dsym->type->isZeroInit (e->loc)) + && dmd::isZeroInit (sd->dsym->type, e->loc)) ? null_pointer_node : build_address (result); this->result_ = d_array_value (type, length, ptr); @@ -2241,18 +2225,24 @@ public: storage class, then the instance is allocated on the stack rather than the heap or using the class specific allocator. */ tree var = build_local_temp (TREE_TYPE (type)); + SET_DECL_ALIGN (var, cd->alignsize * BITS_PER_UNIT); + DECL_USER_ALIGN (var) = 1; new_call = build_nop (type, build_address (var)); setup_exp = modify_expr (var, aggregate_initializer_decl (cd)); } - else if (global.params.ehnogc && e->thrownew) + else if (e->placement != NULL) { - /* Allocating a `@nogc' Exception with `_d_newThrowable' has already - been handled by the front-end. */ - gcc_unreachable (); + /* Generate: placement_expr = typeid(class).init */ + tree placement_expr = build_expr (e->placement); + new_call = build_nop (type, build_address (placement_expr)); + tree class_init = build_vconvert (TREE_TYPE (placement_expr), + aggregate_initializer_decl (cd)); + setup_exp = modify_expr (placement_expr, class_init); } else { - /* Generate: _d_newclass() */ + /* Generate: _d_newclass() + or: _d_newThrowable() */ new_call = build_expr (e->lowering); } @@ -2321,12 +2311,22 @@ public: return; } - /* This case should have been rewritten to `_d_newitemT' during the - semantic phase. */ - gcc_assert (e->lowering); + if (e->placement != NULL) + { + /* Generate: &placement_expr */ + tree placement_expr = build_expr (e->placement); + new_call = build_nop (build_ctype (tb), + build_address (placement_expr)); + } + else + { + /* This case should have been rewritten to `_d_newitemT' during the + semantic phase. */ + gcc_assert (e->lowering); - /* Generate: _d_newitemT() */ - new_call = build_expr (e->lowering); + /* Generate: _d_newitemT() */ + new_call = build_expr (e->lowering); + } if (e->member || !e->arguments) { @@ -2391,7 +2391,7 @@ public: /* Allocating memory for a new pointer. */ TypePointer *tpointer = tb->isTypePointer (); - if (tpointer->next->size () == 0) + if (dmd::size (tpointer->next) == 0) { /* Pointer element size is unknown. */ this->result_ = d_convert (build_ctype (e->type), @@ -2399,12 +2399,22 @@ public: return; } - /* This case should have been rewritten to `_d_newitemT' during the - semantic phase. */ - gcc_assert (e->lowering); + if (e->placement != NULL) + { + /* Generate: &placement_expr */ + tree placement_expr = build_expr (e->placement); + result = build_nop (build_ctype (tb), + build_address (placement_expr)); + } + else + { + /* This case should have been rewritten to `_d_newitemT' during the + semantic phase. */ + gcc_assert (e->lowering); - /* Generate: _d_newitemT() */ - result = build_expr (e->lowering); + /* Generate: _d_newitemT() */ + result = build_expr (e->lowering); + } if (e->arguments && e->arguments->length == 1) { @@ -2429,7 +2439,7 @@ public: CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); result = build_nop (build_ctype (e->type), - build_constructor (aatype, ce)); + build_padded_constructor (aatype, ce)); } else gcc_unreachable (); @@ -2502,7 +2512,7 @@ public: CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } - tree ctor = build_constructor (type, elms); + tree ctor = build_padded_constructor (type, elms); TREE_CONSTANT (ctor) = 1; this->result_ = ctor; return; @@ -2584,8 +2594,10 @@ public: this->result_ = d_array_value (build_ctype (e->type), size_int (0), null_pointer_node); else - this->result_ = build_constructor (make_array_type (tb->nextOf (), 0), - NULL); + { + tree arrtype = make_array_type (tb->nextOf (), 0); + this->result_ = build_padded_constructor (arrtype, NULL); + } return; } @@ -2626,7 +2638,7 @@ public: /* Now return the constructor as the correct type. For static arrays there is nothing else to do. For dynamic arrays, return a two field struct. For pointers, return the address. */ - tree ctor = build_constructor (satype, elms); + tree ctor = build_padded_constructor (satype, elms); tree type = build_ctype (e->type); /* Nothing else to do for static arrays. */ @@ -2655,22 +2667,6 @@ public: if (constant_p && initializer_constant_valid_p (ctor, TREE_TYPE (ctor))) TREE_STATIC (ctor) = 1; - /* Use memset to fill any alignment holes in the array. */ - if (!this->constp_ && !this->literalp_) - { - TypeStruct *ts = etype->baseElemOf ()->isTypeStruct (); - - if (ts != NULL && (!identity_compare_p (ts->sym) - || ts->sym->isUnionDeclaration ())) - { - tree var = build_local_temp (TREE_TYPE (ctor)); - tree init = build_memset_call (var); - /* Evaluate memset() first, then any saved elements. */ - saved_elems = compound_expr (init, saved_elems); - ctor = compound_expr (modify_expr (var, ctor), var); - } - } - this->result_ = compound_expr (saved_elems, d_convert (type, ctor)); } else if (e->onstack) @@ -2692,10 +2688,13 @@ public: /* Now copy the constructor into memory. */ tree size = size_mult_expr (size_int (e->elements->length), - size_int (tb->nextOf ()->size ())); + size_int (dmd::size (tb->nextOf ()))); tree result = build_memcpy_call (mem, build_address (ctor), size); + /* Fill any alignment holes in the array. */ + result = compound_expr (result, build_clear_padding_call (mem)); + /* Return the array pointed to by MEM. */ result = compound_expr (result, mem); @@ -2727,7 +2726,7 @@ public: TypeAArray *ta = tb->isTypeAArray (); if (e->keys->length == 0) { - this->result_ = build_constructor (build_ctype (ta), NULL); + this->result_ = build_padded_constructor (build_ctype (ta), NULL); return; } @@ -2759,7 +2758,7 @@ public: CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); tree result = build_nop (build_ctype (e->type), - build_constructor (aatype, ce)); + build_padded_constructor (aatype, ce)); this->result_ = compound_expr (init, result); } @@ -2770,13 +2769,13 @@ public: /* Handle empty struct literals. */ if (e->elements == NULL || e->sd->fields.length == 0) { - this->result_ = build_constructor (build_ctype (e->type), NULL); + this->result_ = build_padded_constructor (build_ctype (e->type), NULL); return; } /* Building sinit trees are delayed until after frontend semantic processing has complete. Build the static initializer now. */ - if (e->useStaticInit && !this->constp_ && !e->sd->isCsymbol ()) + if (e->useStaticInit () && !this->constp_ && !e->sd->isCsymbol ()) { tree init = aggregate_initializer_decl (e->sd); @@ -2821,7 +2820,7 @@ public: elem = d_save_expr (elem); if (initializer_zerop (elem)) - value = build_constructor (build_ctype (ftype), NULL); + value = build_padded_constructor (build_ctype (ftype), NULL); else value = build_array_from_val (ftype, elem); } @@ -2844,7 +2843,7 @@ public: tree field = get_symbol_decl (e->sd->vthis); tree value = build_vthis (e->sd); CONSTRUCTOR_APPEND_ELT (ve, field, value); - gcc_assert (e->useStaticInit == false); + gcc_assert (e->useStaticInit () == false); } /* Build a constructor in the correct shape of the aggregate type. */ @@ -2869,18 +2868,6 @@ public: tree var = build_deref (e->sym); ctor = compound_expr (modify_expr (var, ctor), var); } - else if (!this->literalp_) - { - /* Use memset to fill any alignment holes in the object. */ - if (!identity_compare_p (e->sd) || e->sd->isUnionDeclaration ()) - { - tree var = build_local_temp (TREE_TYPE (ctor)); - tree init = build_memset_call (var); - /* Evaluate memset() first, then any saved element constructors. */ - saved_elems = compound_expr (init, saved_elems); - ctor = compound_expr (modify_expr (var, ctor), var); - } - } this->result_ = compound_expr (saved_elems, ctor); } @@ -2920,7 +2907,7 @@ public: if (constant_p) this->result_ = build_vector_from_ctor (type, elms); else - this->result_ = build_constructor (type, elms); + this->result_ = build_padded_constructor (type, elms); } else if (e->e1->type->toBasetype ()->ty == TY::Tsarray) { @@ -3060,7 +3047,7 @@ build_return_dtor (Expression *e, Type *type, TypeFunction *tf) tree result = build_expr (e); /* Convert for initializing the DECL_RESULT. */ - if (tf->isref ()) + if (tf->isRef ()) { /* If we are returning a reference, take the address. */ result = convert_expr (result, e->type, type); diff --git a/gcc/d/gdc.texi b/gcc/d/gdc.texi index fe1c625..3a8bea0 100644 --- a/gcc/d/gdc.texi +++ b/gcc/d/gdc.texi @@ -14,7 +14,7 @@ @include gcc-common.texi @c Copyright years for this manual. -@set copyrights-d 2006-2024 +@set copyrights-d 2006-2025 @copying @c man begin COPYRIGHT @@ -273,8 +273,16 @@ Sets @code{__traits(getTargetInfo, "cppStd")} to @code{201703}. This is the default. @item c++20 Sets @code{__traits(getTargetInfo, "cppStd")} to @code{202002}. +@item c++23 +Sets @code{__traits(getTargetInfo, "cppStd")} to @code{202302}. @end table +@opindex finclude-imports +@item -finclude-imports +Include imported modules in the compilation, as if they were given on the +command line. When this option is enabled, all imported modules are compiled +except those that are part of libphobos. + @opindex finvariants @opindex fno-invariants @item -fno-invariants diff --git a/gcc/d/implement-d.texi b/gcc/d/implement-d.texi index 770189c..a39fd58 100644 --- a/gcc/d/implement-d.texi +++ b/gcc/d/implement-d.texi @@ -1,5 +1,5 @@ @ignore -Copyright (C) 2022-2024 Free Software Foundation, Inc. +Copyright (C) 2022-2025 Free Software Foundation, Inc. This is part of the GNU D manual. For copying conditions, see the file gdc.texi. @end ignore @@ -1892,6 +1892,10 @@ This version is defined by the GNU D compiler. If all you need to know is whether or not your D program is being compiled by GDC, or a non-GDC compiler, you can simply test @code{version(GNU)}. +@item GNU_CET +This version is defined when @option{-fcf-protection} is used. The protection +level is also set in @code{__traits(getTargetInfo, "CET")} (@pxref{Traits}). + @item GNU_DWARF2_Exceptions @itemx GNU_SEH_Exceptions @itemx GNU_SjLj_Exceptions @@ -2121,6 +2125,10 @@ recognize. These are documented by the D language specification hosted at The following keys are recognized by GNU D. @table @code +@item CET +When @option{-fcf-protection} is used, the first bit is set to 1 for the value +@code{branch} and the second bit is set to 1 for the value @code{return}. + @item cppRuntimeLibrary The C++ runtime library affinity for this toolchain. diff --git a/gcc/d/imports.cc b/gcc/d/imports.cc index 074d9e6..16e4df6 100644 --- a/gcc/d/imports.cc +++ b/gcc/d/imports.cc @@ -1,5 +1,5 @@ /* imports.cc -- Build imported modules/declarations. - Copyright (C) 2014-2024 Free Software Foundation, Inc. + Copyright (C) 2014-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -77,7 +77,7 @@ public: void visit (Module *m) final override { Loc loc = (m->md != NULL) ? m->md->loc - : Loc (m->srcfile.toChars (), 1, 0); + : Loc::singleFilename (m->srcfile.toChars ()); this->result_ = build_decl (make_location_t (loc), NAMESPACE_DECL, get_identifier (m->toPrettyChars ()), @@ -130,7 +130,7 @@ public: void visit (VarDeclaration *d) final override { /* Not all kinds of manifest constants create a CONST_DECL. */ - if (!d->canTakeAddressOf () && !d->type->isscalar ()) + if (!d->canTakeAddressOf () && !d->type->isScalar ()) return; visit ((Declaration *) d); @@ -182,7 +182,11 @@ public: vec_alloc (tset, d->a.length); for (size_t i = 0; i < d->a.length; i++) - vec_safe_push (tset, build_import_decl (d->a[i])); + { + tree overload = build_import_decl (d->a[i]); + if (overload != NULL_TREE) + vec_safe_push (tset, overload); + } this->result_ = build_tree_list_vec (tset); tset->truncate (0); diff --git a/gcc/d/intrinsics.cc b/gcc/d/intrinsics.cc index c895c1a..e2b6fe1 100644 --- a/gcc/d/intrinsics.cc +++ b/gcc/d/intrinsics.cc @@ -1,5 +1,5 @@ /* intrinsics.cc -- D language compiler intrinsics. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/intrinsics.def b/gcc/d/intrinsics.def index 24c9830..083613b 100644 --- a/gcc/d/intrinsics.def +++ b/gcc/d/intrinsics.def @@ -1,5 +1,5 @@ /* intrinsics.def -- Definitions for D compiler intrinsics. - Copyright (C) 2014-2024 Free Software Foundation, Inc. + Copyright (C) 2014-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/lang-specs.h b/gcc/d/lang-specs.h index 6f3ff2f..64e7a5f 100644 --- a/gcc/d/lang-specs.h +++ b/gcc/d/lang-specs.h @@ -1,5 +1,5 @@ /* lang-specs.h -- GCC driver specs for D frontend. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -22,7 +22,7 @@ along with GCC; see the file COPYING3. If not see {".dd", "@d", 0, 1, 0 }, {".di", "@d", 0, 1, 0 }, {"@d", - "%{!E:d21 %i %(cc1_options) %I %{nostdinc*} %{i*} %{I*} %{J*} \ + "%{!E:d21 %i %(cc1_options) %I %{nostdinc} %{i*} %{I*} %{J*} \ %{H} %{Hd*} %{Hf*} %{MD:-MD %b.deps} %{MMD:-MMD %b.deps} \ %{M} %{MM} %{MF*} %{MG} %{MP} %{MQ*} %{MT*} \ %{X:-Xf %b.json} %{Xf*} \ diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index 2a92489..298ff58 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -1,5 +1,5 @@ ; lang.opt -- Options for the D front end. -; Copyright (C) 2006-2024 Free Software Foundation, Inc. +; Copyright (C) 2006-2025 Free Software Foundation, Inc. ; ; GCC is free software; you can redistribute it and/or modify it under ; the terms of the GNU General Public License as published by the Free @@ -320,10 +320,17 @@ Enum(extern_stdcpp) String(c++17) Value(201703) EnumValue Enum(extern_stdcpp) String(c++20) Value(202002) +EnumValue +Enum(extern_stdcpp) String(c++23) Value(202302) + fignore-unknown-pragmas D Ignore unsupported pragmas. +finclude-imports +D RejectNegative +Include imported modules in the compilation. + finvariants D Var(flag_invariants) Generate code for class invariant contracts. @@ -400,6 +407,10 @@ fpreview=nosharedaccess D RejectNegative Disable access to shared memory objects. +fpreview=safer +D RejectNegative +Enable safety checks on all functions by default. + fpreview=rvaluerefparam D RejectNegative Enable rvalue arguments to ref parameters. diff --git a/gcc/d/lang.opt.urls b/gcc/d/lang.opt.urls index 09b2a26..b4886bf 100644 --- a/gcc/d/lang.opt.urls +++ b/gcc/d/lang.opt.urls @@ -1,7 +1,7 @@ ; Autogenerated by regenerate-opt-urls.py from gcc/d/lang.opt and generated HTML H -UrlSuffix(gcc/Preprocessor-Options.html#index-H) LangUrlSuffix_D(gdc/Code-Generation.html#index-H) +UrlSuffix(gcc/Preprocessor-Options.html#index-H) LangUrlSuffix_D(gdc/Code-Generation.html#index-H) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-H) Hd LangUrlSuffix_D(gdc/Code-Generation.html#index-Hd) @@ -49,7 +49,7 @@ UrlSuffix(gcc/Warning-Options.html#index-Waddress) ; duplicate: 'gcc/Standard-Libraries.html#index-Wall-1' ; duplicate: 'gcc/Warning-Options.html#index-Wall' Wall -LangUrlSuffix_D(gdc/Warnings.html#index-Wall) +LangUrlSuffix_D(gdc/Warnings.html#index-Wall) LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-Wall) Walloca UrlSuffix(gcc/Warning-Options.html#index-Walloca) LangUrlSuffix_D(gdc/Warnings.html#index-Walloca) @@ -67,14 +67,17 @@ Wdeprecated UrlSuffix(gcc/Warning-Options.html#index-Wdeprecated) LangUrlSuffix_D(gdc/Warnings.html#index-Wdeprecated) Werror -UrlSuffix(gcc/Warning-Options.html#index-Werror) LangUrlSuffix_D(gdc/Warnings.html#index-Werror) +UrlSuffix(gcc/Warning-Options.html#index-Werror) LangUrlSuffix_D(gdc/Warnings.html#index-Werror) LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-Werror) Wextra -UrlSuffix(gcc/Warning-Options.html#index-Wextra) LangUrlSuffix_D(gdc/Warnings.html#index-Wextra) +UrlSuffix(gcc/Warning-Options.html#index-Wextra) LangUrlSuffix_D(gdc/Warnings.html#index-Wextra) LangUrlSuffix_Fortran(gfortran/Error-and-Warning-Options.html#index-Wextra) Wmismatched-special-enum LangUrlSuffix_D(gdc/Warnings.html#index-Wmismatched-special-enum) +Wpsabi +UrlSuffix(gcc/Warning-Options.html#index-Wno-psabi) + Wspeculative LangUrlSuffix_D(gdc/Warnings.html#index-Wno-speculative) @@ -103,14 +106,14 @@ fassert LangUrlSuffix_D(gdc/Runtime-Options.html#index-fassert) fbounds-check -LangUrlSuffix_D(gdc/Runtime-Options.html#index-fbounds-check) +LangUrlSuffix_D(gdc/Runtime-Options.html#index-fbounds-check) LangUrlSuffix_Fortran(gfortran/Code-Gen-Options.html#index-fbounds-check) fbounds-check= -LangUrlSuffix_D(gdc/Runtime-Options.html#index-fbounds-check) +LangUrlSuffix_D(gdc/Runtime-Options.html#index-fbounds-check) LangUrlSuffix_Fortran(gfortran/Code-Gen-Options.html#index-fbounds-check) ; skipping UrlSuffix for 'fbuiltin' due to multiple URLs: ; duplicate: 'gcc/C-Dialect-Options.html#index-fbuiltin' -; duplicate: 'gcc/Other-Builtins.html#index-fno-builtin-3' +; duplicate: 'gcc/Library-Builtins.html#index-fno-builtin-3' ; duplicate: 'gcc/Warning-Options.html#index-fno-builtin-1' ; skipping LangUrlSuffix_D for 'fbuiltin' due to multiple URLs: ; duplicate: 'gdc/Other-Builtins.html#index-fno-builtin-1' @@ -152,6 +155,9 @@ LangUrlSuffix_D(gdc/Runtime-Options.html#index-fextern-std) fignore-unknown-pragmas LangUrlSuffix_D(gdc/Warnings.html#index-fignore-unknown-pragmas) +finclude-imports +LangUrlSuffix_D(gdc/Runtime-Options.html#index-finclude-imports) + finvariants LangUrlSuffix_D(gdc/Runtime-Options.html#index-finvariants) @@ -195,22 +201,22 @@ fweak-templates LangUrlSuffix_D(gdc/Runtime-Options.html#index-fno-weak-templates) imultilib -UrlSuffix(gcc/Directory-Options.html#index-imultilib) LangUrlSuffix_D(gdc/Directory-Options.html#index-imultilib) +UrlSuffix(gcc/Directory-Options.html#index-imultilib) LangUrlSuffix_D(gdc/Directory-Options.html#index-imultilib) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-imultilib) iprefix -UrlSuffix(gcc/Directory-Options.html#index-iprefix) LangUrlSuffix_D(gdc/Directory-Options.html#index-iprefix) +UrlSuffix(gcc/Directory-Options.html#index-iprefix) LangUrlSuffix_D(gdc/Directory-Options.html#index-iprefix) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-iprefix) isysroot -UrlSuffix(gcc/Directory-Options.html#index-isysroot) +UrlSuffix(gcc/Directory-Options.html#index-isysroot) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-isysroot) isystem -UrlSuffix(gcc/Directory-Options.html#index-isystem) +UrlSuffix(gcc/Directory-Options.html#index-isystem) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-isystem) nophoboslib LangUrlSuffix_D(gdc/Linking.html#index-nophoboslib) nostdinc -UrlSuffix(gcc/Directory-Options.html#index-nostdinc) LangUrlSuffix_D(gdc/Directory-Options.html#index-nostdinc) +UrlSuffix(gcc/Directory-Options.html#index-nostdinc) LangUrlSuffix_D(gdc/Directory-Options.html#index-nostdinc) LangUrlSuffix_Fortran(gfortran/Preprocessing-Options.html#index-nostdinc) static-libphobos LangUrlSuffix_D(gdc/Linking.html#index-static-libphobos) diff --git a/gcc/d/longdouble.h b/gcc/d/longdouble.h index 52e8e1d..37f4474 100644 --- a/gcc/d/longdouble.h +++ b/gcc/d/longdouble.h @@ -1,5 +1,5 @@ /* longdouble.h -- Definitions of floating-point access for the frontend. - Copyright (C) 2015-2024 Free Software Foundation, Inc. + Copyright (C) 2015-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/modules.cc b/gcc/d/modules.cc index b111ee9..14e4a48 100644 --- a/gcc/d/modules.cc +++ b/gcc/d/modules.cc @@ -1,5 +1,5 @@ /* modules.cc -- D module initialization and termination. - Copyright (C) 2013-2024 Free Software Foundation, Inc. + Copyright (C) 2013-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "function.h" #include "cgraph.h" #include "stor-layout.h" +#include "debug.h" #include "toplev.h" #include "target.h" #include "common/common-target.h" @@ -143,13 +144,13 @@ get_internal_fn (tree ident, const Visibility &visibility) name = IDENTIFIER_POINTER (s); } - FuncDeclaration *fd = FuncDeclaration::genCfunc (NULL, Type::tvoid, - Identifier::idPool (name)); + FuncDeclaration *fd = dmd::genCfunc (NULL, Type::tvoid, + Identifier::idPool (name)); fd->isGenerated (true); - fd->loc = Loc (mod->srcfile.toChars (), 1, 0); + fd->loc = Loc::singleFilename (mod->srcfile.toChars ()); fd->parent = mod; fd->visibility = visibility; - fd->semanticRun = PASS::semantic3done; + fd->semanticRun (PASS::semantic3done); return fd; } @@ -667,7 +668,7 @@ layout_moduleinfo (Module *decl) CONSTRUCTOR_APPEND_ELT (minit, NULL_TREE, size_int (aimports_dim)); CONSTRUCTOR_APPEND_ELT (minit, NULL_TREE, - build_constructor (satype, elms)); + build_padded_constructor (satype, elms)); } if (flags & MIlocalClasses) @@ -684,7 +685,7 @@ layout_moduleinfo (Module *decl) CONSTRUCTOR_APPEND_ELT (minit, NULL_TREE, size_int (aclasses.length)); CONSTRUCTOR_APPEND_ELT (minit, NULL_TREE, - build_constructor (satype, elms)); + build_padded_constructor (satype, elms)); } if (flags & MIname) @@ -927,6 +928,14 @@ d_finish_compilation (tree *vec, int len) /* Complete all generated thunks. */ symtab->process_same_body_aliases (); + /* Output debug information for all type declarations in this unit. */ + for (int i = 0; i < len; i++) + { + tree decl = vec[i]; + if (TREE_CODE (decl) == TYPE_DECL) + debug_hooks->type_decl (decl, false); + } + /* Process all file scopes in this compilation, and the external_scope, through wrapup_global_declarations. */ for (int i = 0; i < len; i++) diff --git a/gcc/d/runtime.cc b/gcc/d/runtime.cc index 8a64c52..d337669 100644 --- a/gcc/d/runtime.cc +++ b/gcc/d/runtime.cc @@ -1,5 +1,5 @@ /* runtime.cc -- D runtime functions called by generated code. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def index 6c1d44f..4aff0a6 100644 --- a/gcc/d/runtime.def +++ b/gcc/d/runtime.def @@ -1,5 +1,5 @@ /* runtime.def -- Definitions for D runtime functions. - Copyright (C) 2014-2024 Free Software Foundation, Inc. + Copyright (C) 2014-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -142,8 +142,8 @@ DEF_D_RUNTIME (CXA_END_CATCH, "__cxa_end_catch", RT(VOID), P0(), 0) /* When invariant() contracts are turned on, used after testing whether a class != null for validating the state of a class. */ -DEF_D_RUNTIME (INVARIANT, "_D9invariant12_d_invariantFC6ObjectZv", RT(VOID), - P1(OBJECT), 0) +DEF_D_RUNTIME (INVARIANT, "_D2rt10invariant_12_d_invariantFC6ObjectZv", + RT(VOID), P1(OBJECT), 0) #undef P0 #undef P1 diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index 9f5531c..b70db7a 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -1,5 +1,5 @@ /* toir.cc -- Lower D frontend statements to GCC trees. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -831,7 +831,7 @@ public: /* A switch statement on a string gets turned into a library call. It is not lowered during codegen. */ - if (!condtype->isscalar ()) + if (!condtype->isScalar ()) { error ("cannot handle switch condition of type %s", condtype->toChars ()); @@ -920,7 +920,7 @@ public: else { tree casevalue; - if (s->exp->type->isscalar ()) + if (s->exp->type->isScalar ()) casevalue = build_expr (s->exp); else casevalue = build_integer_cst (s->index, build_ctype (Type::tint32)); @@ -1008,7 +1008,7 @@ public: /* Returning by hidden reference, store the result into the retval decl. The result returned then becomes the retval reference itself. */ tree decl = DECL_RESULT (get_symbol_decl (this->func_)); - gcc_assert (!tf->isref ()); + gcc_assert (!tf->isRef ()); /* If returning via NRVO, just refer to the DECL_RESULT; this differs from using NULL_TREE in that it indicates that we care about the @@ -1058,13 +1058,8 @@ public: if (sle != NULL) { - StructDeclaration *sd = type->baseElemOf ()->isTypeStruct ()->sym; sle->sym = build_address (this->func_->shidden); using_rvo_p = true; - - /* Fill any alignment holes in the return slot using memset. */ - if (!identity_compare_p (sd) || sd->isUnionDeclaration ()) - add_stmt (build_memset_call (this->func_->shidden)); } if (using_rvo_p == true) @@ -1491,10 +1486,9 @@ public: outputs, inputs, clobbers, labels); SET_EXPR_LOCATION (exp, make_location_t (s->loc)); - /* If the extended syntax was not used, mark the ASM_EXPR as being an - ASM_INPUT expression instead of an ASM_OPERAND with no operands. */ + /* Record whether the basic rather than extended syntax was used. */ if (s->args == NULL && s->clobbers == NULL) - ASM_INPUT_P (exp) = 1; + ASM_BASIC_P (exp) = 1; /* All asm statements are assumed to have a side effect. As a future optimization, this could be unset when building in release mode. */ diff --git a/gcc/d/typeinfo.cc b/gcc/d/typeinfo.cc index cadcbe8..4c3e9e4 100644 --- a/gcc/d/typeinfo.cc +++ b/gcc/d/typeinfo.cc @@ -1,5 +1,5 @@ /* typeinfo.cc -- D runtime type identification. - Copyright (C) 2013-2024 Free Software Foundation, Inc. + Copyright (C) 2013-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #include "dmd/scope.h" #include "dmd/template.h" #include "dmd/target.h" +#include "dmd/typinf.h" #include "tree.h" #include "fold-const.h" @@ -258,10 +259,10 @@ create_tinfo_types (Module *mod) Identifier::idPool ("TypeInfo_Class"), array_type_node, array_type_node, array_type_node, array_type_node, ptr_type_node, ptr_type_node, - ptr_type_node, d_uint_type, ptr_type_node, - array_type_node, ptr_type_node, ptr_type_node, - d_uint_type, d_uint_type, d_uint_type, d_uint_type, - NULL); + ptr_type_node, d_ushort_type, d_ushort_type, + ptr_type_node, array_type_node, ptr_type_node, + ptr_type_node, d_uint_type, d_uint_type, d_uint_type, + d_uint_type, NULL); object_module = mod; } @@ -488,14 +489,14 @@ class TypeInfoVisitor : public Visitor CONSTRUCTOR_APPEND_ELT (v, size_int (3), size_int (b->offset)); /* Add to the array of interfaces. */ - value = build_constructor (vtbl_interface_type_node, v); + value = build_padded_constructor (vtbl_interface_type_node, v); CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); } tree domain = size_int (cd->vtblInterfaces->length - 1); tree arrtype = build_array_type (vtbl_interface_type_node, build_index_type (domain)); - return build_constructor (arrtype, elms); + return build_padded_constructor (arrtype, elms); } /* Write out the interfacing vtable[] of base class BCD that will be accessed @@ -541,7 +542,7 @@ class TypeInfoVisitor : public Visitor tree vtbldomain = build_index_type (size_int (id->vtbl.length - 1)); tree vtbltype = build_array_type (vtable_entry_type, vtbldomain); - tree value = build_constructor (vtbltype, elms); + tree value = build_padded_constructor (vtbltype, elms); this->layout_field (value); } @@ -662,9 +663,9 @@ public: this->layout_string (ed->toPrettyChars ()); /* Default initializer for enum. */ - if (ed->members && !d->tinfo->isZeroInit ()) + if (ed->members && !dmd::isZeroInit (d->tinfo)) { - tree length = size_int (ed->type->size ()); + tree length = size_int (dmd::size (ed->type)); tree ptr = build_address (enum_initializer_decl (ed)); this->layout_field (d_array_value (array_type_node, length, ptr)); } @@ -728,7 +729,8 @@ public: void **__vptr; void *__monitor; TypeInfo value; - TypeInfo key; */ + TypeInfo key; + TypeInfo entry; */ void visit (TypeInfoAssociativeArrayDeclaration *d) final override { @@ -742,6 +744,12 @@ public: /* TypeInfo for index of type. */ this->layout_field (build_typeinfo (d->loc, ti->index)); + + /* TypeInfo for the key/value pair. */ + if (d->entry != NULL) + this->layout_field (build_typeinfo (d->loc, d->entry)); + else + this->layout_field (null_pointer_node); } /* Layout of TypeInfo_Vector is: @@ -813,6 +821,7 @@ public: void *destructor; void function(Object) classInvariant; ClassFlags m_flags; + ushort depth; void *deallocator; OffsetTypeInfo[] m_offTi; void function(Object) defaultConstructor; @@ -918,7 +927,10 @@ public: flags |= ClassFlags::noPointers; Lhaspointers: - this->layout_field (build_integer_cst (flags, d_uint_type)); + this->layout_field (build_integer_cst (flags, d_ushort_type)); + + /* ushort depth; (not implemented) */ + this->layout_field (build_zero_cst (d_ushort_type)); /* void *deallocator; */ this->layout_field (null_pointer_node); @@ -979,7 +991,10 @@ public: if (cd->isCOMinterface ()) flags |= ClassFlags::isCOMclass; - this->layout_field (build_integer_cst (flags, d_uint_type)); + this->layout_field (build_integer_cst (flags, d_ushort_type)); + + /* ushort depth; (not implemented) */ + this->layout_field (build_zero_cst (d_ushort_type)); /* void *deallocator; OffsetTypeInfo[] m_offTi; (not implemented) @@ -1145,7 +1160,7 @@ public: CONSTRUCTOR_APPEND_ELT (elms, size_int (i), build_typeinfo (d->loc, arg->type)); } - tree ctor = build_constructor (build_ctype (satype), elms); + tree ctor = build_padded_constructor (build_ctype (satype), elms); tree decl = this->internal_reference (ctor); tree length = size_int (ti->arguments->length); @@ -1415,7 +1430,7 @@ check_typeinfo_type (const Loc &loc, Scope *sc, Expression *expr) { /* Even when compiling without RTTI we should still be able to evaluate TypeInfo at compile-time, just not at run-time. */ - if (!sc || !(sc->flags & unsigned(SCOPE::ctfe))) + if (!sc || !sc->ctfe ()) { static int warned = 0; @@ -1540,12 +1555,10 @@ get_cpp_typeinfo_decl (ClassDeclaration *decl) return decl->cpp_type_info_ptr_sym; } -/* Get the exact TypeInfo for TYPE, if it doesn't exist, create it. - When GENERATE is true, push the TypeInfo as a member of MOD so that it will - get code generation. */ +/* Get the exact TypeInfo for TYPE, if it doesn't exist, create it. */ void -create_typeinfo (Type *type, Module *mod, bool generate) +create_typeinfo (Type *type, Scope *sc) { if (!Type::dtypeinfo) create_frontend_tinfo_types (); @@ -1554,6 +1567,9 @@ create_typeinfo (Type *type, Module *mod, bool generate) Type *t = dmd::merge2 (type); Identifier *ident; + if (TypeAArray *ta = t->isTypeAArray ()) + t = dmd::makeNakedAssociativeArray (ta); + if (!t->vtinfo) { tinfo_kind tk = get_typeinfo_kind (t); @@ -1631,9 +1647,11 @@ create_typeinfo (Type *type, Module *mod, bool generate) { ident = Identifier::idPool ("TypeInfo_AssociativeArray"); make_internal_typeinfo (tk, ident, ptr_type_node, ptr_type_node, - NULL); + ptr_type_node, NULL); } - t->vtinfo = TypeInfoAssociativeArrayDeclaration::create (t); + t->vtinfo = sc && have_typeinfo_p (Type::typeinfoassociativearray) + ? dmd::getTypeInfoAssocArrayDeclaration (t->isTypeAArray (), sc) + : TypeInfoAssociativeArrayDeclaration::create (t); break; case TK_STRUCT_TYPE: @@ -1703,9 +1721,10 @@ create_typeinfo (Type *type, Module *mod, bool generate) /* If this has a custom implementation in rt/typeinfo, then do not generate a COMDAT for it. */ - if (generate && !builtin_typeinfo_p (t)) + if (!builtin_typeinfo_p (t)) { /* Find module that will go all the way to an object file. */ + Module *mod = sc ? sc->_module->importedFrom : NULL; if (mod) mod->members->push (t->vtinfo); else @@ -1719,113 +1738,4 @@ create_typeinfo (Type *type, Module *mod, bool generate) gcc_assert (type->vtinfo != NULL); } -/* Implements a visitor interface to check whether a type is speculative. - TypeInfo_Struct would reference the members of the struct it is representing - (e.g: opEquals via xopEquals field), so if it's instantiated in speculative - context, TypeInfo creation should also be stopped to avoid possible - `unresolved symbol' linker errors. */ - -class SpeculativeTypeVisitor : public Visitor -{ - using Visitor::visit; - - bool result_; - -public: - SpeculativeTypeVisitor (void) - { - this->result_ = false; - } - - bool result (void) - { - return this->result_; - } - - void visit (Type *t) final override - { - Type *tb = t->toBasetype (); - if (tb != t) - tb->accept (this); - } - - void visit (TypeNext *t) final override - { - if (t->next) - t->next->accept (this); - } - - void visit (TypeBasic *) final override - { - } - - void visit (TypeVector *t) final override - { - t->basetype->accept (this); - } - - void visit (TypeAArray *t) final override - { - t->index->accept (this); - visit ((TypeNext *) t); - } - - void visit (TypeFunction *t) final override - { - visit ((TypeNext *) t); - } - - void visit (TypeStruct *t) final override - { - StructDeclaration *sd = t->sym; - if (TemplateInstance *ti = sd->isInstantiated ()) - { - if (!ti->needsCodegen ()) - { - if (ti->minst || sd->requestTypeInfo ()) - return; - - this->result_ |= true; - } - } - } - - void visit (TypeClass *t) final override - { - ClassDeclaration *cd = t->sym; - if (TemplateInstance *ti = cd->isInstantiated ()) - { - if (!ti->needsCodegen () && !ti->minst) - { - this->result_ |= true; - } - } - } - - void visit (TypeTuple *t) final override - { - if (!t->arguments) - return; - - for (size_t i = 0; i < t->arguments->length; i++) - { - Type *tprm = (*t->arguments)[i]->type; - if (tprm) - tprm->accept (this); - if (this->result_) - return; - } - } -}; - -/* Return true if type was instantiated in a speculative context. */ - -bool -speculative_type_p (Type *t) -{ - SpeculativeTypeVisitor v = SpeculativeTypeVisitor (); - t->accept (&v); - return v.result (); -} - #include "gt-d-typeinfo.h" diff --git a/gcc/d/types.cc b/gcc/d/types.cc index 9fa2f88..1c74840 100644 --- a/gcc/d/types.cc +++ b/gcc/d/types.cc @@ -1,5 +1,5 @@ /* types.cc -- Lower D frontend types to GCC trees. - Copyright (C) 2006-2024 Free Software Foundation, Inc. + Copyright (C) 2006-2025 Free Software Foundation, Inc. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,7 +33,6 @@ along with GCC; see the file COPYING3. If not see #include "langhooks.h" #include "tm.h" #include "function.h" -#include "toplev.h" #include "target.h" #include "stringpool.h" #include "stor-layout.h" @@ -481,7 +480,7 @@ layout_aggregate_members (Dsymbols *members, tree context, bool inherited_p) AttribDeclaration *attrib = sym->isAttribDeclaration (); if (attrib != NULL) { - Dsymbols *decls = attrib->include (NULL); + Dsymbols *decls = dmd::include (attrib, NULL); if (decls != NULL) { fields += layout_aggregate_members (decls, context, inherited_p); @@ -704,18 +703,13 @@ finish_aggregate_type (unsigned structsize, unsigned alignsize, tree type) TYPE_LANG_SPECIFIC (t) = TYPE_LANG_SPECIFIC (type); TYPE_SIZE (t) = TYPE_SIZE (type); TYPE_SIZE_UNIT (t) = TYPE_SIZE_UNIT (type); - TYPE_PACKED (type) = TYPE_PACKED (type); + TYPE_PACKED (t) = TYPE_PACKED (type); SET_TYPE_ALIGN (t, TYPE_ALIGN (type)); TYPE_USER_ALIGN (t) = TYPE_USER_ALIGN (type); } - /* Finish debugging output for this type. */ - rest_of_type_compilation (type, TYPE_FILE_SCOPE_P (type)); + /* Complete any other forward-referenced fields of this aggregate type. */ finish_incomplete_fields (type); - - /* Finish processing of TYPE_DECL. */ - rest_of_decl_compilation (TYPE_NAME (type), - DECL_FILE_SCOPE_P (TYPE_NAME (type)), 0); } /* Returns true if the class or struct type TYPE has already been layed out by @@ -892,7 +886,7 @@ public: void visit (TypeSArray *t) final override { - if (t->dim->isConst () && t->dim->type->isintegral ()) + if (t->dim->isConst () && t->dim->type->isIntegral ()) { uinteger_t size = t->dim->toUInteger (); t->ctype = make_array_type (t->next, size); @@ -975,7 +969,7 @@ public: if (t->next != NULL) { fntype = build_ctype (t->next); - if (t->isref ()) + if (t->isRef ()) fntype = build_reference_type (fntype); } else @@ -1181,17 +1175,32 @@ public: TYPE_UNSIGNED (t->ctype) = TYPE_UNSIGNED (basetype); SET_TYPE_ALIGN (t->ctype, TYPE_ALIGN (basetype)); TYPE_SIZE (t->ctype) = NULL_TREE; - TYPE_PRECISION (t->ctype) = t->size (t->sym->loc) * 8; + TYPE_PRECISION (t->ctype) = dmd::size (t, t->sym->loc) * 8; layout_type (t->ctype); - /* Finish debugging output for this type. */ - rest_of_type_compilation (t->ctype, TYPE_FILE_SCOPE_P (t->ctype)); - finish_incomplete_fields (t->ctype); + /* Fix up all forward-referenced variants of this enum type. */ + for (tree v = TYPE_MAIN_VARIANT (t->ctype); v; + v = TYPE_NEXT_VARIANT (v)) + { + if (v == t->ctype) + continue; - /* Finish processing of TYPE_DECL. */ - rest_of_decl_compilation (TYPE_NAME (t->ctype), - DECL_FILE_SCOPE_P (TYPE_NAME (t->ctype)), 0); + TYPE_VALUES (v) = TYPE_VALUES (t->ctype); + TYPE_LANG_SPECIFIC (v) = TYPE_LANG_SPECIFIC (t->ctype); + TYPE_MIN_VALUE (v) = TYPE_MIN_VALUE (t->ctype); + TYPE_MAX_VALUE (v) = TYPE_MAX_VALUE (t->ctype); + TYPE_UNSIGNED (v) = TYPE_UNSIGNED (t->ctype); + TYPE_SIZE (v) = TYPE_SIZE (t->ctype); + TYPE_SIZE_UNIT (v) = TYPE_SIZE_UNIT (t->ctype); + SET_TYPE_MODE (v, TYPE_MODE (t->ctype)); + TYPE_PRECISION (v) = TYPE_PRECISION (t->ctype); + SET_TYPE_ALIGN (v, TYPE_ALIGN (t->ctype)); + TYPE_USER_ALIGN (v) = TYPE_USER_ALIGN (t->ctype); + } + + /* Complete forward-referenced fields of this enum type. */ + finish_incomplete_fields (t->ctype); } } @@ -1278,7 +1287,8 @@ public: build_type_decl (basetype, t->sym); set_visibility_for_decl (basetype, t->sym); apply_user_attributes (t->sym, basetype); - finish_aggregate_type (t->sym->structsize, t->sym->alignsize, basetype); + /* The underlying record type of classes are packed. */ + finish_aggregate_type (t->sym->structsize, 1, basetype); /* Classes only live in memory, so always set the TREE_ADDRESSABLE bit. */ for (tree tv = basetype; tv != NULL_TREE; tv = TYPE_NEXT_VARIANT (tv)) |