d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)

The D front-end is now itself written in D, in order to build GDC, you
will need a working GDC compiler (GCC version 9.1 or later).

GCC changes:

    - Add support for bootstrapping the D front-end.

These add the required components in order to have a D front-end written
in D itself.  Because the compiler front-end only depends on the core
runtime modules, only libdruntime is built for the bootstrap stages.

D front-end changes:

    - Import dmd v2.098.0-beta.1.

Druntime changes:

    - Import druntime v2.098.0-beta.1.

Phobos changes:

    - Import phobos v2.098.0-beta.1.

The jump from v2.076.1 to v2.098.0 covers nearly 4 years worth of
development on the D programming language and run-time libraries.

ChangeLog:

	* Makefile.def: Add bootstrap to libbacktrace, libphobos, zlib, and
	libatomic.
	* Makefile.in: Regenerate.
	* Makefile.tpl (POSTSTAGE1_HOST_EXPORTS): Fix command for GDC.
	(STAGE1_CONFIGURE_FLAGS): Add --with-libphobos-druntime-only if
	target-libphobos-bootstrap.
	(STAGE2_CONFIGURE_FLAGS): Likewise.
	* configure: Regenerate.
	* configure.ac: Add support for bootstrapping D front-end.

config/ChangeLog:

	* acx.m4 (ACX_PROG_GDC): New m4 function.

gcc/ChangeLog:

	* Makefile.in (GDC): New variable.
	(GDCFLAGS): New variable.
	* configure: Regenerate.
	* configure.ac: Add call to ACX_PROG_GDC.  Substitute GDCFLAGS.

gcc/d/ChangeLog:

	* dmd/MERGE: Merge upstream dmd b8384668f.
	* Make-lang.in (d-warn): Use strict warnings.
	(DMD_WARN_CXXFLAGS): Remove.
	(DMD_COMPILE): Remove.
	(CHECKING_DFLAGS): Define.
	(WARN_DFLAGS): Define.
	(ALL_DFLAGS): Define.
	(DCOMPILE.base): Define.
	(DCOMPILE): Define.
	(DPOSTCOMPILE): Define.
	(DLINKER): Define.
	(DLLINKER): Define.
	(D_FRONTEND_OBJS): Add new dmd front-end objects.
	(D_GENERATED_SRCS): Remove.
	(D_GENERATED_OBJS): Remove.
	(D_ALL_OBJS): Remove D_GENERATED_OBJS.
	(d21$(exeext)): Build using DLLINKER and -static-libphobos.
	(d.tags): Remove dmd/*.c and dmd/root/*.c.
	(d.mostlyclean): Remove D_GENERATED_SRCS, d/idgen$(build_exeext),
	d/impcnvgen$(build_exeext).
	(D_INCLUDES): Include $(srcdir)/d/dmd/res.
	(CFLAGS-d/id.o): Remove.
	(CFLAGS-d/impcnvtab.o): Remove.
	(d/%.o): Build using DCOMPILE and DPOSTCOMPILE.  Update dependencies
	from d/dmd/%.c to d/dmd/%.d.
	(d/idgen$(build_exeext)): Remove.
	(d/impcnvgen$(build_exeext)): Remove.
	(d/id.c): Remove.
	(d/id.h): Remove.
	(d/impcnvtab.c): Remove.
	(d/%.dmdgen.o): Remove.
	(D_SYSTEM_H): Remove.
	(d/idgen.dmdgen.o): Remove.
	(d/impcnvgen.dmdgen.o): Remove.
	* config-lang.in (boot_language): New variable.
	* d-attribs.cc: Include dmd/expression.h.
	* d-builtins.cc: Include d-frontend.h.
	(build_frontend_type): Update for new front-end interface.
	(d_eval_constant_expression): Likewise.
	(d_build_builtins_module): Likewise.
	(maybe_set_builtin_1): Likewise.
	(d_build_d_type_nodes): Likewise.
	* d-codegen.cc (d_decl_context): Likewise.
	(declaration_reference_p): Likewise.
	(declaration_type): Likewise.
	(parameter_reference_p): Likewise.
	(parameter_type): Likewise.
	(get_array_length): Likewise.
	(build_delegate_cst): Likewise.
	(build_typeof_null_value): Likewise.
	(identity_compare_p): Likewise.
	(lower_struct_comparison): Likewise.
	(build_filename_from_loc): Likewise.
	(build_assert_call): Remove LIBCALL_SWITCH_ERROR.
	(build_bounds_index_condition): Call LIBCALL_ARRAYBOUNDS_INDEXP on
	bounds error.
	(build_bounds_slice_condition): Call LIBCALL_ARRAYBOUNDS_SLICEP on
	bounds error.
	(array_bounds_check): Update for new front-end interface.
	(checkaction_trap_p): Handle CHECKACTION_context.
	(get_function_type): Update for new front-end interface.
	(d_build_call): Likewise.
	* d-compiler.cc: Remove include of dmd/scope.h.
	(Compiler::genCmain): Remove.
	(Compiler::paintAsType): Update for new front-end interface.
	(Compiler::onParseModule): Likewise.
	* d-convert.cc (convert_expr): Remove call to LIBCALL_ARRAYCAST.
	(convert_for_rvalue): Update for new front-end interface.
	(convert_for_assignment): Likewise.
	(convert_for_condition): Likewise.
	(d_array_convert): Likewise.
	* d-diagnostic.cc (error): Remove.
	(errorSupplemental): Remove.
	(warning): Remove.
	(warningSupplemental): Remove.
	(deprecation): Remove.
	(deprecationSupplemental): Remove.
	(message): Remove.
	(vtip): New.
	* d-frontend.cc (global): Remove.
	(Global::_init): Remove.
	(Global::startGagging): Remove.
	(Global::endGagging): Remove.
	(Global::increaseErrorCount): Remove.
	(Loc::Loc): Remove.
	(Loc::toChars): Remove.
	(Loc::equals): Remove.
	(isBuiltin): Update for new front-end interface.
	(eval_builtin): Likewise.
	(getTypeInfoType): Likewise.
	(inlineCopy): Remove.
	* d-incpath.cc: Include d-frontend.h.
	(add_globalpaths): Call d_gc_malloc to allocate Strings.
	(add_filepaths): Likewise.
	* d-lang.cc: Include dmd/id.h, dmd/root/file.h, d-frontend.h.  Remove
	include of dmd/mars.h, id.h.
	(entrypoint_module): Remove.
	(entrypoint_root_module): Remove.
	(deps_write_string): Update for new front-end interface.
	(deps_write): Likewise.
	(d_init_options): Call rt_init.  Remove setting global params that are
	default initialized by the front-end.
	(d_handle_option): Handle OPT_fcheckaction_, OPT_fdump_c___spec_,
	OPT_fdump_c___spec_verbose, OPT_fextern_std_, OPT_fpreview,
	OPT_revert, OPT_fsave_mixins_, and OPT_ftransition.
	(d_post_options): Propagate dip1021 and dip1000 preview flags to
	dip25, and flag_diagnostics_show_caret to printErrorContext.
	(d_add_entrypoint_module): Remove.
	(d_parse_file): Update for new front-end interface.
	(d_type_promotes_to): Likewise.
	(d_types_compatible_p): Likewise.
	* d-longdouble.cc (CTFloat::zero): Remove.
	(CTFloat::one): Remove.
	(CTFloat::minusone): Remove.
	(CTFloat::half): Remove.
	* d-system.h (POSIX): Remove.
	(realpath): Remove.
	(isalpha): Remove.
	(isalnum): Remove.
	(isdigit): Remove.
	(islower): Remove.
	(isprint): Remove.
	(isspace): Remove.
	(isupper): Remove.
	(isxdigit): Remove.
	(tolower): Remove.
	(_mkdir): Remove.
	(INT32_MAX): Remove.
	(INT32_MIN): Remove.
	(INT64_MIN): Remove.
	(UINT32_MAX): Remove.
	(UINT64_MAX): Remove.
	* d-target.cc: Include calls.h.
	(target): Remove.
	(define_float_constants): Remove initialization of snan.
	(Target::_init): Update for new front-end interface.
	(Target::isVectorTypeSupported): Likewise.
	(Target::isVectorOpSupported): Remove cases for unordered operators.
	(TargetCPP::typeMangle): Update for new front-end interface.
	(TargetCPP::parameterType): Likewise.
	(Target::systemLinkage): Likewise.
	(Target::isReturnOnStack): Likewise.
	(Target::isCalleeDestroyingArgs): Define.
	(Target::preferPassByRef): Define.
	* d-tree.h (d_add_entrypoint_module): Remove.
	* decl.cc (gcc_attribute_p): Update for new front-end interface.
	(apply_pragma_crt): Define.
	(DeclVisitor::visit(PragmaDeclaration *)): Handle pragmas
	crt_constructor and crt_destructor.
	(DeclVisitor::visit(TemplateDeclaration *)): Update for new front-end
	interface.
	(DeclVisitor::visit): Likewise.
	(DeclVisitor::finish_vtable): Likewise.
	(get_symbol_decl): Error if template has more than one nesting
	context.  Update for new front-end interface.
	(make_thunk): Update for new front-end interface.
	(get_vtable_decl): Likewise.
	* expr.cc (ExprVisitor::visit): Likewise.
	(build_return_dtor): Likewise.
	* imports.cc (ImportVisitor::visit): Likewise.
	* intrinsics.cc: Include dmd/expression.h.  Remove include of
	dmd/mangle.h.
	(maybe_set_intrinsic): Update for new front-end interface.
	* intrinsics.def (INTRINSIC_ROL): Update intrinsic signature.
	(INTRINSIC_ROR): Likewise.
	(INTRINSIC_ROR_TIARG): Likewise.
	(INTRINSIC_TOPREC): Likewise.
	(INTRINSIC_TOPRECL): Likewise.
	(INTRINSIC_TAN): Update intrinsic module and signature.
	(INTRINSIC_ISNAN): Likewise.
	(INTRINSIC_ISFINITE): Likewise.
	(INTRINSIC_COPYSIGN): Define intrinsic.
	(INTRINSIC_COPYSIGNI): Define intrinsic.
	(INTRINSIC_EXP): Update intrinsic module.
	(INTRINSIC_EXPM1): Likewise.
	(INTRINSIC_EXP2): Likewise.
	(INTRINSIC_LOG): Likewise.
	(INTRINSIC_LOG2): Likewise.
	(INTRINSIC_LOG10): Likewise.
	(INTRINSIC_POW): Likewise.
	(INTRINSIC_ROUND): Likewise.
	(INTRINSIC_FLOORF): Likewise.
	(INTRINSIC_FLOOR): Likewise.
	(INTRINSIC_FLOORL): Likewise.
	(INTRINSIC_CEILF): Likewise.
	(INTRINSIC_CEIL): Likewise.
	(INTRINSIC_CEILL): Likewise.
	(INTRINSIC_TRUNC): Likewise.
	(INTRINSIC_FMIN): Likewise.
	(INTRINSIC_FMAX): Likewise.
	(INTRINSIC_FMA): Likewise.
	(INTRINSIC_VA_ARG): Update intrinsic signature.
	(INTRINSIC_VASTART): Likewise.
	* lang.opt (fcheck=): Add alternate aliases for contract switches.
	(fcheckaction=): New option.
	(check_action): New Enum and EnumValue entries.
	(fdump-c++-spec-verbose): New option.
	(fdump-c++-spec=): New option.
	(fextern-std=): New option.
	(extern_stdcpp): New Enum and EnumValue entries
	(fpreview=): New options.
	(frevert=): New options.
	(fsave-mixins): New option.
	(ftransition=): Update options.
	* modules.cc (get_internal_fn): Replace Prot with Visibility.
	(build_internal_fn): Likewise.
	(build_dso_cdtor_fn): Likewise.
	(build_module_tree): Remove check for __entrypoint module.
	* runtime.def (P5): Define.
	(ARRAYBOUNDS_SLICEP): Define.
	(ARRAYBOUNDS_INDEXP): Define.
	(NEWTHROW): Define.
	(ADCMP2): Remove.
	(ARRAYCAST): Remove.
	(SWITCH_STRING): Remove.
	(SWITCH_USTRING): Remove.
	(SWITCH_DSTRING): Remove.
	(SWITCH_ERROR): Remove.
	* toir.cc (IRVisitor::visit): Update for new front-end interface.
	(IRVisitor::check_previous_goto): Remove checks for case and default
	statements.
	(IRVisitor::visit(SwitchStatement *)): Remove handling of string
	switch conditions.
	* typeinfo.cc: Include d-frontend.h.
	(get_typeinfo_kind): Update for new front-end interface.
	(make_frontend_typeinfo): Likewise.
	(TypeInfoVisitor::visit): Likewise.
	(builtin_typeinfo_p): Likewise.
	(get_typeinfo_decl): Likewise.
	(build_typeinfo): Likewise.
	* types.cc (valist_array_p): Likewise.
	(make_array_type): Likewise.
	(merge_aggregate_types): Likewise.
	(TypeVisitor::visit(TypeBasic *)): Likewise.
	(TypeVisitor::visit(TypeFunction *)): Likewise.
	(TypeVisitor::visit(TypeStruct *)): Update comment.
	* verstr.h: Removed.
	* d-frontend.h: New file.

gcc/po/ChangeLog:

	* EXCLUDES: Remove d/dmd sources from list.

gcc/testsuite/ChangeLog:

	* gdc.dg/Wcastresult2.d: Update test.
	* gdc.dg/asm1.d: Likewise.
	* gdc.dg/asm2.d: Likewise.
	* gdc.dg/asm3.d: Likewise.
	* gdc.dg/gdc282.d: Likewise.
	* gdc.dg/imports/gdc170.d: Likewise.
	* gdc.dg/intrinsics.d: Likewise.
	* gdc.dg/pr101672.d: Likewise.
	* gdc.dg/pr90650a.d: Likewise.
	* gdc.dg/pr90650b.d: Likewise.
	* gdc.dg/pr94777a.d: Likewise.
	* gdc.dg/pr95250.d: Likewise.
	* gdc.dg/pr96869.d: Likewise.
	* gdc.dg/pr98277.d: Likewise.
	* gdc.dg/pr98457.d: Likewise.
	* gdc.dg/simd1.d: Likewise.
	* gdc.dg/simd2a.d: Likewise.
	* gdc.dg/simd2b.d: Likewise.
	* gdc.dg/simd2c.d: Likewise.
	* gdc.dg/simd2d.d: Likewise.
	* gdc.dg/simd2e.d: Likewise.
	* gdc.dg/simd2f.d: Likewise.
	* gdc.dg/simd2g.d: Likewise.
	* gdc.dg/simd2h.d: Likewise.
	* gdc.dg/simd2i.d: Likewise.
	* gdc.dg/simd2j.d: Likewise.
	* gdc.dg/simd7951.d: Likewise.
	* gdc.dg/torture/gdc309.d: Likewise.
	* gdc.dg/torture/pr94424.d: Likewise.
	* gdc.dg/torture/pr94777b.d: Likewise.
	* lib/gdc-utils.exp (gdc-convert-args): Handle new compiler options.
	(gdc-convert-test): Handle CXXFLAGS, EXTRA_OBJC_SOURCES, and ARG_SETS
	test directives.
	(gdc-do-test): Only import modules in the test run directory.
	* gdc.dg/pr94777c.d: New test.
	* gdc.dg/pr96156b.d: New test.
	* gdc.dg/pr96157c.d: New test.
	* gdc.dg/simd_ctfe.d: New test.
	* gdc.dg/torture/simd17344.d: New test.
	* gdc.dg/torture/simd20052.d: New test.
	* gdc.dg/torture/simd6.d: New test.
	* gdc.dg/torture/simd7.d: New test.

libphobos/ChangeLog:

	* libdruntime/MERGE: Merge upstream druntime e6caaab9.
	* libdruntime/Makefile.am (D_EXTRA_FLAGS): Build libdruntime with
	-fpreview=dip1000, -fpreview=fieldwise, and -fpreview=dtorfields.
	(ALL_DRUNTIME_SOURCES): Add DRUNTIME_DSOURCES_STDCXX.
	(DRUNTIME_DSOURCES): Update list of C binding modules.
	(DRUNTIME_DSOURCES_STDCXX): Likewise.
	(DRUNTIME_DSOURCES_LINUX): Likewise.
	(DRUNTIME_DSOURCES_OPENBSD): Likewise.
	(DRUNTIME_DISOURCES): Remove __entrypoint.di.
	* libdruntime/Makefile.in: Regenerated.
	* libdruntime/__entrypoint.di: Removed.
	* libdruntime/gcc/deh.d (_d_isbaseof): Update signature.
	(_d_createTrace): Likewise.
	(__gdc_begin_catch): Remove reference to the exception.
	(_d_throw): Increment reference count of thrown object before unwind.
	(__gdc_personality): Chain exceptions with  Throwable.chainTogether.
	* libdruntime/gcc/emutls.d: Update imports.
	* libdruntime/gcc/sections/elf.d: Update imports.
	(DSO.moduleGroup): Update signature.
	* libdruntime/gcc/sections/macho.d: Update imports.
	(DSO.moduleGroup): Update signature.
	* libdruntime/gcc/sections/pecoff.d: Update imports.
	(DSO.moduleGroup): Update signature.
	* src/MERGE: Merge upstream phobos 5ab9ad256.
	* src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=dip1000 and
	-fpreview=dtorfields flags.
	(PHOBOS_DSOURCES): Update list of std modules.
	* src/Makefile.in: Regenerate.
	* testsuite/lib/libphobos.exp (libphobos-dg-test): Handle assembly
	compile types.
	(dg-test): Override.
	(additional_prunes): Define.
	(libphobos-dg-prune): Filter any additional_prunes set by tests.
	* testsuite/libphobos.aa/test_aa.d: Update test.
	* testsuite/libphobos.druntime/druntime.exp (version_flags): Add
	-fversion=CoreUnittest.
	* testsuite/libphobos.druntime_shared/druntime_shared.exp
	(version_flags): Add -fversion=CoreUnittest -fversion=Shared.
	* testsuite/libphobos.exceptions/unknown_gc.d: Update test.
	* testsuite/libphobos.hash/test_hash.d: Update test.
	* testsuite/libphobos.phobos/phobos.exp (version_flags): Add
	-fversion=StdUnittest
	* testsuite/libphobos.phobos_shared/phobos_shared.exp (version_flags):
	Likewise.
	* testsuite/libphobos.shared/host.c: Update test.
	* testsuite/libphobos.shared/load.d: Update test.
	* testsuite/libphobos.shared/load_13414.d: Update test.
	* testsuite/libphobos.thread/fiber_guard_page.d: Update test.
	* testsuite/libphobos.thread/tlsgc_sections.d: Update test.
	* testsuite/testsuite_flags.in: Add -fpreview=dip1000 to --gdcflags.
	* testsuite/libphobos.shared/link_mod_collision.d: Removed.
	* testsuite/libphobos.shared/load_mod_collision.d: Removed.
	* testsuite/libphobos.betterc/betterc.exp: New test.
	* testsuite/libphobos.config/config.exp: New test.
	* testsuite/libphobos.gc/gc.exp: New test.
	* testsuite/libphobos.imports/imports.exp: New test.
	* testsuite/libphobos.lifetime/lifetime.exp: New test.
	* testsuite/libphobos.unittest/unittest.exp: New test.
This commit is contained in:
Iain Buclaw 2019-06-18 20:42:10 +02:00
parent b3f60112ed
commit 5fee5ec362
2803 changed files with 336333 additions and 191383 deletions

View file

@ -169,14 +169,14 @@ target_modules = { module= newlib; };
target_modules = { module= libgcc; bootstrap=true; no_check=true;
missing=TAGS;
missing=install-dvi; };
target_modules = { module= libbacktrace; };
target_modules = { module= libbacktrace; bootstrap=true; };
target_modules = { module= libquadmath; };
target_modules = { module= libgfortran; };
target_modules = { module= libobjc;
missing=TAGS;
missing=install-dvi; };
target_modules = { module= libgo; };
target_modules = { module= libphobos;
target_modules = { module= libphobos; bootstrap=true;
lib_path=src/.libs; };
target_modules = { module= libtermcap; no_check=true;
missing=mostlyclean;
@ -186,12 +186,12 @@ target_modules = { module= libtermcap; no_check=true;
target_modules = { module= winsup; };
target_modules = { module= libgloss; no_check=true; };
target_modules = { module= libffi; no_install=true; };
target_modules = { module= zlib; };
target_modules = { module= zlib; bootstrap=true; };
target_modules = { module= rda; };
target_modules = { module= libada; };
target_modules = { module= libgomp; bootstrap= true; lib_path=.libs; };
target_modules = { module= libitm; lib_path=.libs; };
target_modules = { module= libatomic; lib_path=.libs; };
target_modules = { module= libatomic; bootstrap=true; lib_path=.libs; };
// These are (some of) the make targets to be done in each subdirectory.
// Not all; these are the ones which don't have special options.

File diff suppressed because it is too large Load diff

View file

@ -276,11 +276,14 @@ POSTSTAGE1_HOST_EXPORTS = \
$(POSTSTAGE1_CXX_EXPORT) \
$(LTO_EXPORTS) \
GDC="$$r/$(HOST_SUBDIR)/prev-gcc/gdc$(exeext) -B$$r/$(HOST_SUBDIR)/prev-gcc/ \
-B$(build_tooldir)/bin/ $(GDC_FLAGS_FOR_TARGET) \
-B$(build_tooldir)/bin/ $(GDCFLAGS_FOR_TARGET) \
-B$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime/gcc \
-B$$r/prev-$(TARGET_SUBDIR)/libphobos/src \
-B$$r/prev-$(TARGET_SUBDIR)/libphobos/src/.libs \
-I$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime -I$$s/libphobos/libdruntime \
-L$$r/prev-$(TARGET_SUBDIR)/libphobos/src/.libs \
-L$$r/prev-$(TARGET_SUBDIR)/libphobos/libdruntime/.libs"; \
-B$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs \
-L$$r/prev-$(TARGET_SUBDIR)/libstdc++-v3/src/.libs"; \
export GDC; \
GDC_FOR_BUILD="$$GDC"; export GDC_FOR_BUILD; \
GNATBIND="$$r/$(HOST_SUBDIR)/prev-gcc/gnatbind"; export GNATBIND; \
@ -487,6 +490,11 @@ STAGE1_CONFIGURE_FLAGS = --disable-intermodule $(STAGE1_CHECKING) \
--disable-coverage --enable-languages="$(STAGE1_LANGUAGES)" \
--disable-build-format-warnings
@if target-libphobos-bootstrap
STAGE1_CONFIGURE_FLAGS += --with-libphobos-druntime-only
STAGE2_CONFIGURE_FLAGS += --with-libphobos-druntime-only
@endif target-libphobos-bootstrap
# When using the slow stage1 compiler disable IL verification and forcefully
# enable it when using the stage2 compiler instead. As we later compare
# stage2 and stage3 we are merely avoid doing redundant work, plus we apply

View file

@ -420,6 +420,18 @@ else
fi
])
# Test for D.
AC_DEFUN([ACX_PROG_GDC],
[AC_REQUIRE([AC_CHECK_TOOL_PREFIX])
AC_REQUIRE([AC_PROG_CC])
AC_CHECK_TOOL(GDC, gdc, no)
if test "x$GDC" != xno; then
have_gdc=yes
else
have_gdc=no
fi
])
dnl 'make compare' can be significantly faster, if cmp itself can
dnl skip bytes instead of using tail. The test being performed is
dnl "if cmp --ignore-initial=2 t1 t2 && ! cmp --ignore-initial=1 t1 t2"

133
configure vendored
View file

@ -619,6 +619,7 @@ GFORTRAN_FOR_TARGET
GCC_FOR_TARGET
CXX_FOR_TARGET
CC_FOR_TARGET
GDCFLAGS
READELF
OTOOL
OBJDUMP
@ -702,6 +703,7 @@ gmplibs
HAVE_CXX11_FOR_BUILD
HAVE_CXX11
do_compare
GDC
GNATMAKE
GNATBIND
ac_ct_CXX
@ -5625,6 +5627,106 @@ else
have_gnat=no
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gdc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gdc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_GDC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$GDC"; then
ac_cv_prog_GDC="$GDC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_GDC="${ac_tool_prefix}gdc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
GDC=$ac_cv_prog_GDC
if test -n "$GDC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $GDC" >&5
$as_echo "$GDC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_GDC"; then
ac_ct_GDC=$GDC
# Extract the first word of "gdc", so it can be a program name with args.
set dummy gdc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_GDC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_GDC"; then
ac_cv_prog_ac_ct_GDC="$ac_ct_GDC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_GDC="gdc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_GDC=$ac_cv_prog_ac_ct_GDC
if test -n "$ac_ct_GDC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_GDC" >&5
$as_echo "$ac_ct_GDC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_GDC" = x; then
GDC="no"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
GDC=$ac_ct_GDC
fi
else
GDC="$ac_cv_prog_GDC"
fi
if test "x$GDC" != xno; then
have_gdc=yes
else
have_gdc=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to compare bootstrapped objects" >&5
$as_echo_n "checking how to compare bootstrapped objects... " >&6; }
if ${gcc_cv_prog_cmp_skip+:} false; then :
@ -8679,6 +8781,23 @@ $as_echo "$as_me: WARNING: GNAT is required to build $language" >&2;}
;;
esac
# Disable D if no preexisting GDC is available.
case ${add_this_lang}:${language}:${have_gdc} in
yes:d:no)
# Specifically requested language; tell them.
as_fn_error $? "GDC is required to build $language" "$LINENO" 5
;;
all:d:no)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: GDC is required to build $language" >&5
$as_echo "$as_me: WARNING: GDC is required to build $language" >&2;}
add_this_lang=unsupported
;;
*:d:no)
# Silently disable.
add_this_lang=unsupported
;;
esac
# Disable jit if -enable-host-shared not specified
# but not if building for Mingw. All code in Windows
# is position independent code (PIC).
@ -8748,7 +8867,7 @@ $as_echo "$as_me: WARNING: ${language} not supported for this target" >&2;}
*) stage1_languages="${stage1_languages}${language}," ;;
esac
# We need to bootstrap any supporting libraries.
bootstrap_target_libs="${bootstrap_target_libs}${target_libs},"
bootstrap_target_libs=`echo "${bootstrap_target_libs}${target_libs}," | sed "s/ /,/g"`
;;
esac
;;
@ -9517,6 +9636,16 @@ if echo " ${target_configdirs} " | grep " libvtv " > /dev/null 2>&1 &&
bootstrap_target_libs=${bootstrap_target_libs}target-libvtv,
fi
# If we are building libatomic and the list of enabled languages includes the
# D frontend, bootstrap it.
if echo " ${target_configdirs} " | grep " libatomic " > /dev/null 2>&1; then
case ,${enable_languages}, in
*,d,*)
bootstrap_target_libs=${bootstrap_target_libs}target-libatomic,
;;
esac
fi
# Determine whether gdb needs tk/tcl or not.
# Use 'maybe' since enable_gdbtk might be true even if tk isn't available
# and in that case we want gdb to be built without tk. Ugh!
@ -12614,6 +12743,8 @@ fi
# Target tools.
# Check whether --with-build-time-tools was given.

View file

@ -1406,6 +1406,7 @@ int main() {}])],
fi
ACX_PROG_GNAT
ACX_PROG_GDC
ACX_PROG_CMP_IGNORE_INITIAL
AC_ARG_ENABLE([bootstrap],
@ -2087,6 +2088,22 @@ if test -d ${srcdir}/gcc; then
;;
esac
# Disable D if no preexisting GDC is available.
case ${add_this_lang}:${language}:${have_gdc} in
yes:d:no)
# Specifically requested language; tell them.
AC_MSG_ERROR([GDC is required to build $language])
;;
all:d:no)
AC_MSG_WARN([GDC is required to build $language])
add_this_lang=unsupported
;;
*:d:no)
# Silently disable.
add_this_lang=unsupported
;;
esac
# Disable jit if -enable-host-shared not specified
# but not if building for Mingw. All code in Windows
# is position independent code (PIC).
@ -2154,7 +2171,7 @@ directories, to avoid imposing the performance cost of
*) stage1_languages="${stage1_languages}${language}," ;;
esac
# We need to bootstrap any supporting libraries.
bootstrap_target_libs="${bootstrap_target_libs}${target_libs},"
bootstrap_target_libs=`echo "${bootstrap_target_libs}${target_libs}," | sed "s/ /,/g"`
;;
esac
;;
@ -2837,6 +2854,16 @@ if echo " ${target_configdirs} " | grep " libvtv " > /dev/null 2>&1 &&
bootstrap_target_libs=${bootstrap_target_libs}target-libvtv,
fi
# If we are building libatomic and the list of enabled languages includes the
# D frontend, bootstrap it.
if echo " ${target_configdirs} " | grep " libatomic " > /dev/null 2>&1; then
case ,${enable_languages}, in
*,d,*)
bootstrap_target_libs=${bootstrap_target_libs}target-libatomic,
;;
esac
fi
# Determine whether gdb needs tk/tcl or not.
# Use 'maybe' since enable_gdbtk might be true even if tk isn't available
# and in that case we want gdb to be built without tk. Ugh!
@ -3505,6 +3532,8 @@ AC_SUBST(CC)
AC_SUBST(CXX)
AC_SUBST(CFLAGS)
AC_SUBST(CXXFLAGS)
AC_SUBST(GDC)
AC_SUBST(GDCFLAGS)
# Target tools.
AC_ARG_WITH([build-time-tools],

View file

@ -1092,6 +1092,10 @@ SYSLIBS = @GNAT_LIBEXC@
GNATBIND = @GNATBIND@
GNATMAKE = @GNATMAKE@
# Used from d/Make-lang.in
GDC = @GDC@
GDCFLAGS = @GDCFLAGS@
# Libs needed (at present) just for jcf-dump.
LDEXP_LIB = @LDEXP_LIB@

107
gcc/configure vendored
View file

@ -858,6 +858,8 @@ EGREP
GREP
CXXCPP
PICFLAG_FOR_TARGET
GDCFLAGS
GDC
GNATMAKE
GNATBIND
ac_ct_CXX
@ -5257,6 +5259,106 @@ else
fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gdc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gdc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_GDC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$GDC"; then
ac_cv_prog_GDC="$GDC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_GDC="${ac_tool_prefix}gdc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
GDC=$ac_cv_prog_GDC
if test -n "$GDC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $GDC" >&5
$as_echo "$GDC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_prog_GDC"; then
ac_ct_GDC=$GDC
# Extract the first word of "gdc", so it can be a program name with args.
set dummy gdc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if ${ac_cv_prog_ac_ct_GDC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_GDC"; then
ac_cv_prog_ac_ct_GDC="$ac_ct_GDC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_GDC="gdc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
fi
fi
ac_ct_GDC=$ac_cv_prog_ac_ct_GDC
if test -n "$ac_ct_GDC"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_GDC" >&5
$as_echo "$ac_ct_GDC" >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_ct_GDC" = x; then
GDC="no"
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
GDC=$ac_ct_GDC
fi
else
GDC="$ac_cv_prog_GDC"
fi
if test "x$GDC" != xno; then
have_gdc=yes
else
have_gdc=no
fi
# Do configure tests with the C++ compiler, since that's what we build with.
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
@ -5275,6 +5377,7 @@ esac
# Determine PICFLAG for target gnatlib.
@ -19458,7 +19561,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 19461 "configure"
#line 19564 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -19564,7 +19667,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 19567 "configure"
#line 19670 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H

View file

@ -435,6 +435,7 @@ rm -f a.out a.exe b.out
AC_PROG_CC
AC_PROG_CXX
ACX_PROG_GNAT([-I"$srcdir"/ada/libgnat])
ACX_PROG_GDC([-I"$srcdir"/d])
# Do configure tests with the C++ compiler, since that's what we build with.
AC_LANG(C++)
@ -448,6 +449,7 @@ case "$CC" in
esac
AC_SUBST(CFLAGS)
AC_SUBST(CXXFLAGS)
AC_SUBST(GDCFLAGS)
# Determine PICFLAG for target gnatlib.
GCC_PICFLAG_FOR_TARGET

View file

@ -46,30 +46,61 @@ gdc-cross$(exeext): gdc$(exeext)
-rm -f gdc-cross$(exeext)
cp gdc$(exeext) gdc-cross$(exeext)
# Filter out pedantic and virtual overload warnings.
d-warn = $(filter-out -pedantic -Woverloaded-virtual, $(STRICT_WARN))
# Use strict warnings.
d-warn = $(STRICT_WARN)
# Also filter out warnings for missing format attributes in the D Frontend.
DMD_WARN_CXXFLAGS = $(filter-out -Wmissing-format-attribute, $(WARN_CXXFLAGS))
DMD_COMPILE = $(subst $(WARN_CXXFLAGS), $(DMD_WARN_CXXFLAGS), $(COMPILE))
# D compiler and flags for building the front-end.
ifeq ($(TREECHECKING),)
CHECKING_DFLAGS = -frelease
else
CHECKING_DFLAGS =
endif
WARN_DFLAGS = -Wall -Wdeprecated $(NOCOMMON_FLAG)
ALL_DFLAGS = $(DFLAGS-$@) $(GDCFLAGS) -fversion=IN_GCC $(CHECKING_DFLAGS) \
$(PICFLAG) $(ALIASING_FLAGS) $(COVERAGE_FLAGS) $(WARN_DFLAGS)
DCOMPILE.base = $(GDC) $(NO_PIE_CFLAGS) -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
DLINKER = $(GDC) $(NO_PIE_FLAG) -lstdc++
# Like LINKER, but use a mutex for serializing front end links.
ifeq (@DO_LINK_MUTEX@,true)
DLLINKER = $(SHELL) $(srcdir)/lock-and-run.sh linkfe.lck $(DLINKER)
else
DLLINKER = $(DLINKER)
endif
# D Frontend object files.
D_FRONTEND_OBJS = \
d/aav.o \
d/access.o \
d/aggregate.o \
d/aliasthis.o \
d/apply.o \
d/array.o \
d/arrayop.o \
d/arraytypes.o \
d/attrib.o \
d/ast_node.o \
d/astcodegen.o \
d/astenums.o \
d/bitarray.o \
d/blockexit.o \
d/builtin.o \
d/canthrow.o \
d/checkedint.o \
d/chkformat.o \
d/clone.o \
d/compiler.o \
d/complex.o \
d/cond.o \
d/constfold.o \
d/cparse.o \
d/cppmangle.o \
d/ctfeexpr.o \
d/ctfloat.o \
d/ctorflow.o \
d/dcast.o \
d/dclass.o \
d/declaration.o \
@ -86,32 +117,49 @@ D_FRONTEND_OBJS = \
d/dsymbol.o \
d/dsymbolsem.o \
d/dtemplate.o \
d/dtoh.o \
d/dversion.o \
d/entity.o \
d/errors.o \
d/escape.o \
d/expression.o \
d/expressionsem.o \
d/file.o \
d/filename.o \
d/foreachvar.o \
d/func.o \
d/globals.o \
d/gluelayer.o \
d/hash.o \
d/hdrgen.o \
d/iasm.o \
d/iasmgcc.o \
d/id.o \
d/identifier.o \
d/impcnvtab.o \
d/imphint.o \
d/init.o \
d/initsem.o \
d/inline.o \
d/intrange.o \
d/json.o \
d/lambdacomp.o \
d/lexer.o \
d/longdouble.o \
d/mtype.o \
d/nogc.o \
d/nspace.o \
d/ob.o \
d/objc.o \
d/opover.o \
d/optimize.o \
d/outbuffer.o \
d/parse.o \
d/parsetimevisitor.o \
d/permissivevisitor.o \
d/port.o \
d/printast.o \
d/region.o \
d/rmem.o \
d/rootobject.o \
d/safe.o \
@ -121,20 +169,23 @@ D_FRONTEND_OBJS = \
d/sideeffect.o \
d/speller.o \
d/statement.o \
d/statement_rewrite_walker.o \
d/statementsem.o \
d/staticassert.o \
d/staticcond.o \
d/stmtstate.o \
d/string.o \
d/stringtable.o \
d/target.o \
d/templateparamsem.o \
d/tokens.o \
d/traits.o \
d/transitivevisitor.o \
d/typesem.o \
d/typinf.o \
d/utf.o \
d/utils.o
# D Frontend generated files.
D_GENERATED_SRCS = d/id.c d/id.h d/impcnvtab.c
D_GENERATED_OBJS = d/id.o d/impcnvtab.o
d/utils.o \
d/visitor.o
# Language-specific object files for D.
D_OBJS = \
@ -163,13 +214,13 @@ D_OBJS = \
d/types.o
# All language-specific object files for D.
D_ALL_OBJS = $(D_FRONTEND_OBJS) $(D_GENERATED_OBJS) $(D_OBJS) $(D_TARGET_OBJS)
D_ALL_OBJS = $(D_FRONTEND_OBJS) $(D_OBJS) $(D_TARGET_OBJS)
d_OBJS = $(D_ALL_OBJS) d/d-spec.o
d21$(exeext): $(D_ALL_OBJS) attribs.o $(BACKEND) $(LIBDEPS) $(d.prev)
@$(call LINK_PROGRESS,$(INDEX.d),start)
+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+$(DLLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -static-libphobos -o $@ \
$(D_ALL_OBJS) attribs.o $(BACKEND) $(LIBS) $(BACKENDLIBS)
@$(call LINK_PROGRESS,$(INDEX.d),end)
@ -221,7 +272,7 @@ d.srcextra:
d.tags: force
cd $(srcdir)/d; \
$(ETAGS) -o TAGS.sub *.c *.cc *.h dmd/*.c dmd/*.h dmd/root/*.h dmd/root/*.c; \
$(ETAGS) -o TAGS.sub *.c *.cc *.h dmd/*.h dmd/root/*.h; \
$(ETAGS) --include TAGS.sub --include ../TAGS.sub
d.man: doc/gdc.1
@ -313,8 +364,6 @@ d.uninstall:
d.mostlyclean:
-rm -f d/*$(objext)
-rm -f d/*$(coverageexts)
-rm -f $(D_GENERATED_SRCS)
-rm -f d/idgen$(build_exeext) d/impcnvgen$(build_exeext)
-rm -f gdc$(exeext) gdc-cross$(exeext) d21$(exeext)
d.clean:
d.distclean:
@ -337,48 +386,13 @@ d.stagefeedback: stagefeedback-start
-mv d/*$(objext) stagefeedback/d
# Include the dfrontend and build directories for headers.
D_INCLUDES = -I$(srcdir)/d -I$(srcdir)/d/dmd -Id
CFLAGS-d/id.o += $(D_INCLUDES)
CFLAGS-d/impcnvtab.o += $(D_INCLUDES)
D_INCLUDES = -I$(srcdir)/d -J$(srcdir)/d/dmd -J$(srcdir)/d/dmd/res
# Override build rules for D frontend.
d/%.o: d/dmd/%.c $(D_GENERATED_SRCS)
$(DMD_COMPILE) $(D_INCLUDES) $<
$(POSTCOMPILE)
d/%.o: d/dmd/%.d
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)
d/%.o: d/dmd/root/%.c $(D_GENERATED_SRCS)
$(DMD_COMPILE) $(D_INCLUDES) $<
$(POSTCOMPILE)
# Generated programs.
d/idgen$(build_exeext): d/idgen.dmdgen.o $(BUILD_LIBDEPS)
+$(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ \
$(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS)
d/impcnvgen$(build_exeext): d/impcnvgen.dmdgen.o $(BUILD_LIBDEPS)
+$(LINKER_FOR_BUILD) $(BUILD_LINKERFLAGS) $(BUILD_LDFLAGS) -o $@ \
$(filter-out $(BUILD_LIBDEPS), $^) $(BUILD_LIBS)
# Generated sources.
d/id.c: d/idgen$(build_exeext)
cd d && ./idgen$(build_exeext)
# idgen also generates id.h; just verify it exists.
d/id.h: d/id.c
d/impcnvtab.c: d/impcnvgen$(build_exeext)
cd d && ./impcnvgen$(build_exeext)
# Compile the generator programs.
d/%.dmdgen.o: $(srcdir)/d/dmd/%.c
$(COMPILER_FOR_BUILD) -c $(BUILD_COMPILERFLAGS) $(D_INCLUDES) \
$(BUILD_CPPFLAGS) -o $@ $<
# Header dependencies for the generator programs.
D_SYSTEM_H = d/dmd/root/dsystem.h d/d-system.h
d/idgen.dmdgen.o: d/dmd/idgen.c $(D_SYSTEM_H) $(BCONFIG_H) $(SYSTEM_H)
d/impcnvgen.dmdgen.o: d/dmd/impcnvgen.c d/dmd/mtype.h $(D_SYSTEM_H) \
$(BCONFIG_H) $(SYSTEM_H)
d/%.o: d/dmd/root/%.d
$(DCOMPILE) $(D_INCLUDES) $<
$(DPOSTCOMPILE)

View file

@ -19,10 +19,12 @@
# We define several parameters used by configure:
#
# language - name of language as it would appear in $(LANGUAGES)
# boot_language - "yes" if we need to build this language in stage1
# compilers - value to add to $(COMPILERS)
language="d"
boot_language=yes
compilers="d21\$(exeext)"
phobos_target_deps="target-zlib target-libbacktrace"

View file

@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see
#include "dmd/attrib.h"
#include "dmd/declaration.h"
#include "dmd/expression.h"
#include "dmd/module.h"
#include "dmd/mtype.h"
#include "dmd/template.h"

View file

@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
#include "stor-layout.h"
#include "d-tree.h"
#include "d-frontend.h"
#include "d-target.h"
@ -98,7 +99,7 @@ build_frontend_type (tree type)
if (TYPE_MAIN_VARIANT (TREE_TYPE (type)) == char_type_node)
return Type::tchar->addMod (dtype->mod)->pointerTo ()->addMod (mod);
if (dtype->ty == Tfunction)
if (dtype->ty == TY::Tfunction)
return (TypePointer::create (dtype))->addMod (mod);
return dtype->pointerTo ()->addMod (mod);
@ -130,7 +131,7 @@ build_frontend_type (tree type)
/* For now, skip support for cent/ucent until the frontend
has better support for handling it. */
for (size_t i = Tint8; i <= Tuns64; i++)
for (size_t i = (size_t) TY::Tint8; i <= (size_t) TY::Tuns64; i++)
{
dtype = Type::basic[i];
@ -148,7 +149,7 @@ build_frontend_type (tree type)
{
unsigned size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type));
for (size_t i = Tfloat32; i <= Tfloat80; i++)
for (size_t i = (size_t) TY::Tfloat32; i <= (size_t) TY::Tfloat80; i++)
{
dtype = Type::basic[i];
@ -164,7 +165,8 @@ build_frontend_type (tree type)
case COMPLEX_TYPE:
{
unsigned size = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (type));
for (size_t i = Tcomplex32; i <= Tcomplex80; i++)
for (size_t i = (size_t) TY::Tcomplex32; i <= (size_t) TY::Tcomplex80;
i++)
{
dtype = Type::basic[i];
@ -235,7 +237,7 @@ build_frontend_type (tree type)
sdecl->structsize = int_size_in_bytes (type);
sdecl->alignsize = TYPE_ALIGN_UNIT (type);
sdecl->alignment = STRUCTALIGN_DEFAULT;
sdecl->sizeok = SIZEOKdone;
sdecl->sizeok = Sizeok::done;
sdecl->type = (TypeStruct::create (sdecl))->addMod (mod);
sdecl->type->ctype = type;
sdecl->type->merge2 ();
@ -243,7 +245,7 @@ build_frontend_type (tree type)
/* Add both named and anonymous fields as members of the struct.
Anonymous fields still need a name in D, so call them "__pad%u". */
unsigned anonfield_id = 0;
sdecl->members = new Dsymbols;
sdecl->members = d_gc_malloc<Dsymbols> ();
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
{
@ -253,7 +255,6 @@ build_frontend_type (tree type)
/* Drop any field types that got cached before the conversion
of this record type failed. */
builtin_converted_decls.truncate (saved_builtin_decls_length);
delete sdecl->members;
return NULL;
}
@ -292,7 +293,7 @@ build_frontend_type (tree type)
tree parms = TYPE_ARG_TYPES (type);
VarArg varargs_p = VARARGvariadic;
Parameters *args = new Parameters;
Parameters *args = d_gc_malloc<Parameters> ();
args->reserve (list_length (parms));
/* Attempt to convert all parameter types. */
@ -318,7 +319,6 @@ build_frontend_type (tree type)
/* Drop any parameter types that got cached before the
conversion of this function type failed. */
builtin_converted_decls.truncate (saved_builtin_decls_length);
delete args;
return NULL;
}
@ -329,7 +329,7 @@ build_frontend_type (tree type)
have no named parameters, and so can't be represented in D. */
if (args->length != 0 || varargs_p == VARARGnone)
{
dtype = TypeFunction::create (args, dtype, varargs_p, LINKc);
dtype = TypeFunction::create (args, dtype, varargs_p, LINK::c);
return dtype->addMod (mod);
}
}
@ -386,7 +386,7 @@ d_eval_constant_expression (const Loc &loc, tree cst)
else if (code == VECTOR_CST)
{
dinteger_t nunits = VECTOR_CST_NELTS (cst).to_constant ();
Expressions *elements = new Expressions;
Expressions *elements = d_gc_malloc<Expressions> ();
elements->setDim (nunits);
for (size_t i = 0; i < nunits; i++)
@ -520,7 +520,7 @@ build_alias_declaration (const char *alias, Type *type)
void
d_build_builtins_module (Module *m)
{
Dsymbols *members = new Dsymbols;
Dsymbols *members = d_gc_malloc<Dsymbols> ();
tree decl;
for (size_t i = 0; vec_safe_iterate (gcc_builtins_functions, i, &decl); ++i)
@ -543,16 +543,16 @@ d_build_builtins_module (Module *m)
flag_unsafe_math_optimizations.
- Built-ins never use the GC or raise a D exception, and so are always
marked as `nothrow' and `@nogc'. */
tf->purity = DECL_PURE_P (decl) ? PUREstrong
: TREE_READONLY (decl) ? PUREconst
: DECL_IS_NOVOPS (decl) ? PUREweak
: !DECL_ASSEMBLER_NAME_SET_P (decl) ? PUREweak
: PUREimpure;
tf->trust = !DECL_ASSEMBLER_NAME_SET_P (decl) ? TRUSTsafe
: TREE_NOTHROW (decl) ? TRUSTtrusted
: TRUSTsystem;
tf->isnothrow = true;
tf->isnogc = true;
tf->purity = DECL_PURE_P (decl) ? PURE::strong
: TREE_READONLY (decl) ? PURE::const_
: DECL_IS_NOVOPS (decl) ? PURE::weak
: !DECL_ASSEMBLER_NAME_SET_P (decl) ? PURE::weak
: PURE::impure;
tf->trust = !DECL_ASSEMBLER_NAME_SET_P (decl) ? TRUST::safe
: TREE_NOTHROW (decl) ? TRUST::trusted
: TRUST::system;
tf->isnothrow (true);
tf->isnogc (true);
FuncDeclaration *func
= FuncDeclaration::create (Loc (), Loc (),
@ -560,7 +560,7 @@ d_build_builtins_module (Module *m)
STCextern, tf);
DECL_LANG_SPECIFIC (decl) = build_lang_decl (func);
func->csym = decl;
func->builtin = BUILTINgcc;
func->builtin = BUILTIN::gcc;
members->push (func);
}
@ -660,7 +660,7 @@ d_build_builtins_module (Module *m)
members->push (build_alias_declaration ("__builtin_unwind_uint", t));
}
m->members->push (LinkDeclaration::create (LINKc, members));
m->members->push (LinkDeclaration::create (Loc (), LINK::c, members));
}
/* Search for any `extern(C)' functions that match any known GCC library builtin
@ -700,7 +700,7 @@ maybe_set_builtin_1 (Dsymbol *d)
/* Found a match, tell the frontend this is a builtin. */
DECL_LANG_SPECIFIC (t) = build_lang_decl (fd);
fd->csym = t;
fd->builtin = BUILTINgcc;
fd->builtin = BUILTIN::gcc;
return;
}
}
@ -858,7 +858,7 @@ d_build_d_type_nodes (void)
/* Calling build_ctype() links the front-end Type to the GCC node,
and sets the TYPE_NAME to the D language type. */
for (unsigned ty = 0; ty < TMAX; ty++)
for (unsigned ty = 0; ty < (unsigned) TY::TMAX; ty++)
{
if (Type::basic[ty] != NULL)
build_ctype (Type::basic[ty]);

View file

@ -76,7 +76,7 @@ d_decl_context (Dsymbol *dsym)
but only for extern(D) symbols. */
if (parent->isModule ())
{
if ((decl != NULL && decl->linkage != LINKd)
if ((decl != NULL && decl->linkage != LINK::d)
|| (ad != NULL && ad->classKind != ClassKind::d))
return NULL_TREE;
@ -131,7 +131,7 @@ declaration_reference_p (Declaration *decl)
Type *tb = decl->type->toBasetype ();
/* Declaration is a reference type. */
if (tb->ty == Treference || decl->storage_class & (STCout | STCref))
if (tb->ty == TY::Treference || decl->storage_class & (STCout | STCref))
return true;
return false;
@ -146,7 +146,7 @@ declaration_type (Declaration *decl)
if (decl->storage_class & STClazy)
{
TypeFunction *tf = TypeFunction::create (NULL, decl->type,
VARARGnone, LINKd);
VARARGnone, LINK::d);
TypeDelegate *t = TypeDelegate::create (tf);
return build_ctype (t->merge2 ());
}
@ -181,7 +181,7 @@ parameter_reference_p (Parameter *arg)
Type *tb = arg->type->toBasetype ();
/* Parameter is a reference type. */
if (tb->ty == Treference || arg->storageClass & (STCout | STCref))
if (tb->ty == TY::Treference || arg->storageClass & (STCout | STCref))
return true;
return false;
@ -196,7 +196,7 @@ parameter_type (Parameter *arg)
if (arg->storageClass & STClazy)
{
TypeFunction *tf = TypeFunction::create (NULL, arg->type,
VARARGnone, LINKd);
VARARGnone, LINK::d);
TypeDelegate *t = TypeDelegate::create (tf);
return build_ctype (t->merge2 ());
}
@ -319,10 +319,10 @@ get_array_length (tree exp, Type *type)
switch (tb->ty)
{
case Tsarray:
case TY::Tsarray:
return size_int (tb->isTypeSArray ()->dim->toUInteger ());
case Tarray:
case TY::Tarray:
return d_array_length (exp);
default:
@ -411,7 +411,7 @@ build_delegate_cst (tree method, tree object, Type *type)
tree ctype;
Type *tb = type->toBasetype ();
if (tb->ty == Tdelegate)
if (tb->ty == TY::Tdelegate)
ctype = build_ctype (type);
else
{
@ -464,11 +464,11 @@ build_typeof_null_value (Type *type)
tree value;
/* For dynamic arrays, set length and pointer fields to zero. */
if (tb->ty == Tarray)
if (tb->ty == TY::Tarray)
value = d_array_value (build_ctype (type), size_int (0), null_pointer_node);
/* For associative arrays, set the pointer field to null. */
else if (tb->ty == Taarray)
else if (tb->ty == TY::Taarray)
{
tree ctype = build_ctype (type);
gcc_assert (TYPE_ASSOCIATIVE_ARRAY (ctype));
@ -478,7 +478,7 @@ build_typeof_null_value (Type *type)
}
/* For delegates, set the frame and function pointer fields to null. */
else if (tb->ty == Tdelegate)
else if (tb->ty == TY::Tdelegate)
value = build_delegate_cst (null_pointer_node, null_pointer_node, type);
/* Simple zero constant for all other types. */
@ -882,7 +882,9 @@ identity_compare_p (StructDeclaration *sd)
}
/* Check for types that may have padding. */
if ((tb->ty == Tcomplex80 || tb->ty == Tfloat80 || tb->ty == Timaginary80)
if ((tb->ty == TY::Tcomplex80
|| tb->ty == TY::Tfloat80
|| tb->ty == TY::Timaginary80)
&& target.realpad != 0)
return false;
@ -960,12 +962,12 @@ 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 != 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 != Tvector && type->isfloating ())
else if (type->ty != TY::Tvector && type->isfloating ())
{
/* Floating-point comparison, don't compare padding in type. */
if (!type->iscomplex ())
@ -1839,7 +1841,7 @@ static tree
build_filename_from_loc (const Loc &loc)
{
const char *filename = loc.filename
? loc.filename : d_function_chain->module->srcfile->toChars ();
? loc.filename : d_function_chain->module->srcfile.toChars ();
unsigned length = strlen (filename);
tree str = build_string (length, filename);
@ -1862,7 +1864,6 @@ build_assert_call (const Loc &loc, libcall_fn libcall, tree msg)
{
case LIBCALL_ASSERT_MSG:
case LIBCALL_UNITTEST_MSG:
case LIBCALL_SWITCH_ERROR:
/* File location is passed as a D string. */
if (loc.filename)
{
@ -1912,7 +1913,7 @@ build_array_bounds_call (const Loc &loc)
/* Builds a bounds condition checking that INDEX is between 0 and LENGTH
in the index expression IE. The condition returns the INDEX if true, or
throws a `RangeError`. */
throws a `ArrayIndexError`. */
tree
build_bounds_index_condition (IndexExp *ie, tree index, tree length)
@ -1927,7 +1928,16 @@ build_bounds_index_condition (IndexExp *ie, tree index, tree length)
No need to check whether INDEX >= 0 as the front-end should
have already taken care of implicit casts to unsigned. */
tree condition = fold_build2 (GE_EXPR, d_bool_type, index, length);
tree boundserr = build_array_bounds_call (ie->e2->loc);
tree boundserr;
if (checkaction_trap_p ())
boundserr = build_call_expr (builtin_decl_explicit (BUILT_IN_TRAP), 0);
else
{
boundserr = build_libcall (LIBCALL_ARRAYBOUNDS_INDEXP, Type::tvoid, 4,
build_filename_from_loc (ie->e2->loc),
size_int (ie->e2->loc.linnum), index, length);
}
return build_condition (TREE_TYPE (index), condition, boundserr, index);
}
@ -1963,7 +1973,22 @@ build_bounds_slice_condition (SliceExp *se, tree lower, tree upper, tree length)
if (condition != NULL_TREE)
{
tree boundserr = build_array_bounds_call (se->loc);
tree boundserr;
if (checkaction_trap_p ())
{
boundserr =
build_call_expr (builtin_decl_explicit (BUILT_IN_TRAP), 0);
}
else
{
boundserr = build_libcall (LIBCALL_ARRAYBOUNDS_SLICEP,
Type::tvoid, 5,
build_filename_from_loc (se->loc),
size_int (se->loc.linnum),
lower, upper, length);
}
upper = build_condition (TREE_TYPE (upper), condition,
boundserr, upper);
}
@ -1993,9 +2018,9 @@ array_bounds_check (void)
case CHECKENABLEsafeonly:
/* For D2 safe functions only. */
fd = d_function_chain->function;
if (fd && fd->type->ty == Tfunction)
if (fd && fd->type->ty == TY::Tfunction)
{
if (fd->type->isTypeFunction ()->trust == TRUSTsafe)
if (fd->type->isTypeFunction ()->trust == TRUST::safe)
return true;
}
return false;
@ -2014,6 +2039,7 @@ checkaction_trap_p (void)
switch (global.params.checkAction)
{
case CHECKACTION_D:
case CHECKACTION_context:
return false;
case CHECKACTION_C:
@ -2032,11 +2058,11 @@ TypeFunction *
get_function_type (Type *t)
{
TypeFunction *tf = NULL;
if (t->ty == Tpointer)
if (t->ty == TY::Tpointer)
t = t->nextOf ()->toBasetype ();
if (t->ty == Tfunction)
if (t->ty == TY::Tfunction)
tf = t->isTypeFunction ();
else if (t->ty == Tdelegate)
else if (t->ty == TY::Tdelegate)
tf = t->isTypeDelegate ()->next->isTypeFunction ();
return tf;
}
@ -2096,7 +2122,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
gcc_assert (FUNC_OR_METHOD_TYPE_P (ctype));
gcc_assert (tf != NULL);
gcc_assert (tf->ty == Tfunction);
gcc_assert (tf->ty == TY::Tfunction);
if (TREE_CODE (ctype) != FUNCTION_TYPE && object == NULL_TREE)
{
@ -2195,7 +2221,7 @@ d_build_call (TypeFunction *tf, tree callable, tree object,
SET_EXPR_LOCATION (result, input_location);
/* Enforce left to right evaluation. */
if (tf->linkage == LINKd)
if (tf->linkage == LINK::d)
CALL_EXPR_ARGS_ORDERED (result) = 1;
result = maybe_expand_intrinsic (result);

View file

@ -20,7 +20,6 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "dmd/compiler.h"
#include "dmd/scope.h"
#include "dmd/expression.h"
#include "dmd/identifier.h"
#include "dmd/module.h"
@ -34,40 +33,6 @@ along with GCC; see the file COPYING3. If not see
/* Implements the Compiler interface used by the frontend. */
/* Generate C main() in response to seeing D main(). This used to be in
libdruntime, but contained a reference to _Dmain which didn't work when
druntime was made into a shared library and was linked to a program, such
as a C++ program, that didn't have a _Dmain. */
void
Compiler::genCmain (Scope *sc)
{
static bool initialized = false;
if (initialized)
return;
/* The D code to be generated is provided by __entrypoint.di, try to load it,
but don't fail if unfound. */
unsigned errors = global.startGagging ();
Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__entrypoint"));
if (global.endGagging (errors))
m = NULL;
if (m != NULL)
{
m->importedFrom = m;
m->importAll (NULL);
dsymbolSemantic (m, NULL);
semantic2 (m, NULL);
semantic3 (m, NULL);
d_add_entrypoint_module (m, sc->_module);
}
initialized = true;
}
/* Perform a reinterpret cast of EXPR to type TYPE for use in CTFE.
The front end should have already ensured that EXPR is a constant,
so we just lower the value to GCC and return the converted CST. */
@ -123,7 +88,7 @@ Compiler::paintAsType (UnionExp *, Expression *expr, Type *type)
/* Encode CST to buffer. */
int len = native_encode_expr (cst, buffer, sizeof (buffer));
if (tb->ty == Tsarray)
if (tb->ty == TY::Tsarray)
{
/* Interpret value as a vector of the same size,
then return the array literal. */
@ -161,22 +126,22 @@ Compiler::onParseModule (Module *m)
{
ModuleDeclaration *md = m->md;
if (!md || !md->id || !md->packages)
if (!md || !md->id|| md->packages.length == 0)
{
Identifier *id = (md && md->id) ? md->id : m->ident;
if (!strcmp (id->toChars (), "object"))
create_tinfo_types (m);
}
else if (md->packages->length == 1)
else if (md->packages.length == 1)
{
if (!strcmp ((*md->packages)[0]->toChars (), "gcc")
if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
&& !strcmp (md->id->toChars (), "builtins"))
d_build_builtins_module (m);
}
else if (md->packages->length == 2)
else if (md->packages.length == 2)
{
if (!strcmp ((*md->packages)[0]->toChars (), "core")
&& !strcmp ((*md->packages)[1]->toChars (), "stdc"))
if (!strcmp (md->packages.ptr[0]->toChars (), "core")
&& !strcmp (md->packages.ptr[1]->toChars (), "stdc"))
d_add_builtin_module (m);
}
}

View file

@ -361,14 +361,14 @@ convert_expr (tree exp, Type *etype, Type *totype)
switch (ebtype->ty)
{
case Tdelegate:
if (tbtype->ty == Tdelegate)
case TY::Tdelegate:
if (tbtype->ty == TY::Tdelegate)
{
exp = d_save_expr (exp);
return build_delegate_cst (delegate_method (exp),
delegate_object (exp), totype);
}
else if (tbtype->ty == Tpointer)
else if (tbtype->ty == TY::Tpointer)
{
/* The front-end converts <delegate>.ptr to cast (void *)<delegate>.
Maybe should only allow void* ? */
@ -382,8 +382,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
}
break;
case Tstruct:
if (tbtype->ty == Tstruct)
case TY::Tstruct:
if (tbtype->ty == TY::Tstruct)
{
if (totype->size () == etype->size ())
{
@ -400,8 +400,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
/* else, default conversion, which should produce an error. */
break;
case Tclass:
if (tbtype->ty == Tclass)
case TY::Tclass:
if (tbtype->ty == TY::Tclass)
{
ClassDeclaration *cdfrom = ebtype->isClassHandle ();
ClassDeclaration *cdto = tbtype->isClassHandle ();
@ -460,12 +460,12 @@ convert_expr (tree exp, Type *etype, Type *totype)
/* else default conversion. */
break;
case Tsarray:
if (tbtype->ty == Tpointer)
case TY::Tsarray:
if (tbtype->ty == TY::Tpointer)
{
result = build_nop (build_ctype (totype), build_address (exp));
}
else if (tbtype->ty == Tarray)
else if (tbtype->ty == TY::Tarray)
{
dinteger_t dim = ebtype->isTypeSArray ()->dim->toInteger ();
dinteger_t esize = ebtype->nextOf ()->size ();
@ -490,12 +490,12 @@ convert_expr (tree exp, Type *etype, Type *totype)
return d_array_value (build_ctype (totype), size_int (dim),
build_nop (ptrtype, build_address (exp)));
}
else if (tbtype->ty == Tsarray)
else if (tbtype->ty == TY::Tsarray)
{
/* D allows casting a static array to any static array type. */
return build_nop (build_ctype (totype), exp);
}
else if (tbtype->ty == Tstruct)
else if (tbtype->ty == TY::Tstruct)
{
/* And allows casting a static array to any struct type too.
Type sizes should have already been checked by the frontend. */
@ -510,12 +510,12 @@ convert_expr (tree exp, Type *etype, Type *totype)
}
break;
case Tarray:
if (tbtype->ty == Tpointer)
case TY::Tarray:
if (tbtype->ty == TY::Tpointer)
{
return d_convert (build_ctype (totype), d_array_ptr (exp));
}
else if (tbtype->ty == Tarray)
else if (tbtype->ty == TY::Tarray)
{
/* Assume tvoid->size() == 1. */
d_uns64 fsize = ebtype->nextOf ()->toBasetype ()->size ();
@ -523,9 +523,18 @@ convert_expr (tree exp, Type *etype, Type *totype)
if (fsize != tsize)
{
/* Conversion requires a reinterpret cast of array. */
return build_libcall (LIBCALL_ARRAYCAST, totype, 3,
size_int (tsize), size_int (fsize), exp);
/* Conversion requires a reinterpret cast of array.
This case should have been lowered in the semantic pass. */
if (tsize != 0 && fsize % tsize == 0)
{
/* Set array dimension to (length * (fsize / tsize)). */
tree newlength = size_mult_expr (d_array_length (exp),
size_int (fsize / tsize));
return d_array_value (build_ctype (totype), newlength,
d_array_ptr (exp));
}
else
gcc_unreachable ();
}
else
{
@ -534,7 +543,7 @@ convert_expr (tree exp, Type *etype, Type *totype)
return build_vconvert (build_ctype (totype), exp);
}
}
else if (tbtype->ty == Tsarray)
else if (tbtype->ty == TY::Tsarray)
{
/* Strings are treated as dynamic arrays in D2. */
if (ebtype->isString () && tbtype->isString ())
@ -548,23 +557,23 @@ convert_expr (tree exp, Type *etype, Type *totype)
}
break;
case Taarray:
if (tbtype->ty == Taarray)
case TY::Taarray:
if (tbtype->ty == TY::Taarray)
return build_vconvert (build_ctype (totype), exp);
/* Can convert associative arrays to void pointers. */
else if (tbtype->ty == Tpointer && tbtype->nextOf ()->ty == Tvoid)
else if (tbtype->ty == TY::Tpointer && tbtype->nextOf ()->ty == TY::Tvoid)
return build_vconvert (build_ctype (totype), exp);
/* Else, default conversion, which should product an error. */
break;
case Tpointer:
case TY::Tpointer:
/* Can convert void pointers to associative arrays too. */
if (tbtype->ty == Taarray && ebtype->nextOf ()->ty == Tvoid)
if (tbtype->ty == TY::Taarray && ebtype->nextOf ()->ty == TY::Tvoid)
return build_vconvert (build_ctype (totype), exp);
break;
case Tnull:
case Tnoreturn:
case TY::Tnull:
case TY::Tnoreturn:
/* Casting from `typeof(null)' for `null' expressions, or `typeof(*null)'
for `noreturn' expressions is represented as all zeros. */
result = build_typeof_null_value (totype);
@ -574,8 +583,8 @@ convert_expr (tree exp, Type *etype, Type *totype)
result = compound_expr (exp, result);
break;
case Tvector:
if (tbtype->ty == Tsarray)
case TY::Tvector:
if (tbtype->ty == TY::Tsarray)
{
if (tbtype->size () == ebtype->size ())
return build_vconvert (build_ctype (totype), exp);
@ -613,7 +622,7 @@ convert_for_rvalue (tree expr, Type *etype, Type *totype)
Type *ebtype = etype->toBasetype ();
Type *tbtype = totype->toBasetype ();
if (ebtype->ty == Tbool)
if (ebtype->ty == TY::Tbool)
{
/* If casting from bool, the result is either 0 or 1, any other value
violates @safe code, so enforce that it is never invalid. */
@ -651,7 +660,7 @@ convert_for_assignment (tree expr, Type *etype, Type *totype)
/* Assuming this only has to handle converting a non Tsarray type to
arbitrarily dimensioned Tsarrays. */
if (tbtype->ty == Tsarray)
if (tbtype->ty == TY::Tsarray)
{
Type *telem = tbtype->nextOf ()->baseElemOf ();
@ -685,7 +694,7 @@ convert_for_assignment (tree expr, Type *etype, Type *totype)
}
/* D Front end uses IntegerExp(0) to mean zero-init an array or structure. */
if ((tbtype->ty == Tsarray || tbtype->ty == Tstruct)
if ((tbtype->ty == TY::Tsarray || tbtype->ty == TY::Tstruct)
&& ebtype->isintegral ())
{
if (!integer_zerop (expr))
@ -736,12 +745,12 @@ convert_for_condition (tree expr, Type *type)
switch (type->toBasetype ()->ty)
{
case Taarray:
case TY::Taarray:
/* Checks that aa.ptr !is null. */
result = component_ref (expr, TYPE_FIELDS (TREE_TYPE (expr)));
break;
case Tarray:
case TY::Tarray:
{
/* Checks (arr.length || arr.ptr) (i.e arr !is null). */
expr = d_save_expr (expr);
@ -762,7 +771,7 @@ convert_for_condition (tree expr, Type *type)
break;
}
case Tdelegate:
case TY::Tdelegate:
{
/* Checks (function || object), but what good is it if there is
a null function pointer? */
@ -783,7 +792,7 @@ convert_for_condition (tree expr, Type *type)
break;
}
case Tnoreturn:
case TY::Tnoreturn:
/* Front-end allows conditionals that never return, represent the
conditional result value as all zeros. */
result = build_zero_cst (d_bool_type);
@ -810,10 +819,10 @@ d_array_convert (Expression *exp)
{
Type *tb = exp->type->toBasetype ();
if (tb->ty == Tarray)
if (tb->ty == TY::Tarray)
return build_expr (exp);
if (tb->ty == Tsarray)
if (tb->ty == TY::Tsarray)
{
Type *totype = tb->nextOf ()->arrayOf ();
return convert_expr (build_expr (exp), exp->type, totype);
@ -832,7 +841,8 @@ d_array_convert (Type *etype, Expression *exp)
{
Type *tb = exp->type->toBasetype ();
if ((tb->ty != Tarray && tb->ty != Tsarray) || same_type_p (tb, etype))
if ((tb->ty != TY::Tarray && tb->ty != TY::Tsarray)
|| same_type_p (tb, etype))
{
/* Convert single element to an array. */
tree expr = build_expr (exp);

View file

@ -222,15 +222,6 @@ d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format,
message prefix PREFIX1 and PREFIX2, increasing the global or gagged
error count. */
void ATTRIBUTE_GCC_DIAG(2,3)
error (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
verror (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
verror (const Loc &loc, const char *format, va_list ap,
const char *prefix1, const char *prefix2, const char *)
@ -263,15 +254,6 @@ verror (const Loc &loc, const char *format, va_list ap,
/* Print supplementary message about the last error with explicit location LOC.
This doesn't increase the global error count. */
void ATTRIBUTE_GCC_DIAG(2,3)
errorSupplemental (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
verrorSupplemental (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
verrorSupplemental (const Loc &loc, const char *format, va_list ap)
{
@ -284,15 +266,6 @@ verrorSupplemental (const Loc &loc, const char *format, va_list ap)
/* Print a warning message with explicit location LOC, increasing the
global warning count. */
void ATTRIBUTE_GCC_DIAG(2,3)
warning (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
vwarning (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
vwarning (const Loc &loc, const char *format, va_list ap)
{
@ -311,15 +284,6 @@ vwarning (const Loc &loc, const char *format, va_list ap)
/* Print supplementary message about the last warning with explicit location
LOC. This doesn't increase the global warning count. */
void ATTRIBUTE_GCC_DIAG(2,3)
warningSupplemental (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
vwarningSupplemental (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
vwarningSupplemental (const Loc &loc, const char *format, va_list ap)
{
@ -333,15 +297,6 @@ vwarningSupplemental (const Loc &loc, const char *format, va_list ap)
message prefix PREFIX1 and PREFIX2, increasing the global warning or
error count depending on how deprecations are treated. */
void ATTRIBUTE_GCC_DIAG(2,3)
deprecation (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
vdeprecation (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
vdeprecation (const Loc &loc, const char *format, va_list ap,
const char *prefix1, const char *prefix2)
@ -372,15 +327,6 @@ vdeprecation (const Loc &loc, const char *format, va_list ap,
/* Print supplementary message about the last deprecation with explicit
location LOC. This does not increase the global error count. */
void ATTRIBUTE_GCC_DIAG(2,3)
deprecationSupplemental (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
vdeprecationSupplemental (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
vdeprecationSupplemental (const Loc &loc, const char *format, va_list ap)
{
@ -392,30 +338,19 @@ vdeprecationSupplemental (const Loc &loc, const char *format, va_list ap)
/* Print a verbose message with explicit location LOC. */
void ATTRIBUTE_GCC_DIAG(2, 3)
message (const Loc &loc, const char *format, ...)
{
va_list ap;
va_start (ap, format);
vmessage (loc, format, ap);
va_end (ap);
}
void ATTRIBUTE_GCC_DIAG(2,0)
vmessage (const Loc &loc, const char *format, va_list ap)
{
d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, true);
}
/* Same as above, but doesn't take a location argument. */
/* Print a tip message with prefix and highlighing. */
void ATTRIBUTE_GCC_DIAG(1, 2)
message (const char *format, ...)
void ATTRIBUTE_GCC_DIAG(1,0)
vtip (const char *format, va_list ap)
{
va_list ap;
va_start (ap, format);
vmessage (Loc (), format, ap);
va_end (ap);
if (!global.gag)
d_diagnostic_report_diagnostic (Loc (), 0, format, ap, DK_DEBUG, true);
}
/* Call this after printing out fatal error messages to clean up and

View file

@ -27,116 +27,11 @@ along with GCC; see the file COPYING3. If not see
#include "dmd/scope.h"
#include "tree.h"
#include "options.h"
#include "fold-const.h"
#include "diagnostic.h"
#include "d-tree.h"
/* Implements the Global interface defined by the frontend.
Used for managing the state of the current compilation. */
Global global;
void
Global::_init (void)
{
this->mars_ext = "d";
this->hdr_ext = "di";
this->doc_ext = "html";
this->ddoc_ext = "ddoc";
this->json_ext = "json";
this->obj_ext = "o";
this->run_noext = true;
this->version = "v"
#include "verstr.h"
;
this->stdmsg = stderr;
}
/* Start gagging. Return the current number of gagged errors. */
unsigned
Global::startGagging (void)
{
this->gag++;
return this->gaggedErrors;
}
/* End gagging, restoring the old gagged state. Return true if errors
occured while gagged. */
bool
Global::endGagging (unsigned oldGagged)
{
bool anyErrs = (this->gaggedErrors != oldGagged);
this->gag--;
/* Restore the original state of gagged errors; set total errors
to be original errors + new ungagged errors. */
this->errors -= (this->gaggedErrors - oldGagged);
this->gaggedErrors = oldGagged;
return anyErrs;
}
/* Increment the error count to record that an error has occured in the
current context. An error message may or may not have been printed. */
void
Global::increaseErrorCount (void)
{
if (gag)
this->gaggedErrors++;
this->errors++;
}
/* Implements the Loc interface defined by the frontend.
Used for keeping track of current file/line position in code. */
Loc::Loc (const char *filename, unsigned linnum, unsigned charnum)
{
this->linnum = linnum;
this->charnum = charnum;
this->filename = filename;
}
const char *
Loc::toChars (void) const
{
OutBuffer buf;
if (this->filename)
buf.printf ("%s", this->filename);
if (this->linnum)
{
buf.printf (":%u", this->linnum);
if (this->charnum)
buf.printf (":%u", this->charnum);
}
return buf.extractChars ();
}
bool
Loc::equals (const Loc &loc)
{
if (this->linnum != loc.linnum || this->charnum != loc.charnum)
return false;
if (!FileName::equals (this->filename, loc.filename))
return false;
return true;
}
/* Implements back-end specific interfaces used by the frontend. */
/* Determine if function FD is a builtin one that we can evaluate in CTFE. */
@ -144,7 +39,7 @@ Loc::equals (const Loc &loc)
BUILTIN
isBuiltin (FuncDeclaration *fd)
{
if (fd->builtin != BUILTINunknown)
if (fd->builtin != BUILTIN::unknown)
return fd->builtin;
maybe_set_intrinsic (fd);
@ -158,7 +53,7 @@ isBuiltin (FuncDeclaration *fd)
Expression *
eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
{
if (fd->builtin == BUILTINunimp)
if (fd->builtin == BUILTIN::unimp)
return NULL;
tree decl = get_symbol_decl (fd);
@ -185,16 +80,8 @@ eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments)
Type *
getTypeInfoType (Loc loc, Type *type, Scope *sc)
{
gcc_assert (type->ty != Terror);
gcc_assert (type->ty != TY::Terror);
check_typeinfo_type (loc, sc);
create_typeinfo (type, sc ? sc->_module->importedFrom : NULL);
return type->vtinfo->type;
}
/* Return an inlined copy of a default argument for a function parameter. */
Expression *
inlineCopy (Expression *e, Scope *)
{
return e->copy ();
}

37
gcc/d/d-frontend.h Normal file
View file

@ -0,0 +1,37 @@
/* d-frontend.h -- D frontend interface to the gcc back-end.
Copyright (C) 2019 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#ifndef GCC_D_FRONTEND_H
#define GCC_D_FRONTEND_H
/* These functions are defined in D runtime. */
extern "C" int rt_init (void);
extern "C" int rt_term (void);
//extern "C" void gc_disable (void);
extern "C" void *gc_malloc (size_t sz, unsigned ba = 0, const void *ti = NULL);
extern "C" void gc_free (void *);
extern "C" void gc_collect (void);
template<typename T>
inline T *
d_gc_malloc (void)
{
void *ptr = gc_malloc (sizeof (T));
return new(ptr) T ();
}
#endif

View file

@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
#include "coretypes.h"
#include "dmd/globals.h"
#include "d-frontend.h"
#include "cppdefault.h"
@ -71,7 +72,7 @@ add_globalpaths (Strings *paths)
if (paths)
{
if (!global.path)
global.path = new Strings ();
global.path = d_gc_malloc<Strings> ();
for (size_t i = 0; i < paths->length; i++)
{
@ -98,7 +99,7 @@ add_filepaths (Strings *paths)
if (paths)
{
if (!global.filePath)
global.filePath = new Strings ();
global.filePath = d_gc_malloc<Strings> ();
for (size_t i = 0; i < paths->length; i++)
{

View file

@ -26,12 +26,13 @@ along with GCC; see the file COPYING3. If not see
#include "dmd/errors.h"
#include "dmd/expression.h"
#include "dmd/hdrgen.h"
#include "dmd/id.h"
#include "dmd/identifier.h"
#include "dmd/json.h"
#include "dmd/mangle.h"
#include "dmd/mars.h"
#include "dmd/module.h"
#include "dmd/mtype.h"
#include "dmd/root/file.h"
#include "dmd/target.h"
#include "opts.h"
@ -53,7 +54,7 @@ along with GCC; see the file COPYING3. If not see
#include "input.h"
#include "d-tree.h"
#include "id.h"
#include "d-frontend.h"
/* Array of D frontend type/decl nodes. */
@ -83,10 +84,6 @@ d_option;
/* List of modules being compiled. */
static Modules builtin_modules;
/* Module where `C main' is defined, compiled in if needed. */
static Module *entrypoint_module = NULL;
static Module *entrypoint_root_module = NULL;
/* The current and global binding level in effect. */
struct binding_level *current_binding_level;
struct binding_level *global_binding_level;
@ -202,7 +199,7 @@ deps_write (Module *module, obstack *buffer)
deps_write_string (d_option.deps_target[i], buffer, column);
}
else
deps_write_string (module->objfile->name->str, buffer, column);
deps_write_string (module->objfile.toChars (), buffer, column);
obstack_1grow (buffer, ':');
column++;
@ -212,7 +209,7 @@ deps_write (Module *module, obstack *buffer)
{
Module *depmod = modlist.pop ();
const char *modstr = depmod->srcfile->name->str;
const char *modstr = depmod->srcfile.toChars ();
/* Skip modules that have already been looked at. */
if (seen_modules.add (modstr))
@ -238,9 +235,7 @@ deps_write (Module *module, obstack *buffer)
Module *m = depmod->aimports[i];
/* Ignore compiler-generated modules. */
if ((m->ident == Identifier::idPool ("__entrypoint")
|| m->ident == Identifier::idPool ("__main"))
&& m->parent == NULL)
if (m->ident == Identifier::idPool ("__main") && m->parent == NULL)
continue;
/* Don't search system installed modules, this includes
@ -251,9 +246,9 @@ deps_write (Module *module, obstack *buffer)
&& m->parent == NULL)
continue;
if (m->md && m->md->packages)
if (m->md && m->md->packages.length)
{
Identifier *package = (*m->md->packages)[0];
Identifier *package = m->md->packages.ptr[0];
if (package == Identifier::idPool ("core")
|| package == Identifier::idPool ("std")
@ -291,27 +286,15 @@ deps_write (Module *module, obstack *buffer)
static void
d_init_options (unsigned int, cl_decoded_option *decoded_options)
{
/* Initialize the D runtime. */
rt_init ();
// gc_disable ();
/* Set default values. */
global._init ();
global.vendor = lang_hooks.name;
global.params.argv0 = xstrdup (decoded_options[0].arg);
global.params.link = true;
global.params.useAssert = CHECKENABLEdefault;
global.params.useInvariants = CHECKENABLEdefault;
global.params.useIn = CHECKENABLEdefault;
global.params.useOut = CHECKENABLEdefault;
global.params.useArrayBounds = CHECKENABLEdefault;
global.params.useSwitchError = CHECKENABLEdefault;
global.params.checkAction = CHECKACTION_D;
global.params.useModuleInfo = true;
global.params.useTypeInfo = true;
global.params.useExceptions = true;
global.params.useInline = false;
global.params.obj = true;
global.params.hdrStripPlainFunctions = true;
global.params.betterC = false;
global.params.allInst = false;
global.params.errorLimit = flag_max_errors;
/* Default extern(C++) mangling to C++14. */
@ -320,9 +303,10 @@ 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.messageStyle = MESSAGESTYLEgnu;
global.params.imppath = new Strings ();
global.params.fileImppath = new Strings ();
global.params.imppath = d_gc_malloc<Strings> ();
global.params.fileImppath = d_gc_malloc<Strings> ();
/* Extra GDC-specific options. */
d_option.fonly = NULL;
@ -462,6 +446,11 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
: (value == 1) ? CHECKENABLEsafeonly : CHECKENABLEoff;
break;
case OPT_fcheckaction_:
global.params.checkAction = (value == 0) ? CHECKACTION_D
: (value == 1) ? CHECKACTION_halt : CHECKACTION_context;
break;
case OPT_fdebug:
global.params.debuglevel = value ? 1 : 0;
break;
@ -480,7 +469,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
if (Identifier::isValidIdentifier (CONST_CAST (char *, arg)))
{
if (!global.params.debugids)
global.params.debugids = new Strings ();
global.params.debugids = d_gc_malloc<Strings> ();
global.params.debugids->push (arg);
break;
}
@ -510,6 +499,16 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.betterC = !value;
break;
case OPT_fdump_c___spec_:
if (global.params.doCxxHdrGeneration == CxxHeaderMode::none)
global.params.doCxxHdrGeneration = CxxHeaderMode::silent;
global.params.cxxhdrname = arg;
break;
case OPT_fdump_c___spec_verbose:
global.params.doCxxHdrGeneration = CxxHeaderMode::verbose;
break;
case OPT_fdump_d_original:
global.params.vcg_ast = value;
break;
@ -518,6 +517,22 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.useExceptions = value;
break;
case OPT_fextern_std_:
switch (value)
{
case CppStdRevisionCpp98:
case CppStdRevisionCpp11:
case CppStdRevisionCpp14:
case CppStdRevisionCpp17:
case CppStdRevisionCpp20:
global.params.cplusplus = (CppStdRevision) value;
break;
default:
error ("bad argument for %<-fextern-std%>: %qs", arg);
}
break;
case OPT_fignore_unknown_pragmas:
global.params.ignoreUnsupportedPragmas = value;
break;
@ -552,35 +567,115 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.useIn = value ? CHECKENABLEon : CHECKENABLEoff;
break;
case OPT_fpreview_all:
global.params.ehnogc = value;
global.params.useDIP25 = FeatureState::enabled;
global.params.useDIP1000 = FeatureState::enabled;
global.params.useDIP1021 = value;
global.params.dtorFields = FeatureState::enabled;
global.params.fieldwise = value;
global.params.fixAliasThis = value;
global.params.previewIn = value;
global.params.fix16997 = value;
global.params.markdown = value;
global.params.noSharedAccess = value;
global.params.rvalueRefParam = value;
global.params.inclusiveInContracts = value;
global.params.shortenedMethods = value;
break;
case OPT_fpreview_dip1000:
global.params.useDIP1000 = FeatureState::enabled;
break;
case OPT_fpreview_dip1008:
global.params.ehnogc = value;
break;
case OPT_fpreview_dip1021:
global.params.useDIP1021 = value;
break;
case OPT_fpreview_dip25:
global.params.useDIP25 = FeatureState::enabled;
break;
case OPT_fpreview_dtorfields:
global.params.dtorFields = FeatureState::enabled;
break;
case OPT_fpreview_fieldwise:
global.params.fieldwise = value;
break;
case OPT_fpreview_fixaliasthis:
global.params.fixAliasThis = value;
break;
case OPT_fpreview_in:
global.params.previewIn = value;
break;
case OPT_fpreview_inclusiveincontracts:
global.params.inclusiveInContracts = value;
break;
case OPT_fpreview_intpromote:
global.params.fix16997 = value;
break;
case OPT_fpreview_nosharedaccess:
global.params.noSharedAccess = value;
break;
case OPT_fpreview_rvaluerefparam:
global.params.rvalueRefParam = value;
break;
case OPT_fpreview_shortenedmethods:
global.params.shortenedMethods = value;
break;
case OPT_frelease:
global.params.release = value;
break;
case OPT_frevert_all:
global.params.useDIP25 = FeatureState::disabled;
global.params.markdown = !value;
global.params.dtorFields = FeatureState::disabled;
break;
case OPT_frevert_dip25:
global.params.useDIP25 = FeatureState::disabled;
break;
case OPT_frevert_dtorfields:
global.params.dtorFields = FeatureState::disabled;
break;
case OPT_frevert_markdown:
global.params.markdown = !value;
break;
case OPT_frtti:
global.params.useTypeInfo = value;
break;
case OPT_fsave_mixins_:
global.params.mixinFile = arg;
global.params.mixinOut = d_gc_malloc<OutBuffer> ();
break;
case OPT_fswitch_errors:
global.params.useSwitchError = value ? CHECKENABLEon : CHECKENABLEoff;
break;
case OPT_ftransition_all:
global.params.vtls = value;
global.params.vfield = value;
global.params.vcomplex = value;
break;
case OPT_ftransition_complex:
global.params.vcomplex = value;
break;
case OPT_ftransition_dip1000:
global.params.vsafe = value;
global.params.useDIP25 = value;
break;
case OPT_ftransition_dip25:
global.params.useDIP25 = value;
global.params.vgc = value;
global.params.vmarkdown= value;
global.params.vtls = value;
break;
case OPT_ftransition_field:
@ -591,6 +686,14 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
global.params.vgc = value;
break;
case OPT_ftransition_vmarkdown:
global.params.vmarkdown = value;
break;
case OPT_ftransition_templates:
global.params.vtemplates = value;
break;
case OPT_ftransition_tls:
global.params.vtls = value;
break;
@ -613,7 +716,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
if (Identifier::isValidIdentifier (CONST_CAST (char *, arg)))
{
if (!global.params.versionids)
global.params.versionids = new Strings ();
global.params.versionids = d_gc_malloc<Strings> ();
global.params.versionids->push (arg);
break;
}
@ -804,6 +907,14 @@ d_post_options (const char ** fn)
global.params.checkAction = CHECKACTION_C;
}
/* Enabling DIP1021 implies DIP1000. */
if (global.params.useDIP1021)
global.params.useDIP1000 = FeatureState::enabled;
/* Enabling DIP1000 implies DIP25. */
if (global.params.useDIP1000 == FeatureState::enabled)
global.params.useDIP25 = FeatureState::enabled;
/* Keep in sync with existing -fbounds-check flag. */
flag_bounds_check = (global.params.useArrayBounds == CHECKENABLEon);
@ -828,6 +939,7 @@ d_post_options (const char ** fn)
global.params.symdebug = write_symbols != NO_DEBUG;
global.params.useInline = flag_inline_functions;
global.params.showColumns = flag_show_column;
global.params.printErrorContext = flag_diagnostics_show_caret;
if (global.params.useInline)
global.params.hdrStripPlainFunctions = false;
@ -872,17 +984,6 @@ d_add_builtin_module (Module *m)
builtin_modules.push (m);
}
/* Record the entrypoint module ENTRY which will be compiled in the current
compilation. ROOT is the module scope where this was requested from. */
void
d_add_entrypoint_module (Module *entry, Module *root)
{
/* We are emitting this straight to object file. */
entrypoint_module = entry;
entrypoint_root_module = root;
}
/* Implements the lang_hooks.parse_file routine for language D. */
static void
@ -891,7 +992,7 @@ d_parse_file (void)
if (global.params.verbose)
{
message ("binary %s", global.params.argv0.ptr);
message ("version %s", global.version.ptr);
message ("version %s", global.versionChars ());
if (global.versionids)
{
@ -955,16 +1056,16 @@ d_parse_file (void)
/* Handling stdin, generate a unique name for the module. */
Module *m = Module::create (in_fnames[i],
Identifier::generateId ("__stdin"),
Identifier::idPool ("__stdin"),
global.params.doDocComments,
global.params.doHdrGeneration);
modules.push (m);
/* Overwrite the source file for the module, the one created by
Module::create would have a forced a `.d' suffix. */
m->srcfile = File::create ("<stdin>");
m->srcfile->len = len;
m->srcfile->buffer = buffer;
m->srcBuffer = FileBuffer::create ();
m->srcBuffer->data.length = len;
m->srcBuffer->data.ptr = buffer;
}
else
{
@ -1011,15 +1112,16 @@ d_parse_file (void)
}
/* Load the module containing D main. */
Module *main_module = NULL;
if (global.params.addMain)
{
unsigned errors = global.startGagging ();
Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__main"));
main_module = Module::load (Loc (), NULL, Identifier::idPool ("__main"));
if (!global.endGagging (errors))
{
m->importedFrom = m;
modules.push (m);
main_module->importedFrom = main_module;
modules.push (main_module);
}
}
@ -1038,7 +1140,7 @@ d_parse_file (void)
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
if (d_option.fonly && m != Module::rootModule)
if (m->isHdrFile || (d_option.fonly && m != Module::rootModule))
continue;
if (global.params.verbose)
@ -1203,7 +1305,7 @@ d_parse_file (void)
if (name && (name[0] != '-' || name[1] != '\0'))
{
const char *nameext
= FileName::defaultExt (name, global.json_ext.ptr);
= FileName::defaultExt (name, json_ext.ptr);
json_stream = fopen (nameext, "w");
if (!json_stream)
{
@ -1245,22 +1347,25 @@ d_parse_file (void)
}
}
/* Generate C++ header files. */
if (global.params.doCxxHdrGeneration != CxxHeaderMode::none)
genCppHdrFiles (modules);
if (global.errors)
goto had_errors;
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
if (d_option.fonly && m != Module::rootModule)
if ((m->isHdrFile && m != main_module)
|| (d_option.fonly && m != Module::rootModule))
continue;
if (global.params.verbose)
message ("code %s", m->toChars ());
if (!flag_syntax_only)
{
if ((entrypoint_module != NULL) && (m == entrypoint_root_module))
build_decl_tree (entrypoint_module);
build_decl_tree (m);
}
build_decl_tree (m);
}
/* And end the main input file, if the debug writer wants it. */
@ -1272,16 +1377,37 @@ d_parse_file (void)
exit with an error status. */
errorcount += (global.errors + global.warnings);
/* We want to write the mixin expansion file also on error. */
if (global.params.mixinOut)
{
FILE *mixin_stream = fopen (global.params.mixinFile, "w");
if (mixin_stream)
{
OutBuffer *buf = global.params.mixinOut;
fprintf (mixin_stream, "%s", buf->peekChars ());
if (ferror (mixin_stream) || fclose (mixin_stream))
fatal_error (input_location, "closing mixin file %s: %m",
global.params.mixinFile);
}
else
{
fatal_error (input_location, "opening mixin file %s: %m",
global.params.mixinFile);
}
}
/* Remove generated .di files on error. */
if (errorcount && dump_headers)
{
for (size_t i = 0; i < modules.length; i++)
{
Module *m = modules[i];
if (d_option.fonly && m != Module::rootModule)
if (m->isHdrFile || (d_option.fonly && m != Module::rootModule))
continue;
remove (m->hdrfile->toChars ());
remove (m->hdrfile.toChars ());
}
}
@ -1406,7 +1532,7 @@ d_type_promotes_to (tree type)
/* Promotions are only applied on unnamed function arguments for declarations
with `extern(C)' or `extern(C++)' linkage. */
if (cfun && DECL_LANG_FRONTEND (cfun->decl)
&& DECL_LANG_FRONTEND (cfun->decl)->linkage != LINKd)
&& DECL_LANG_FRONTEND (cfun->decl)->linkage != LINK::d)
{
/* In [type/integer-promotions], integer promotions are conversions of the
following types:
@ -1559,7 +1685,8 @@ d_types_compatible_p (tree x, tree y)
return true;
/* Type system allows implicit conversion between. */
if (tx->implicitConvTo (ty) || ty->implicitConvTo (tx))
if (tx->implicitConvTo (ty) != MATCH::nomatch
|| ty->implicitConvTo (tx) != MATCH::nomatch)
return true;
}

View file

@ -30,12 +30,6 @@ along with GCC; see the file COPYING3. If not see
#include "longdouble.h"
/* Constant real values 0, 1, -1 and 0.5. */
real_t CTFloat::zero;
real_t CTFloat::one;
real_t CTFloat::minusone;
real_t CTFloat::half;
/* Truncate longdouble to the highest precision supported by target. */
longdouble

View file

@ -26,60 +26,8 @@
#endif
#include "system.h"
/* Used by the dmd front-end to determine if we have POSIX-style IO. */
#define POSIX (__linux__ || __GLIBC__ || __gnu_hurd__ || __APPLE__ \
|| __FreeBSD__ || __NetBSD__ || __OpenBSD__ || __DragonFly__ \
|| __sun || __unix__)
/* Forward assert invariants to gcc_assert. */
#undef assert
#define assert(EXPR) gcc_assert(EXPR)
/* Use libiberty's lrealpath to avoid portability problems. */
#undef realpath
#define realpath(a, b) lrealpath((a))
/* Forward ctype.h macros used by the dmd front-end to safe-ctype.h. */
#undef isalpha
#define isalpha(c) ISALPHA(c)
#undef isalnum
#define isalnum(c) ISALNUM(c)
#undef isdigit
#define isdigit(c) ISDIGIT(c)
#undef islower
#define islower(c) ISLOWER(c)
#undef isprint
#define isprint(c) ISPRINT(c)
#undef isspace
#define isspace(c) ISSPACE(c)
#undef isupper
#define isupper(c) ISUPPER(c)
#undef isxdigit
#define isxdigit(c) ISXDIGIT(c)
#undef tolower
#define tolower(c) TOLOWER(c)
/* Forward _mkdir on MinGW to mkdir in system.h. */
#ifdef _WIN32
#undef _mkdir
#define _mkdir(p) mkdir(p, 0)
#endif
/* Define any missing _MAX and _MIN macros. */
#ifndef INT32_MAX
# define INT32_MAX INTTYPE_MAXIMUM (int32_t)
#endif
#ifndef INT32_MIN
# define INT32_MIN INTTYPE_MINIMUM (int32_t)
#endif
#ifndef INT64_MIN
# define INT64_MIN INTTYPE_MINIMUM (int64_t)
#endif
#ifndef UINT32_MAX
# define UINT32_MAX INTTYPE_MAXIMUM (uint32_t)
#endif
#ifndef UINT64_MAX
# define UINT64_MAX INTTYPE_MAXIMUM (uint64_t)
#endif
#endif /* GCC_D_SYSTEM_H */

View file

@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "tm.h"
#include "tm_p.h"
#include "target.h"
#include "calls.h"
#include "d-tree.h"
#include "d-target.h"
@ -42,8 +43,6 @@ along with GCC; see the file COPYING3. If not see
/* Implements the Target interface defined by the front end.
Used for retrieving target-specific information. */
Target target;
/* Internal key handlers for `__traits(getTargetInfo)'. */
static tree d_handle_target_cpp_std (void);
static tree d_handle_target_cpp_runtime_library (void);
@ -89,9 +88,6 @@ define_float_constants (T &f, tree type)
/* Floating-point NaN. */
real_nan (&f.nan.rv (), "", 1, mode);
/* Signalling floating-point NaN. */
real_nan (&f.snan.rv (), "", 0, mode);
/* Floating-point +Infinity if the target supports infinities. */
real_inf (&f.infinity.rv ());
@ -142,19 +138,19 @@ Target::_init (const Param &)
/* Define what type to use for size_t, ptrdiff_t. */
if (this->ptrsize == 8)
{
global.params.isLP64 = true;
Type::tsize_t = Type::basic[Tuns64];
Type::tptrdiff_t = Type::basic[Tint64];
this->isLP64 = true;
Type::tsize_t = Type::basic[(int)TY::Tuns64];
Type::tptrdiff_t = Type::basic[(int)TY::Tint64];
}
else if (this->ptrsize == 4)
{
Type::tsize_t = Type::basic[Tuns32];
Type::tptrdiff_t = Type::basic[Tint32];
Type::tsize_t = Type::basic[(int)TY::Tuns32];
Type::tptrdiff_t = Type::basic[(int)TY::Tint32];
}
else if (this->ptrsize == 2)
{
Type::tsize_t = Type::basic[Tuns16];
Type::tptrdiff_t = Type::basic[Tint16];
Type::tsize_t = Type::basic[(int)TY::Tuns16];
Type::tptrdiff_t = Type::basic[(int)TY::Tint16];
}
else
sorry ("D does not support pointers on this target.");
@ -164,15 +160,7 @@ Target::_init (const Param &)
/* Set-up target C ABI. */
this->c.longsize = int_size_in_bytes (long_integer_type_node);
this->c.long_doublesize = int_size_in_bytes (long_double_type_node);
/* Define what type to use for wchar_t. We don't want to support wide
characters less than "short" in D. */
if (WCHAR_TYPE_SIZE == 32)
this->c.twchar_t = Type::basic[Tdchar];
else if (WCHAR_TYPE_SIZE == 16)
this->c.twchar_t = Type::basic[Twchar];
else
sorry ("D does not support wide characters on this target.");
this->c.wchar_tsize = (WCHAR_TYPE_SIZE / BITS_PER_UNIT);
/* Set-up target C++ ABI. */
this->cpp.reverseOverloads = false;
@ -182,6 +170,12 @@ Target::_init (const Param &)
/* Set-up target Objective-C ABI. */
this->objc.supported = false;
/* Set-up environmental settings. */
this->obj_ext = "o";
this->lib_ext = "a";
this->dll_ext = "so";
this->run_noext = true;
/* Initialize all compile-time properties for floating-point types.
Should ensure that our real_t type is able to represent real_value. */
gcc_assert (sizeof (real_t) >= sizeof (real_value));
@ -273,7 +267,7 @@ 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 == Tbool)
if (!type->isTypeBasic () || type->iscomplex () || type->ty == TY::Tbool)
return 2;
/* In [simd/vector extensions], which vector types are supported depends on
@ -293,9 +287,9 @@ Target::isVectorTypeSupported (int sz, Type *type)
Returns true if the operation is supported or type is not a vector. */
bool
Target::isVectorOpSupported (Type *type, TOK op, Type *)
Target::isVectorOpSupported (Type *type, unsigned op, Type *)
{
if (type->ty != Tvector)
if (type->ty != TY::Tvector)
return true;
/* Don't support if type is non-scalar, such as __vector(void[]). */
@ -322,18 +316,10 @@ Target::isVectorOpSupported (Type *type, TOK op, Type *)
/* Logical operators must have a result type of bool. */
return false;
case TOKue:
case TOKlg:
case TOKule:
case TOKul:
case TOKuge:
case TOKug:
case TOKle:
case TOKlt:
case TOKge:
case TOKgt:
case TOKleg:
case TOKunord:
case TOKequal:
case TOKnotequal:
case TOKidentity:
@ -379,7 +365,8 @@ TargetCPP::thunkMangle (FuncDeclaration *fd, int offset)
const char *
TargetCPP::typeMangle (Type *type)
{
if (type->isTypeBasic () || type->ty == Tvector || type->ty == Tstruct)
if (type->isTypeBasic () || type->ty == TY::Tvector
|| type->ty == TY::Tstruct)
{
tree ctype = build_ctype (type);
return targetm.mangle_type (ctype);
@ -400,14 +387,14 @@ TargetCPP::parameterType (Parameter *arg)
else if (arg->storageClass & STClazy)
{
/* Mangle as delegate. */
Type *td = TypeFunction::create (NULL, t, VARARGnone, LINKd);
td = TypeDelegate::create (td);
t = t->merge2 ();
TypeFunction *tf = TypeFunction::create (NULL, t, VARARGnone, LINK::d);
TypeDelegate *td = TypeDelegate::create (tf);
t = td->merge2 ();
}
/* Could be a va_list, which we mangle as a pointer. */
Type *tvalist = target.va_listType (Loc (), NULL);
if (t->ty == Tsarray && tvalist->ty == Tsarray)
if (t->ty == TY::Tsarray && tvalist->ty == TY::Tsarray)
{
Type *tb = t->toBasetype ()->mutableOf ();
if (tb == tvalist)
@ -450,10 +437,10 @@ Target::systemLinkage (void)
/* In [attribute/linkage], `System' is the same as `Windows' on Windows
platforms, and `C' on other platforms. */
if (link_system)
return LINKwindows;
return LINK::windows;
}
return LINKc;
return LINK::c;
}
/* Generate a TypeTuple of the equivalent types used to determine if a
@ -477,12 +464,12 @@ 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 ();
return (tn->ty == Tstruct || tn->ty == Tsarray);
return (tn->ty == TY::Tstruct || tn->ty == TY::Tsarray);
}
/* Add all target info in HANDLERS to D_TARGET_INFO_TABLE for use by
@ -575,12 +562,49 @@ Target::getTargetInfo (const char *key, const Loc &loc)
return NULL;
}
/**
* Returns true if the implementation for object monitors is always defined
* in the D runtime library (rt/monitor_.d). */
/* Returns true if the callee invokes destructors for arguments. */
bool
Target::isCalleeDestroyingArgs (TypeFunction *tf)
{
return tf->linkage == LINK::d;
}
/* Returns true if the implementation for object monitors is always defined
in the D runtime library (rt/monitor_.d). */
bool
Target::libraryObjectMonitors (FuncDeclaration *, Statement *)
{
return true;
}
/* Decides whether an `in' parameter of the specified POD type PARAM_TYPE is to
be passed by reference or by valie. This is used only when compiling with
`-fpreview=in' enabled. */
bool
Target::preferPassByRef (Type *param_type)
{
if (param_type->size () == SIZE_INVALID)
return false;
tree type = build_ctype (param_type);
/* Prefer a `ref' if the type is an aggregate, and its size is greater than
its alignment. */
if (AGGREGATE_TYPE_P (type)
&& (!valid_constant_size_p (TYPE_SIZE_UNIT (type))
|| compare_tree_int (TYPE_SIZE_UNIT (type), TYPE_ALIGN (type)) > 0))
return true;
/* If the back-end is always going to pass this by invisible reference. */
if (pass_by_reference (NULL, function_arg_info (type, true)))
return true;
/* If returning the parameter means the caller will do RVO. */
if (targetm.calls.return_in_memory (type, NULL_TREE))
return true;
return false;
}

View file

@ -617,7 +617,6 @@ extern void add_import_paths (const char *, const char *, bool);
/* In d-lang.cc. */
extern void d_add_builtin_module (Module *);
extern void d_add_entrypoint_module (Module *, Module *);
extern d_tree_node_structure_enum d_tree_node_structure (lang_tree_node *);
extern struct lang_type *build_lang_type (Type *);
extern struct lang_decl *build_lang_decl (Declaration *);

View file

@ -106,9 +106,9 @@ gcc_attribute_p (Dsymbol *decl)
{
ModuleDeclaration *md = decl->getModule ()->md;
if (md && md->packages && md->packages->length == 1)
if (md && md->packages.length == 1)
{
if (!strcmp ((*md->packages)[0]->toChars (), "gcc")
if (!strcmp (md->packages.ptr[0]->toChars (), "gcc")
&& !strcmp (md->id->toChars (), "attributes"))
return true;
}
@ -116,6 +116,59 @@ gcc_attribute_p (Dsymbol *decl)
return false;
}
/* Subroutine of pragma declaration visitor for marking the function in the
defined in SYM as a global constructor or destructor. If ISCTOR is true,
then we're applying pragma(crt_constructor). */
static int
apply_pragma_crt (Dsymbol *sym, bool isctor)
{
AttribDeclaration *ad = sym->isAttribDeclaration ();
if (ad != NULL)
{
int nested = 0;
/* Walk all declarations of the attribute scope. */
Dsymbols *ds = ad->include (NULL);
if (ds)
{
for (size_t i = 0; i < ds->length; i++)
nested += apply_pragma_crt ((*ds)[i], isctor);
}
return nested;
}
FuncDeclaration *fd = sym->isFuncDeclaration ();
if (fd != NULL)
{
tree decl = get_decl_tree (fd);
/* Apply flags to the function. */
if (isctor)
{
DECL_STATIC_CONSTRUCTOR (decl) = 1;
decl_init_priority_insert (decl, DEFAULT_INIT_PRIORITY);
}
else
{
DECL_STATIC_DESTRUCTOR (decl) = 1;
decl_fini_priority_insert (decl, DEFAULT_INIT_PRIORITY);
}
if (fd->linkage != LINK::c)
{
error_at (make_location_t (fd->loc),
"must be %<extern(C)%> for %<pragma(%s)%>",
isctor ? "crt_constructor" : "crt_destructor");
}
return 1;
}
return 0;
}
/* Implements the visitor interface to lower all Declaration AST classes
emitted from the D Front-end to GCC trees.
All visit methods accept one parameter D, which holds the frontend AST
@ -246,19 +299,31 @@ public:
}
/* Pragmas are a way to pass special information to the compiler and to add
vendor specific extensions to D. We don't do anything here, yet. */
vendor specific extensions to D. */
void visit (PragmaDeclaration *d)
{
if (!global.params.ignoreUnsupportedPragmas)
if (d->ident == Identifier::idPool ("lib")
|| d->ident == Identifier::idPool ("startaddress"))
{
if (d->ident == Identifier::idPool ("lib")
|| d->ident == Identifier::idPool ("startaddress"))
if (!global.params.ignoreUnsupportedPragmas)
{
warning_at (make_location_t (d->loc), OPT_Wunknown_pragmas,
"pragma(%s) not implemented", d->ident->toChars ());
}
}
else if (d->ident == Identifier::idPool ("crt_constructor")
|| d->ident == Identifier::idPool ("crt_destructor"))
{
/* Handle pragma(crt_constructor) and pragma(crt_destructor). Apply
flag to indicate that the functions enclosed should run automatically
at the beginning or end of execution. */
bool isctor = (d->ident == Identifier::idPool ("crt_constructor"));
if (apply_pragma_crt (d, isctor) > 1)
error_at (make_location_t (d->loc),
"can only apply to a single declaration");
}
visit ((AttribDeclaration *) d);
}
@ -311,14 +376,14 @@ public:
nested members. Only applies to classes or structs. */
Type *tb = fd->type->nextOf ()->baseElemOf ();
while (tb->ty == Tarray || tb->ty == Tpointer)
while (tb->ty == TY::Tarray || tb->ty == TY::Tpointer)
tb = tb->nextOf ()->baseElemOf ();
TemplateInstance *ti = NULL;
if (tb->ty == Tstruct)
if (tb->ty == TY::Tstruct)
ti = tb->isTypeStruct ()->sym->isInstantiated ();
else if (tb->ty == Tclass)
else if (tb->ty == TY::Tclass)
ti = tb->isTypeClass ()->sym->isInstantiated ();
/* Return type is instantiated from this template declaration, walk over
@ -360,7 +425,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
if (d->type->ty == Terror)
if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@ -447,7 +512,8 @@ public:
if (fd2->isFuture ())
continue;
if (fd->leastAsSpecialized (fd2) || fd2->leastAsSpecialized (fd))
if (fd->leastAsSpecialized (fd2) != MATCH::nomatch
|| fd2->leastAsSpecialized (fd) != MATCH::nomatch)
{
error_at (make_location_t (fd->loc), "use of %qs",
fd->toPrettyChars ());
@ -474,7 +540,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
if (d->type->ty == Terror)
if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@ -495,7 +561,8 @@ public:
/* Generate C symbols. */
d->csym = get_classinfo_decl (d);
d->vtblsym = get_vtable_decl (d);
Dsymbol *vtblsym = d->vtblSymbol ();
vtblsym->csym = get_vtable_decl (d);
tree sinit = aggregate_initializer_decl (d);
/* Generate static initializer. */
@ -527,9 +594,9 @@ public:
}
}
DECL_INITIAL (d->vtblsym)
= build_constructor (TREE_TYPE (d->vtblsym), elms);
d_finish_decl (d->vtblsym);
DECL_INITIAL (vtblsym->csym)
= build_constructor (TREE_TYPE (vtblsym->csym), elms);
d_finish_decl (vtblsym->csym);
/* Add this decl to the current binding level. */
tree ctype = TREE_TYPE (build_ctype (d->type));
@ -547,7 +614,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
if (d->type->ty == Terror)
if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@ -590,7 +657,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
if (d->errors || d->type->ty == Terror)
if (d->errors || d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@ -629,7 +696,7 @@ public:
if (d->semanticRun >= PASSobj)
return;
if (d->type->ty == Terror)
if (d->type->ty == TY::Terror)
{
error_at (make_location_t (d->loc),
"had semantic errors when compiling");
@ -695,7 +762,7 @@ public:
/* Frontend should have already caught this. */
gcc_assert (!integer_zerop (size)
|| d->type->toBasetype ()->ty == Tsarray);
|| d->type->toBasetype ()->ty == TY::Tsarray);
d_finish_decl (decl);
@ -770,7 +837,7 @@ public:
/* Check if any errors occurred when running semantic. */
if (TypeFunction *tf = d->type->isTypeFunction ())
{
if (tf->next == NULL || tf->next->ty == Terror)
if (tf->next == NULL || tf->next->ty == TY::Terror)
return;
}
@ -1183,6 +1250,17 @@ get_symbol_decl (Declaration *decl)
}
else if (TREE_CODE (decl->csym) == FUNCTION_DECL)
{
/* Dual-context functions require the code generation to build an array
for the context pointer of the function, making the delicate task of
tracking which context to follow when encountering a non-local symbol,
and so are a not planned to be supported. */
if (fd->needThis () && !fd->isMember2 ())
{
fatal_error (make_location_t (fd->loc),
"function requires a dual-context, which is not yet "
"supported by GDC");
}
/* The real function type may differ from its declaration. */
tree fntype = TREE_TYPE (decl->csym);
tree newfntype = NULL_TREE;
@ -1238,9 +1316,9 @@ get_symbol_decl (Declaration *decl)
/* In [pragma/inline], functions decorated with `pragma(inline)' affects
whether they are inlined or not. */
if (fd->inlining == PINLINEalways)
if (fd->inlining == PINLINE::always)
DECL_DECLARED_INLINE_P (decl->csym) = 1;
else if (fd->inlining == PINLINEnever)
else if (fd->inlining == PINLINE::never)
DECL_UNINLINABLE (decl->csym) = 1;
/* Function was declared `naked'. */
@ -1254,13 +1332,6 @@ get_symbol_decl (Declaration *decl)
if (fd->generated)
DECL_ARTIFICIAL (decl->csym) = 1;
/* Vector array operations are always compiler generated. */
if (fd->isArrayOp)
{
DECL_ARTIFICIAL (decl->csym) = 1;
DECL_DECLARED_INLINE_P (decl->csym) = 1;
}
/* Ensure and require contracts are lexically nested in the function they
part of, but are always publicly callable. */
if (fd->ident == Identifier::idPool ("ensure")
@ -1271,7 +1342,7 @@ get_symbol_decl (Declaration *decl)
DECL_FINAL_P (decl->csym) = 1;
/* Function is of type `noreturn' or `typeof(*null)'. */
if (fd->type->nextOf ()->ty == Tnoreturn)
if (fd->type->nextOf ()->ty == TY::Tnoreturn)
TREE_THIS_VOLATILE (decl->csym) = 1;
/* Check whether this function is expanded by the frontend. */
@ -1298,10 +1369,10 @@ get_symbol_decl (Declaration *decl)
if (decl->storage_class & STCvolatile)
TREE_THIS_VOLATILE (decl->csym) = 1;
/* Protection attributes are used by the debugger. */
if (decl->protection.kind == Prot::private_)
/* Visibility attributes are used by the debugger. */
if (decl->visibility.kind == Visibility::private_)
TREE_PRIVATE (decl->csym) = 1;
else if (decl->protection.kind == Prot::protected_)
else if (decl->visibility.kind == Visibility::protected_)
TREE_PROTECTED (decl->csym) = 1;
/* Likewise, so could the deprecated attribute. */
@ -1794,7 +1865,7 @@ make_thunk (FuncDeclaration *decl, int offset)
forcing a D local thunk to be emitted. */
const char *ident;
if (decl->linkage == LINKcpp)
if (decl->linkage == LINK::cpp)
ident = target.cpp.thunkMangle (decl, offset);
else
{
@ -1810,7 +1881,9 @@ make_thunk (FuncDeclaration *decl, int offset)
SET_DECL_ASSEMBLER_NAME (thunk, DECL_NAME (thunk));
d_keep (thunk);
free (CONST_CAST (char *, ident));
if (decl->linkage != LINK::cpp)
free (CONST_CAST (char *, ident));
if (!DECL_EXTERNAL (function))
finish_thunk (thunk, function);
@ -1989,26 +2062,27 @@ d_mark_needed (tree decl)
tree
get_vtable_decl (ClassDeclaration *decl)
{
if (decl->vtblsym)
return decl->vtblsym;
if (decl->vtblsym && decl->vtblsym->csym)
return decl->vtblsym->csym;
tree ident = mangle_internal_decl (decl, "__vtbl", "Z");
/* Note: Using a static array type for the VAR_DECL, the DECL_INITIAL value
will have a different type. However the back-end seems to accept this. */
tree type = build_ctype (Type::tvoidptr->sarrayOf (decl->vtbl.length));
decl->vtblsym = declare_extern_var (ident, type);
DECL_LANG_SPECIFIC (decl->vtblsym) = build_lang_decl (NULL);
Dsymbol *vtblsym = decl->vtblSymbol ();
vtblsym->csym = declare_extern_var (ident, type);
DECL_LANG_SPECIFIC (vtblsym->csym) = build_lang_decl (NULL);
/* Class is a reference, want the record type. */
DECL_CONTEXT (decl->vtblsym) = TREE_TYPE (build_ctype (decl->type));
TREE_READONLY (decl->vtblsym) = 1;
DECL_VIRTUAL_P (decl->vtblsym) = 1;
DECL_CONTEXT (vtblsym->csym) = TREE_TYPE (build_ctype (decl->type));
TREE_READONLY (vtblsym->csym) = 1;
DECL_VIRTUAL_P (vtblsym->csym) = 1;
SET_DECL_ALIGN (decl->vtblsym, TARGET_VTABLE_ENTRY_ALIGN);
DECL_USER_ALIGN (decl->vtblsym) = true;
SET_DECL_ALIGN (vtblsym->csym, TARGET_VTABLE_ENTRY_ALIGN);
DECL_USER_ALIGN (vtblsym->csym) = true;
return decl->vtblsym;
return vtblsym->csym;
}
/* Helper function of build_class_instance. Find the field inside aggregate

View file

@ -1,4 +1,4 @@
27e388b4c4d292cac25811496aaf79341c05c940
b8384668f28741ad5884fc055a2bdb9c05fd95ec
The first line of this file holds the git revision number of the last
merge done from the dlang/dmd repository.

259
gcc/d/dmd/README.md Normal file
View file

@ -0,0 +1,259 @@
# DMD Source code
This is the source code to the DMD compiler
for the D Programming Language defined in the documents at
http://dlang.org/
These sources are free, they are redistributable and modifiable
under the terms of the Boost Software License, Version 1.0.
The terms of this license are in the file boostlicense.txt,
or see http://www.boost.org/LICENSE_1_0.txt.
If a particular file has a different license in it, that overrides
this license for that file.
-Walter Bright
## Directory structure
| Folder | Purpose |
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [dmd/](https://github.com/dlang/dmd/tree/master/src/dmd) | The dmd driver and front-end |
| [dmd/backend/](https://github.com/dlang/dmd/tree/master/src/dmd/backend) | Code generation for x86 or x86-64. Shared by the [Digital Mars C compiler](https://github.com/DigitalMars/Compiler/), but not [LDC](https://github.com/ldc-developers/ldc) or [GDC](https://gdcproject.org/). |
| [dmd/root/](https://github.com/dlang/dmd/tree/master/src/dmd/root) | Meant as a portable utility library, but ["it wasn't very good and the only project left using it is dmd"](https://github.com/dlang/dmd/pull/9844#issuecomment-498479516). |
DMD has a mostly flat directory structure, so this section aims to divide all source files into logical groups for easier navigation.
The groups are roughly ordered by how late they appear in the compilation process.
Note that these groups have no strict meaning, the category assignments are a bit subjective.
### Driver
| File | Purpose |
|-----------------------------------------------------------------------------|-----------------------------------------------------------------------|
| [mars.d](https://github.com/dlang/dmd/blob/master/src/dmd/mars.d) | The entry point. Contains `main`. |
| [cli.d](https://github.com/dlang/dmd/blob/master/src/dmd/cli.d) | Define the command line interface |
| [globals.d](https://github.com/dlang/dmd/blob/master/src/dmd/globals.d) | Define a structure storing command line options |
| [dinifile.d](https://github.com/dlang/dmd/blob/master/src/dmd/dinifile.d) | Parse settings from .ini file (`sc.ini` / `dmd.conf`) |
| [vsoptions.d](https://github.com/dlang/dmd/blob/master/src/dmd/vsoptions.d) | Detect the Microsoft Visual Studio toolchain for linking |
| [frontend.d](https://github.com/dlang/dmd/blob/master/src/dmd/frontend.d) | An interface for using DMD as a library |
| [errors.d](https://github.com/dlang/dmd/blob/master/src/dmd/errors.d) | Error reporting functionality |
| [target.d](https://github.com/dlang/dmd/blob/master/src/dmd/target.d) | Manage target-specific parameters for cross-compiling (for LDC/GDC) |
| [compiler.d](https://github.com/dlang/dmd/blob/master/src/dmd/compiler.d) | Describe a back-end compiler and implements compiler-specific actions |
### Lexing / parsing
| File | Purpose |
|-----------------------------------------------------------------------|----------------------------------------------------------------------|
| [lexer.d](https://github.com/dlang/dmd/blob/master/src/dmd/lexer.d) | Convert source code into tokens for the D and ImportC parsers |
| [entity.d](https://github.com/dlang/dmd/blob/master/src/dmd/entity.d) | Define "\\&Entity;" escape sequence for strings / character literals |
| [tokens.d](https://github.com/dlang/dmd/blob/master/src/dmd/tokens.d) | Define lexical tokens. |
| [parse.d](https://github.com/dlang/dmd/blob/master/src/dmd/parse.d) | D parser, converting tokens into an Abstract Syntax Tree (AST) |
| [cparse.d](https://github.com/dlang/dmd/blob/master/src/dmd/cparse.d) | ImportC parser, converting tokens into an Abstract Syntax Tree (AST) |
### Semantic analysis
**Symbols and declarations**
| File | Purpose |
|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| [dsymbol.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbol.d) | Base class for a D symbol, e.g. a variable, function, module, enum etc. |
| [identifier.d](https://github.com/dlang/dmd/blob/master/src/dmd/identifier.d) | Represents the name of a `Dsymbol` |
| [id.d](https://github.com/dlang/dmd/blob/master/src/dmd/id.d) | Define strings for pre-defined identifiers (e.g. `sizeof`, `string`) |
| [dscope.d](https://github.com/dlang/dmd/blob/master/src/dmd/dscope.d) | Define a 'scope' on which symbol lookup can be performed |
| [dtemplate.d](https://github.com/dlang/dmd/blob/master/src/dmd/dtemplate.d) | A template declaration or instance |
| [dmodule.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmodule.d) | Define a package and module |
| [mtype.d](https://github.com/dlang/dmd/blob/master/src/dmd/mtype.d) | Define expression types such as `int`, `char[]`, `void function()` |
| [arraytypes.d](https://github.com/dlang/dmd/blob/master/src/dmd/arraytypes.d) | For certain Declaration nodes of type `T`, provides aliases for `Array!T` |
| [declaration.d](https://github.com/dlang/dmd/blob/master/src/dmd/declaration.d) | Misc. declarations of `alias`, variables, type tuples, `ClassInfo` etc. |
| [denum.d](https://github.com/dlang/dmd/blob/master/src/dmd/denum.d) | Defines `enum` declarations and enum members |
| [attrib.d](https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d) | Declarations of 'attributes' such as `private`, `pragma()`, `immutable`, `@UDA`, `align`, `extern(C++)` and more |
| [func.d](https://github.com/dlang/dmd/blob/master/src/dmd/func.d) | Define a function declaration (includes function literals, `invariant`, `unittest`) |
| [dversion.d](https://github.com/dlang/dmd/blob/master/src/dmd/dversion.d) | Defines a version symbol, e.g. `version = ident`, `debug = ident` |
**AST nodes**
| File | Purpose |
|-----------------------------------------------------------------------------------|-------------------------------------------------------------|
| [ast_node.d](https://github.com/dlang/dmd/blob/master/src/dmd/ast_node.d) | Define an abstract AST node class |
| [astbase.d](https://github.com/dlang/dmd/blob/master/src/dmd/astbase.d) | Namespace of AST nodes that can be produced by the parser |
| [astcodegen.d](https://github.com/dlang/dmd/blob/master/src/dmd/astcodegen.d) | Namespace of AST nodes of a AST ready for code generation |
| [astenums.d](https://github.com/dlang/dmd/blob/master/src/dmd/astenums.d) | Enums common to DMD and AST |
| [expression.d](https://github.com/dlang/dmd/blob/master/src/dmd/expression.d) | Define expression AST nodes |
| [statement.d](https://github.com/dlang/dmd/blob/master/src/dmd/statement.d) | Define statement AST nodes |
| [staticassert.d](https://github.com/dlang/dmd/blob/master/src/dmd/staticassert.d) | Define a `static assert` AST node |
| [aggregate.d](https://github.com/dlang/dmd/blob/master/src/dmd/aggregate.d) | Define an aggregate (`struct`, `union` or `class`) AST node |
| [dclass.d](https://github.com/dlang/dmd/blob/master/src/dmd/dclass.d) | Define a `class` AST node |
| [dstruct.d](https://github.com/dlang/dmd/blob/master/src/dmd/dstruct.d) | Define a `struct` or `union` AST node |
| [init.d](https://github.com/dlang/dmd/blob/master/src/dmd/init.d) | Define variable initializers |
**AST visitors**
| File | Purpose |
|-----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
| [parsetimevisitor.d](https://github.com/dlang/dmd/blob/master/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/src/dmd/permissivevisitor.d) | Subclass of ParseTimeVisitor that does not `assert(0)` on unimplemented nodes |
| [strictvisitor.d](https://github.com/dlang/dmd/blob/master/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/src/dmd/visitor.d) | A visitor implementing `visit` for all nodes present in the compiler |
| [transitivevisitor.d](https://github.com/dlang/dmd/blob/master/src/dmd/transitivevisitor.d) | Provide a mixin template with visit methods for the parse time AST |
| [apply.d](https://github.com/dlang/dmd/blob/master/src/dmd/apply.d) | Depth-first expression visitor |
| [sapply.d](https://github.com/dlang/dmd/blob/master/src/dmd/sapply.d) | Depth-first statement visitor |
| [statement_rewrite_walker.d](https://github.com/dlang/dmd/blob/master/src/dmd/statement_rewrite_walker.d) | Statement visitor that allows replacing the currently visited node |
**Semantic passes**
| File | Purpose |
|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| [dsymbolsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d) | Do semantic 1 pass (symbol identifiers/types) |
| [semantic2.d](https://github.com/dlang/dmd/blob/master/src/dmd/semantic2.d) | Do semantic 2 pass (symbol initializers) |
| [semantic3.d](https://github.com/dlang/dmd/blob/master/src/dmd/semantic3.d) | Do semantic 3 pass (function bodies) |
| [inline.d](https://github.com/dlang/dmd/blob/master/src/dmd/inline.d) | Do inline pass (optimization pass that dmd does in the front-end) |
| [inlinecost.d](https://github.com/dlang/dmd/blob/master/src/dmd/inlinecost.d) | Compute the cost of inlining a function call. |
| [expressionsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/expressionsem.d) | Do semantic analysis for expressions |
| [statementsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/statementsem.d) | Do semantic analysis for statements |
| [initsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/initsem.d) | Do semantic analysis for initializers |
| [templateparamsem.d](https://github.com/dlang/dmd/blob/master/src/dmd/templateparamsem.d) | Do semantic analysis for template parameters |
| [typesem.d](https://github.com/dlang/dmd/blob/master/src/dmd/typesem.d) | Do semantic analysis for types |
**Semantic helpers**
| File | Purpose |
|-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| [opover.d](https://github.com/dlang/dmd/blob/master/src/dmd/opover.d) | Operator overloading |
| [clone.d](https://github.com/dlang/dmd/blob/master/src/dmd/dsymbolsem.d) | Generate automatic `opEquals`, `opAssign` and constructors for structs |
| [blockexit.d](https://github.com/dlang/dmd/blob/master/src/dmd/blockexit.d) | Find out in what ways control flow can exit a block |
| [ctorflow.d](https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d) | Control flow in constructors |
| [constfold.d](https://github.com/dlang/dmd/blob/master/src/dmd/constfold.d) | Do constant folding of arithmetic expressions |
| [optimize.d](https://github.com/dlang/dmd/blob/master/src/dmd/optimize.d) | Do constant folding more generally |
| [dcast.d](https://github.com/dlang/dmd/blob/master/src/dmd/dcast.d) | Implicit or explicit cast(), finding common types e.g. in `x ? a : b`, integral promotions |
| [impcnvtab.d](https://github.com/dlang/dmd/blob/master/src/dmd/impcnvtab.d) | Define an implicit conversion table for basic types |
| [sideeffect.d](https://github.com/dlang/dmd/blob/master/src/dmd/sideeffect.d) | Extract side-effects of expressions for certain lowerings. |
**Compile Time Function Execution (CTFE)**
| File | Purpose |
|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| [dinterpret.d](https://github.com/dlang/dmd/blob/master/src/dmd/dinterpret.d) | CTFE entry point |
| [ctfeexpr.d](https://github.com/dlang/dmd/blob/master/src/dmd/ctfeexpr.d) | CTFE for expressions involving pointers, slices, array concatenation etc. |
| [builtin.d](https://github.com/dlang/dmd/blob/master/src/dmd/builtin.d) | Allow CTFE of certain external functions (`core.math`, `std.math` and `core.bitop`) |
### Specific language features
**Attribute checks**
| File | Purpose |
|---------------------------------------------------------------------------|----------------------------------------|
| [nogc.d](https://github.com/dlang/dmd/blob/master/src/dmd/nogc.d) | `@nogc` checks |
| [safe.d](https://github.com/dlang/dmd/blob/master/src/dmd/safe.d) | `@safe` checks |
| [canthrow.d](https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d) | `nothrow` checks |
| [escape.d](https://github.com/dlang/dmd/blob/master/src/dmd/escape.d) | `scope` checks |
| [access.d](https://github.com/dlang/dmd/blob/master/src/dmd/access.d) | `public` / `private` checks |
| [ob.d](https://github.com/dlang/dmd/blob/master/src/dmd/ob.d) | Ownership / borrowing (`@live`) checks |
**Inline Assembly**
| File | Purpose |
|-------------------------------------------------------------------------|-------------------------------------------|
| [iasm.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasm.d) | Inline assembly depending on the compiler |
| [iasmdmd.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasmdmd.d) | Inline assembly for DMD |
| [iasmgcc.d](https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d) | Inline assembly for GDC |
**Other**
| File | Purpose |
|-------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| [aliasthis.d](https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.d) | Resolve implicit conversions for `alias X this` |
| [traits.d](https://github.com/dlang/dmd/blob/master/src/dmd/traits.d) | `__traits()` |
| [lambdacomp.d](https://github.com/dlang/dmd/blob/master/src/dmd/lambdacomp.d) | `__traits(isSame, x => y, z => w)` |
| [cond.d](https://github.com/dlang/dmd/blob/master/src/dmd/cond.d) | Evaluate `static if`, `version` `debug ` |
| [staticcond.d](https://github.com/dlang/dmd/blob/master/src/dmd/staticcond.d) | Lazily evaluate static conditions for `static if`, `static assert` and template constraints |
| [delegatize.d](https://github.com/dlang/dmd/blob/master/src/dmd/delegatize.d) | Converts expression to delegates for `lazy` parameters |
| [eh.d](https://github.com/dlang/dmd/blob/master/src/dmd/eh.d) | Generate tables for exception handling |
| [nspace.d](https://github.com/dlang/dmd/blob/master/src/dmd/nspace.d) | Namespace for `extern (C++, Module)` |
| [intrange.d](https://github.com/dlang/dmd/blob/master/src/dmd/intrange.d) | [Value range propagation](https://digitalmars.com/articles/b62.html) |
| [dimport.d](https://github.com/dlang/dmd/blob/master/src/dmd/dimport.d) | Renamed imports (`import aliasSymbol = pkg1.pkg2.symbol`) |
| [arrayop.d](https://github.com/dlang/dmd/blob/master/src/dmd/arrayop.d) | Array operations (`a[] = b[] + c[]`) |
| [typinf.d](https://github.com/dlang/dmd/blob/master/src/dmd/typinf.d) | Generate typeinfo for `typeid()` (as well as internals) |
| File | Purpose |
|-----------------------------------------------------------------------------|------------------------------------------------------------------------------------|
| [chkformat.d](https://github.com/dlang/dmd/blob/master/src/dmd/chkformat.d) | Validate arguments with format specifiers for `printf` / `scanf` etc. |
| [imphint.d](https://github.com/dlang/dmd/blob/master/src/dmd/imphint.d) | Give a suggestion to e.g. `import std.stdio` when `writeln` could not be resolved. |
### Library files
| File | Purpose |
|-------------------------------------------------------------------------------|------------------------------------------------------|
| [lib.d](https://github.com/dlang/dmd/blob/master/src/dmd/lib.d) | Abstract library class |
| [libelf.d](https://github.com/dlang/dmd/blob/master/src/dmd/libelf.d) | Library in ELF format (Unix) |
| [libmach.d](https://github.com/dlang/dmd/blob/master/src/dmd/libmach.d) | Library in Mach-O format (macOS) |
| [libmscoff.d](https://github.com/dlang/dmd/blob/master/src/dmd/libmscoff.d) | Library in COFF format (32/64-bit Windows) |
| [libomf.d](https://github.com/dlang/dmd/blob/master/src/dmd/libomf.d) | Library in OMF format (legacy 32-bit Windows) |
| [scanelf.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanelf.d) | Extract symbol names from a library in ELF format |
| [scanmach.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanmach.d) | Extract symbol names from a library in Mach-O format |
| [scanmscoff.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanmscoff.d) | Extract symbol names from a library in COFF format |
| [scanomf.d](https://github.com/dlang/dmd/blob/master/src/dmd/scanomf.d) | Extract symbol names from a library in OMF format |
### Code generation / back-end interfacing
| File | Purpose |
|---------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| [dmsc.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmsc.d) | Configures and initializes the back-end |
| [toobj.d](https://github.com/dlang/dmd/blob/master/src/dmd/toobj.d) | Convert an AST that went through all semantic phases into an object file |
| [toir.d](https://github.com/dlang/dmd/blob/master/src/dmd/toir.d) | Convert Dsymbols intermediate representation |
| [e2ir.d](https://github.com/dlang/dmd/blob/master/src/dmd/e2ir.d) | Convert Expressions to intermediate representation |
| [s2ir.d](https://github.com/dlang/dmd/blob/master/src/dmd/s2ir.d) | Convert Statements to intermediate representation |
| [stmtstate.d](https://github.com/dlang/dmd/blob/master/src/dmd/stmtstate.d) | Used to help transform statement AST into flow graph |
| [toctype.d](https://github.com/dlang/dmd/blob/master/src/dmd/toctype.d) | Convert a D type to a type the back-end understands |
| [tocsym.d](https://github.com/dlang/dmd/blob/master/src/dmd/tocsym.d) | Convert a D symbol to a symbol the linker understands (with mangled name) |
| [argtypes_x86.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_x86.d) | Convert a D type into simple (register) types for the 32-bit x86 ABI |
| [argtypes_sysv_x64.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_sysv_x64.d) | 'argtypes' for the x86_64 System V ABI |
| [argtypes_aarch64.d](https://github.com/dlang/dmd/blob/master/src/dmd/argtypes_aarch64.d) | 'argtypes' for the AArch64 ABI |
| [glue.d](https://github.com/dlang/dmd/blob/master/src/dmd/glue.d) | Generate the object file for function declarations |
| [gluelayer.d](https://github.com/dlang/dmd/blob/master/src/dmd/gluelayer.d) | Declarations for back-end functions that the front-end invokes |
| [todt.d](https://github.com/dlang/dmd/blob/master/src/dmd/todt.d) | Convert initializers into structures that the back-end will add to the data segment |
| [tocvdebug.d](https://github.com/dlang/dmd/blob/master/src/dmd/tovcdebug.d) | Generate debug info in the CV4 debug format. |
| [objc.d](https://github.com/dlang/dmd/blob/master/src/dmd/objc.d) | Objective-C interfacing |
| [objc_glue.d](https://github.com/dlang/dmd/blob/master/src/dmd/objc_glue.d) | Glue code for Objective-C interop. |
**Name mangling**
| File | Purpose |
|-----------------------------------------------------------------------------------|------------------------------------------------------------------|
| [cppmangle.d](https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d) | C++ name mangling |
| [cppmanglewin.d](https://github.com/dlang/dmd/blob/master/src/dmd/cppmanglewin.d) | C++ name mangling for Windows |
| [dmangle.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmangle.d) | D [name mangling](https://dlang.org/spec/abi.html#name_mangling) |
### Linking
| File | Purpose |
|-------------------------------------------------------------------|-----------------------------------------|
| [link.d](https://github.com/dlang/dmd/blob/master/src/dmd/link.d) | Invoke the linker as a separate process |
### Special output
| File | Purpose |
|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| [doc.d](https://github.com/dlang/dmd/blob/master/src/dmd/doc.d) | [Documentation generation](https://dlang.org/spec/ddoc.html) |
| [dmacro.d](https://github.com/dlang/dmd/blob/master/src/dmd/dmacro.d) | DDoc macro processing |
| [hdrgen.d](https://github.com/dlang/dmd/blob/master/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/src/dmd/json.d) | Describe the module in a `.json` file for the `-X` flag |
| [dtoh.d](https://github.com/dlang/dmd/blob/master/src/dmd/dtoh.d) | C++ header generation from D source files |
### Utility
Note: many other utilities are in [dmd/root](https://github.com/dlang/dmd/tree/master/src/dmd/root).
| File | Purpose |
|-----------------------------------------------------------------------------|---------------------------------------------------|
| [env.d](https://github.com/dlang/dmd/blob/master/src/dmd/env.d) | Modify environment variables |
| [console.d](https://github.com/dlang/dmd/blob/master/src/dmd/console.d) | Print error messages in color |
| [utf.d](https://github.com/dlang/dmd/blob/master/src/dmd/utf.d) | Encoding/decoding Unicode text |
| [filecache.d](https://github.com/dlang/dmd/blob/master/src/dmd/filecache.d) | Keep file contents in memory |
| [utils.d](https://github.com/dlang/dmd/blob/master/src/dmd/utils.d) | Utility functions related to files and file paths |
| [complex.d](https://github.com/dlang/dmd/blob/master/src/dmd/complex.d) | A complex number type |
| File | Purpose |
|---------------------------------------------------------------------------------|---------------------------------------------------------------|
| [asttypename.d](https://github.com/dlang/dmd/blob/master/src/dmd/asttypename.d) | Print the internal name of an AST node (for debugging only) |
| [printast.d](https://github.com/dlang/dmd/blob/master/src/dmd/printast.d) | Print the AST data structure |
| [foreachvar.d](https://github.com/dlang/dmd/blob/master/src/dmd/foreachvar.d) | Used in `ob.d` to iterate over all variables in an expression |

1
gcc/d/dmd/VERSION Normal file
View file

@ -0,0 +1 @@
v2.097.2

View file

@ -1,560 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/access.c
*/
#include "root/dsystem.h"
#include "root/root.h"
#include "root/rmem.h"
#include "errors.h"
#include "enum.h"
#include "aggregate.h"
#include "init.h"
#include "attrib.h"
#include "scope.h"
#include "id.h"
#include "mtype.h"
#include "declaration.h"
#include "aggregate.h"
#include "expression.h"
#include "module.h"
#include "template.h"
/* Code to do access checks
*/
bool hasPackageAccess(Scope *sc, Dsymbol *s);
bool hasPackageAccess(Module *mod, Dsymbol *s);
bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember);
bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd);
static Dsymbol *mostVisibleOverload(Dsymbol *s);
/****************************************
* Return Prot access for Dsymbol smember in this declaration.
*/
Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember)
{
Prot access_ret = Prot(Prot::none);
assert(ad->isStructDeclaration() || ad->isClassDeclaration());
if (smember->toParent() == ad)
{
access_ret = smember->prot();
}
else if (smember->isDeclaration()->isStatic())
{
access_ret = smember->prot();
}
if (ClassDeclaration *cd = ad->isClassDeclaration())
{
for (size_t i = 0; i < cd->baseclasses->length; i++)
{
BaseClass *b = (*cd->baseclasses)[i];
Prot access = getAccess(b->sym, smember);
switch (access.kind)
{
case Prot::none:
break;
case Prot::private_:
access_ret = Prot(Prot::none); // private members of base class not accessible
break;
case Prot::package_:
case Prot::protected_:
case Prot::public_:
case Prot::export_:
// If access is to be tightened
if (Prot::public_ < access.kind)
access = Prot(Prot::public_);
// Pick path with loosest access
if (access_ret.isMoreRestrictiveThan(access))
access_ret = access;
break;
default:
assert(0);
}
}
}
return access_ret;
}
/********************************************************
* Helper function for checkAccess()
* Returns:
* false is not accessible
* true is accessible
*/
static bool isAccessible(
Dsymbol *smember,
Dsymbol *sfunc,
AggregateDeclaration *dthis,
AggregateDeclaration *cdscope)
{
assert(dthis);
if (hasPrivateAccess(dthis, sfunc) ||
isFriendOf(dthis, cdscope))
{
if (smember->toParent() == dthis)
return true;
if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
{
for (size_t i = 0; i < cdthis->baseclasses->length; i++)
{
BaseClass *b = (*cdthis->baseclasses)[i];
Prot access = getAccess(b->sym, smember);
if (access.kind >= Prot::protected_ ||
isAccessible(smember, sfunc, b->sym, cdscope))
{
return true;
}
}
}
}
else
{
if (smember->toParent() != dthis)
{
if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
{
for (size_t i = 0; i < cdthis->baseclasses->length; i++)
{
BaseClass *b = (*cdthis->baseclasses)[i];
if (isAccessible(smember, sfunc, b->sym, cdscope))
return true;
}
}
}
}
return false;
}
/*******************************
* Do access check for member of this class, this class being the
* type of the 'this' pointer used to access smember.
* Returns true if the member is not accessible.
*/
bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember)
{
FuncDeclaration *f = sc->func;
AggregateDeclaration *cdscope = sc->getStructClassScope();
Dsymbol *smemberparent = smember->toParent();
if (!smemberparent || !smemberparent->isAggregateDeclaration())
{
return false; // then it is accessible
}
// BUG: should enable this check
//assert(smember->parent->isBaseOf(this, NULL));
bool result;
Prot access;
if (smemberparent == ad)
{
access = smember->prot();
result = access.kind >= Prot::public_ ||
hasPrivateAccess(ad, f) ||
isFriendOf(ad, cdscope) ||
(access.kind == Prot::package_ && hasPackageAccess(sc, smember)) ||
ad->getAccessModule() == sc->_module;
}
else if ((access = getAccess(ad, smember)).kind >= Prot::public_)
{
result = true;
}
else if (access.kind == Prot::package_ && hasPackageAccess(sc, ad))
{
result = true;
}
else
{
result = isAccessible(smember, f, ad, cdscope);
}
if (!result)
{
ad->error(loc, "member %s is not accessible", smember->toChars());
//printf("smember = %s %s, prot = %d, semanticRun = %d\n",
// smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun);
return true;
}
return false;
}
/****************************************
* Determine if this is the same or friend of cd.
*/
bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd)
{
if (ad == cd)
return true;
// Friends if both are in the same module
//if (toParent() == cd->toParent())
if (cd && ad->getAccessModule() == cd->getAccessModule())
{
return true;
}
return false;
}
/****************************************
* Determine if scope sc has package level access to s.
*/
bool hasPackageAccess(Scope *sc, Dsymbol *s)
{
return hasPackageAccess(sc->_module, s);
}
bool hasPackageAccess(Module *mod, Dsymbol *s)
{
Package *pkg = NULL;
if (s->prot().pkg)
pkg = s->prot().pkg;
else
{
// no explicit package for protection, inferring most qualified one
for (; s; s = s->parent)
{
if (Module *m = s->isModule())
{
DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL);
assert(dst);
Dsymbol *s2 = dst->lookup(m->ident);
assert(s2);
Package *p = s2->isPackage();
if (p && p->isPackageMod())
{
pkg = p;
break;
}
}
else if ((pkg = s->isPackage()) != NULL)
break;
}
}
if (pkg)
{
if (pkg == mod->parent)
{
return true;
}
if (pkg->isPackageMod() == mod)
{
return true;
}
Dsymbol* ancestor = mod->parent;
for (; ancestor; ancestor = ancestor->parent)
{
if (ancestor == pkg)
{
return true;
}
}
}
return false;
}
/****************************************
* Determine if scope sc has protected level access to cd.
*/
bool hasProtectedAccess(Scope *sc, Dsymbol *s)
{
if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces
{
for (Scope *scx = sc; scx; scx = scx->enclosing)
{
if (!scx->scopesym)
continue;
ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration();
if (cd2 && cd->isBaseOf(cd2, NULL))
return true;
}
}
return sc->_module == s->getAccessModule();
}
/**********************************
* Determine if smember has access to private members of this declaration.
*/
bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember)
{
if (smember)
{
AggregateDeclaration *cd = NULL;
Dsymbol *smemberparent = smember->toParent();
if (smemberparent)
cd = smemberparent->isAggregateDeclaration();
if (ad == cd) // smember is a member of this class
{
return true; // so we get private access
}
// If both are members of the same module, grant access
while (1)
{
Dsymbol *sp = smember->toParent();
if (sp->isFuncDeclaration() && smember->isFuncDeclaration())
smember = sp;
else
break;
}
if (!cd && ad->toParent() == smember->toParent())
{
return true;
}
if (!cd && ad->getAccessModule() == smember->getAccessModule())
{
return true;
}
}
return false;
}
/****************************************
* Check access to d for expression e.d
* Returns true if the declaration is not accessible.
*/
bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d)
{
if (sc->flags & SCOPEnoaccesscheck)
return false;
if (d->isUnitTestDeclaration())
{
// Unittests are always accessible.
return false;
}
if (!e)
return false;
if (e->type->ty == Tclass)
{
// Do access check
ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym);
if (e->op == TOKsuper)
{
ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration();
if (cd2)
cd = cd2;
}
return checkAccess(cd, loc, sc, d);
}
else if (e->type->ty == Tstruct)
{
// Do access check
StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym);
return checkAccess(cd, loc, sc, d);
}
return false;
}
/****************************************
* Check access to package/module `p` from scope `sc`.
*
* Params:
* loc = source location for issued error message
* sc = scope from which to access to a fully qualified package name
* p = the package/module to check access for
* Returns: true if the package is not accessible.
*
* Because a global symbol table tree is used for imported packages/modules,
* access to them needs to be checked based on the imports in the scope chain
* (see Bugzilla 313).
*
*/
bool checkAccess(Scope *sc, Package *p)
{
if (sc->_module == p)
return false;
for (; sc; sc = sc->enclosing)
{
if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(Prot::private_)))
return false;
}
return true;
}
/**
* Check whether symbols `s` is visible in `mod`.
*
* Params:
* mod = lookup origin
* s = symbol to check for visibility
* Returns: true if s is visible in mod
*/
bool symbolIsVisible(Module *mod, Dsymbol *s)
{
// should sort overloads by ascending protection instead of iterating here
s = mostVisibleOverload(s);
switch (s->prot().kind)
{
case Prot::undefined:
return true;
case Prot::none:
return false; // no access
case Prot::private_:
return s->getAccessModule() == mod;
case Prot::package_:
return s->getAccessModule() == mod || hasPackageAccess(mod, s);
case Prot::protected_:
return s->getAccessModule() == mod;
case Prot::public_:
case Prot::export_:
return true;
default:
assert(0);
}
return false;
}
/**
* Same as above, but determines the lookup module from symbols `origin`.
*/
bool symbolIsVisible(Dsymbol *origin, Dsymbol *s)
{
return symbolIsVisible(origin->getAccessModule(), s);
}
/**
* Same as above but also checks for protected symbols visible from scope `sc`.
* Used for qualified name lookup.
*
* Params:
* sc = lookup scope
* s = symbol to check for visibility
* Returns: true if s is visible by origin
*/
bool symbolIsVisible(Scope *sc, Dsymbol *s)
{
s = mostVisibleOverload(s);
switch (s->prot().kind)
{
case Prot::undefined:
return true;
case Prot::none:
return false; // no access
case Prot::private_:
return sc->_module == s->getAccessModule();
case Prot::package_:
return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s);
case Prot::protected_:
return hasProtectedAccess(sc, s);
case Prot::public_:
case Prot::export_:
return true;
default:
assert(0);
}
return false;
}
/**
* Use the most visible overload to check visibility. Later perform an access
* check on the resolved overload. This function is similar to overloadApply,
* but doesn't recurse nor resolve aliases because protection/visibility is an
* attribute of the alias not the aliasee.
*/
static Dsymbol *mostVisibleOverload(Dsymbol *s)
{
if (!s->isOverloadable())
return s;
Dsymbol *next = NULL;
Dsymbol *fstart = s;
Dsymbol *mostVisible = s;
for (; s; s = next)
{
// void func() {}
// private void func(int) {}
if (FuncDeclaration *fd = s->isFuncDeclaration())
next = fd->overnext;
// template temp(T) {}
// private template temp(T:int) {}
else if (TemplateDeclaration *td = s->isTemplateDeclaration())
next = td->overnext;
// alias common = mod1.func1;
// alias common = mod2.func2;
else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration())
next = fa->overnext;
// alias common = mod1.templ1;
// alias common = mod2.templ2;
else if (OverDeclaration *od = s->isOverDeclaration())
next = od->overnext;
// alias name = sym;
// private void name(int) {}
else if (AliasDeclaration *ad = s->isAliasDeclaration())
{
if (!ad->isOverloadable())
{
//printf("Non overloadable Aliasee in overload list\n");
assert(0);
}
// Yet unresolved aliases store overloads in overnext.
if (ad->semanticRun < PASSsemanticdone)
next = ad->overnext;
else
{
/* This is a bit messy due to the complicated implementation of
* alias. Aliases aren't overloadable themselves, but if their
* Aliasee is overloadable they can be converted to an overloadable
* alias.
*
* This is done by replacing the Aliasee w/ FuncAliasDeclaration
* (for functions) or OverDeclaration (for templates) which are
* simply overloadable aliases w/ weird names.
*
* Usually aliases should not be resolved for visibility checking
* b/c public aliases to private symbols are public. But for the
* overloadable alias situation, the Alias (_ad_) has been moved
* into it's own Aliasee, leaving a shell that we peel away here.
*/
Dsymbol *aliasee = ad->toAlias();
if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration())
next = aliasee;
else
{
/* A simple alias can be at the end of a function or template overload chain.
* It can't have further overloads b/c it would have been
* converted to an overloadable alias.
*/
if (ad->overnext)
{
//printf("Unresolved overload of alias\n");
assert(0);
}
break;
}
}
// handled by overloadApply for unknown reason
assert(next != ad); // should not alias itself
assert(next != fstart); // should not alias the overload list itself
}
else
break;
if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot()))
mostVisible = next;
}
return mostVisible;
}

410
gcc/d/dmd/access.d Normal file
View file

@ -0,0 +1,410 @@
/**
* Enforce visibility contrains such as `public` and `private`.
*
* Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes)
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.access;
import dmd.aggregate;
import dmd.astenums;
import dmd.dclass;
import dmd.declaration;
import dmd.dmodule;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.errors;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.mtype;
import dmd.tokens;
private enum LOG = false;
/*******************************
* Do access check for member of this class, this class being the
* type of the 'this' pointer used to access smember.
* Returns true if the member is not accessible.
*/
bool checkAccess(AggregateDeclaration ad, Loc loc, Scope* sc, Dsymbol smember)
{
static if (LOG)
{
printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", ad.toChars(), smember.toChars(), f ? f.toChars() : null, cdscope ? cdscope.toChars() : null);
}
const p = smember.toParent();
if (p && p.isTemplateInstance())
{
return false; // for backward compatibility
}
if (!symbolIsVisible(sc, smember))
{
// when in @safe code or with -preview=dip1000
if (sc.flags & SCOPE.onlysafeaccess)
{
// if there is a func. ask for it's opinion of safety, and if it considers the access @safe accept it.
if (sc.func && !sc.func.setUnsafe())
return false;
}
ad.error(loc, "%s `%s` is not accessible%s", smember.kind(), smember.toChars(), (sc.flags & SCOPE.onlysafeaccess) ? " from `@safe` code".ptr : "".ptr);
//printf("smember = %s %s, vis = %d, semanticRun = %d\n",
// smember.kind(), smember.toPrettyChars(), smember.visible() smember.semanticRun);
return true;
}
return false;
}
/****************************************
* Determine if scope sc has package level access to s.
*/
private bool hasPackageAccess(Scope* sc, Dsymbol s)
{
return hasPackageAccess(sc._module, s);
}
private bool hasPackageAccess(Module mod, Dsymbol s)
{
static if (LOG)
{
printf("hasPackageAccess(s = '%s', mod = '%s', s.visibility.pkg = '%s')\n", s.toChars(), mod.toChars(), s.visible().pkg ? s.visible().pkg.toChars() : "NULL");
}
Package pkg = null;
if (s.visible().pkg)
pkg = s.visible().pkg;
else
{
// no explicit package for visibility, inferring most qualified one
for (; s; s = s.parent)
{
if (auto m = s.isModule())
{
DsymbolTable dst = Package.resolve(m.md ? m.md.packages : null, null, null);
assert(dst);
Dsymbol s2 = dst.lookup(m.ident);
assert(s2);
Package p = s2.isPackage();
if (p && p.isPackageMod())
{
pkg = p;
break;
}
}
else if ((pkg = s.isPackage()) !is null)
break;
}
}
static if (LOG)
{
if (pkg)
printf("\tsymbol access binds to package '%s'\n", pkg.toChars());
}
if (pkg)
{
if (pkg == mod.parent)
{
static if (LOG)
{
printf("\tsc is in permitted package for s\n");
}
return true;
}
if (pkg.isPackageMod() == mod)
{
static if (LOG)
{
printf("\ts is in same package.d module as sc\n");
}
return true;
}
Dsymbol ancestor = mod.parent;
for (; ancestor; ancestor = ancestor.parent)
{
if (ancestor == pkg)
{
static if (LOG)
{
printf("\tsc is in permitted ancestor package for s\n");
}
return true;
}
}
}
static if (LOG)
{
printf("\tno package access\n");
}
return false;
}
/****************************************
* Determine if scope sc has protected level access to cd.
*/
private bool hasProtectedAccess(Scope *sc, Dsymbol s)
{
if (auto cd = s.isClassMember()) // also includes interfaces
{
for (auto scx = sc; scx; scx = scx.enclosing)
{
if (!scx.scopesym)
continue;
auto cd2 = scx.scopesym.isClassDeclaration();
if (cd2 && cd.isBaseOf(cd2, null))
return true;
}
}
return sc._module == s.getAccessModule();
}
/****************************************
* Check access to d for expression e.d
* Returns true if the declaration is not accessible.
*/
bool checkAccess(Loc loc, Scope* sc, Expression e, Dsymbol d)
{
if (sc.flags & SCOPE.noaccesscheck)
return false;
static if (LOG)
{
if (e)
{
printf("checkAccess(%s . %s)\n", e.toChars(), d.toChars());
printf("\te.type = %s\n", e.type.toChars());
}
else
{
printf("checkAccess(%s)\n", d.toPrettyChars());
}
}
if (d.isUnitTestDeclaration())
{
// Unittests are always accessible.
return false;
}
if (!e)
return false;
if (auto tc = e.type.isTypeClass())
{
// Do access check
ClassDeclaration cd = tc.sym;
if (e.op == TOK.super_)
{
if (ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration())
cd = cd2;
}
return checkAccess(cd, loc, sc, d);
}
else if (auto ts = e.type.isTypeStruct())
{
// Do access check
StructDeclaration cd = ts.sym;
return checkAccess(cd, loc, sc, d);
}
return false;
}
/****************************************
* Check access to package/module `p` from scope `sc`.
*
* Params:
* sc = scope from which to access to a fully qualified package name
* p = the package/module to check access for
* Returns: true if the package is not accessible.
*
* Because a global symbol table tree is used for imported packages/modules,
* access to them needs to be checked based on the imports in the scope chain
* (see https://issues.dlang.org/show_bug.cgi?id=313).
*
*/
bool checkAccess(Scope* sc, Package p)
{
if (sc._module == p)
return false;
for (; sc; sc = sc.enclosing)
{
if (sc.scopesym && sc.scopesym.isPackageAccessible(p, Visibility(Visibility.Kind.private_)))
return false;
}
return true;
}
/**
* Check whether symbols `s` is visible in `mod`.
*
* Params:
* mod = lookup origin
* s = symbol to check for visibility
* Returns: true if s is visible in mod
*/
bool symbolIsVisible(Module mod, Dsymbol s)
{
// should sort overloads by ascending visibility instead of iterating here
s = mostVisibleOverload(s);
final switch (s.visible().kind)
{
case Visibility.Kind.undefined: return true;
case Visibility.Kind.none: return false; // no access
case Visibility.Kind.private_: return s.getAccessModule() == mod;
case Visibility.Kind.package_: return s.getAccessModule() == mod || hasPackageAccess(mod, s);
case Visibility.Kind.protected_: return s.getAccessModule() == mod;
case Visibility.Kind.public_, Visibility.Kind.export_: return true;
}
}
/**
* Same as above, but determines the lookup module from symbols `origin`.
*/
bool symbolIsVisible(Dsymbol origin, Dsymbol s)
{
return symbolIsVisible(origin.getAccessModule(), s);
}
/**
* Same as above but also checks for protected symbols visible from scope `sc`.
* Used for qualified name lookup.
*
* Params:
* sc = lookup scope
* s = symbol to check for visibility
* Returns: true if s is visible by origin
*/
bool symbolIsVisible(Scope *sc, Dsymbol s)
{
s = mostVisibleOverload(s);
return checkSymbolAccess(sc, s);
}
/**
* Check if a symbol is visible from a given scope without taking
* into account the most visible overload.
*
* Params:
* sc = lookup scope
* s = symbol to check for visibility
* Returns: true if s is visible by origin
*/
bool checkSymbolAccess(Scope *sc, Dsymbol s)
{
final switch (s.visible().kind)
{
case Visibility.Kind.undefined: return true;
case Visibility.Kind.none: return false; // no access
case Visibility.Kind.private_: return sc._module == s.getAccessModule();
case Visibility.Kind.package_: return sc._module == s.getAccessModule() || hasPackageAccess(sc._module, s);
case Visibility.Kind.protected_: return hasProtectedAccess(sc, s);
case Visibility.Kind.public_, Visibility.Kind.export_: return true;
}
}
/**
* Use the most visible overload to check visibility. Later perform an access
* check on the resolved overload. This function is similar to overloadApply,
* but doesn't recurse nor resolve aliases because visibility is an
* attribute of the alias not the aliasee.
*/
public Dsymbol mostVisibleOverload(Dsymbol s, Module mod = null)
{
if (!s.isOverloadable())
return s;
Dsymbol next, fstart = s, mostVisible = s;
for (; s; s = next)
{
// void func() {}
// private void func(int) {}
if (auto fd = s.isFuncDeclaration())
next = fd.overnext;
// template temp(T) {}
// private template temp(T:int) {}
else if (auto td = s.isTemplateDeclaration())
next = td.overnext;
// alias common = mod1.func1;
// alias common = mod2.func2;
else if (auto fa = s.isFuncAliasDeclaration())
next = fa.overnext;
// alias common = mod1.templ1;
// alias common = mod2.templ2;
else if (auto od = s.isOverDeclaration())
next = od.overnext;
// alias name = sym;
// private void name(int) {}
else if (auto ad = s.isAliasDeclaration())
{
assert(ad.isOverloadable || ad.type && ad.type.ty == Terror,
"Non overloadable Aliasee in overload list");
// Yet unresolved aliases store overloads in overnext.
if (ad.semanticRun < PASS.semanticdone)
next = ad.overnext;
else
{
/* This is a bit messy due to the complicated implementation of
* alias. Aliases aren't overloadable themselves, but if their
* Aliasee is overloadable they can be converted to an overloadable
* alias.
*
* This is done by replacing the Aliasee w/ FuncAliasDeclaration
* (for functions) or OverDeclaration (for templates) which are
* simply overloadable aliases w/ weird names.
*
* Usually aliases should not be resolved for visibility checking
* b/c public aliases to private symbols are public. But for the
* overloadable alias situation, the Alias (_ad_) has been moved
* into its own Aliasee, leaving a shell that we peel away here.
*/
auto aliasee = ad.toAlias();
if (aliasee.isFuncAliasDeclaration || aliasee.isOverDeclaration)
next = aliasee;
else
{
/* A simple alias can be at the end of a function or template overload chain.
* It can't have further overloads b/c it would have been
* converted to an overloadable alias.
*/
assert(ad.overnext is null, "Unresolved overload of alias");
break;
}
}
// handled by dmd.func.overloadApply for unknown reason
assert(next !is ad); // should not alias itself
assert(next !is fstart); // should not alias the overload list itself
}
else
break;
/**
* Return the "effective" visibility attribute of a symbol when accessed in a module.
* The effective visibility attribute is the same as the regular visibility attribute,
* except package() is "private" if the module is outside the package;
* otherwise, "public".
*/
static Visibility visibilitySeenFromModule(Dsymbol d, Module mod = null)
{
Visibility vis = d.visible();
if (mod && vis.kind == Visibility.Kind.package_)
{
return hasPackageAccess(mod, d) ? Visibility(Visibility.Kind.public_) : Visibility(Visibility.Kind.private_);
}
return vis;
}
if (next &&
visibilitySeenFromModule(mostVisible, mod) < visibilitySeenFromModule(next, mod))
mostVisible = next;
}
return mostVisible;
}

769
gcc/d/dmd/aggregate.d Normal file
View file

@ -0,0 +1,769 @@
/**
* Defines a `Dsymbol` representing an aggregate, which is a `struct`, `union` or `class`.
*
* Specification: $(LINK2 https://dlang.org/spec/struct.html, Structs, Unions),
* $(LINK2 https://dlang.org/spec/class.html, Class).
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.aggregate;
import core.stdc.stdio;
import core.checkedint;
import dmd.aliasthis;
import dmd.apply;
import dmd.arraytypes;
import dmd.astenums;
import dmd.declaration;
import dmd.dscope;
import dmd.dstruct;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.errors;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.tokens;
import dmd.typesem : defaultInit;
import dmd.visitor;
/**
* The ClassKind enum is used in AggregateDeclaration AST nodes to
* specify the linkage type of the struct/class/interface or if it
* is an anonymous class. If the class is anonymous it is also
* considered to be a D class.
*/
enum ClassKind : ubyte
{
/// the aggregate is a d(efault) class
d,
/// the aggregate is a C++ struct/class/interface
cpp,
/// the aggregate is an Objective-C class/interface
objc,
/// the aggregate is a C struct
c,
}
/**
* If an aggregate has a pargma(mangle, ...) this holds the information
* to mangle.
*/
struct MangleOverride
{
Dsymbol agg; // The symbol to copy template parameters from (if any)
Identifier id; // the name to override the aggregate's with, defaults to agg.ident
}
/***********************************************************
* Abstract aggregate as a common ancestor for Class- and StructDeclaration.
*/
extern (C++) abstract class AggregateDeclaration : ScopeDsymbol
{
Type type; ///
StorageClass storage_class; ///
uint structsize; /// size of struct
uint alignsize; /// size of struct for alignment purposes
VarDeclarations fields; /// VarDeclaration fields
Dsymbol deferred; /// any deferred semantic2() or semantic3() symbol
/// specifies whether this is a D, C++, Objective-C or anonymous struct/class/interface
ClassKind classKind;
/// Specify whether to mangle the aggregate as a `class` or a `struct`
/// This information is used by the MSVC mangler
/// Only valid for class and struct. TODO: Merge with ClassKind ?
CPPMANGLE cppmangle;
/// overridden symbol with pragma(mangle, "...") if not null
MangleOverride* mangleOverride;
/**
* !=null if is nested
* pointing to the dsymbol that directly enclosing it.
* 1. The function that enclosing it (nested struct and class)
* 2. The class that enclosing it (nested class only)
* 3. If enclosing aggregate is template, its enclosing dsymbol.
*
* See AggregateDeclaraton::makeNested for the details.
*/
Dsymbol enclosing;
VarDeclaration vthis; /// 'this' parameter if this aggregate is nested
VarDeclaration vthis2; /// 'this' parameter if this aggregate is a template and is nested
// Special member functions
FuncDeclarations invs; /// Array of invariants
FuncDeclaration inv; /// Merged invariant calling all members of invs
/// CtorDeclaration or TemplateDeclaration
Dsymbol ctor;
/// default constructor - should have no arguments, because
/// it would be stored in TypeInfo_Class.defaultConstructor
CtorDeclaration defaultCtor;
AliasThis aliasthis; /// forward unresolved lookups to aliasthis
DtorDeclarations dtors; /// Array of destructors
DtorDeclaration dtor; /// aggregate destructor calling dtors and member constructors
DtorDeclaration primaryDtor;/// non-deleting C++ destructor, same as dtor for D
DtorDeclaration tidtor; /// aggregate destructor used in TypeInfo (must have extern(D) ABI)
FuncDeclaration fieldDtor; /// aggregate destructor for just the fields
Expression getRTInfo; /// pointer to GC info generated by object.RTInfo(this)
///
Visibility visibility;
bool noDefaultCtor; /// no default construction
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)
{
super(loc, id);
visibility = Visibility(Visibility.Kind.public_);
}
/***************************************
* Create a new scope from sc.
* semantic, semantic2 and semantic3 will use this for aggregate members.
*/
Scope* newScope(Scope* sc)
{
auto sc2 = sc.push(this);
sc2.stc &= STC.flowThruAggregate;
sc2.parent = this;
sc2.inunion = isUnionDeclaration();
sc2.visibility = Visibility(Visibility.Kind.public_);
sc2.explicitVisibility = 0;
sc2.aligndecl = null;
sc2.userAttribDecl = null;
sc2.namespace = null;
return sc2;
}
override final void setScope(Scope* sc)
{
// Might need a scope to resolve forward references. The check for
// semanticRun prevents unnecessary setting of _scope during deferred
// setScope phases for aggregates which already finished semantic().
// See https://issues.dlang.org/show_bug.cgi?id=16607
if (semanticRun < PASS.semanticdone)
ScopeDsymbol.setScope(sc);
}
/***************************************
* Returns:
* The total number of fields minus the number of hidden fields.
*/
final size_t nonHiddenFields()
{
return fields.dim - isNested() - (vthis2 !is null);
}
/***************************************
* Collect all instance fields, then determine instance size.
* Returns:
* false if failed to determine the size.
*/
final bool determineSize(Loc loc)
{
//printf("AggregateDeclaration::determineSize() %s, sizeok = %d\n", toChars(), sizeok);
// The previous instance size finalizing had:
if (type.ty == Terror)
return false; // failed already
if (sizeok == Sizeok.done)
return true; // succeeded
if (!members)
{
error(loc, "unknown size");
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, "no size because of forward reference");
// 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 d_uns64 size(const ref Loc loc)
{
//printf("+AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
bool ok = determineSize(loc);
//printf("-AggregateDeclaration::size() %s, scope = %p, sizeok = %d\n", toChars(), _scope, sizeok);
return ok ? structsize : SIZE_INVALID;
}
/***************************************
* Calculate field[i].overlapped and overlapUnsafe, and check that all of explicit
* field initializers have unique memory space on instance.
* Returns:
* true if any errors happen.
*/
extern (D) final bool checkOverlappedFields()
{
//printf("AggregateDeclaration::checkOverlappedFields() %s\n", toChars());
assert(sizeok == Sizeok.done);
size_t nfields = fields.dim;
if (isNested())
{
auto cd = isClassDeclaration();
if (!cd || !cd.baseClass || !cd.baseClass.isNested())
nfields--;
if (vthis2 && !(cd && cd.baseClass && cd.baseClass.vthis2))
nfields--;
}
bool errors = false;
// Fill in missing any elements with default initializers
foreach (i; 0 .. nfields)
{
auto vd = fields[i];
if (vd.errors)
{
errors = true;
continue;
}
const vdIsVoidInit = vd._init && vd._init.isVoidInitializer();
// Find overlapped fields with the hole [vd.offset .. vd.offset.size()].
foreach (j; 0 .. nfields)
{
if (i == j)
continue;
auto v2 = fields[j];
if (v2.errors)
{
errors = true;
continue;
}
if (!vd.isOverlappedWith(v2))
continue;
// vd and v2 are overlapping.
vd.overlapped = true;
v2.overlapped = true;
if (!MODimplicitConv(vd.type.mod, v2.type.mod))
v2.overlapUnsafe = true;
if (!MODimplicitConv(v2.type.mod, vd.type.mod))
vd.overlapUnsafe = true;
if (i > j)
continue;
if (!v2._init)
continue;
if (v2._init.isVoidInitializer())
continue;
if (vd._init && !vdIsVoidInit && v2._init)
{
.error(loc, "overlapping default initialization for field `%s` and `%s`", v2.toChars(), vd.toChars());
errors = true;
}
else if (v2._init && i < j)
{
.error(v2.loc, "union field `%s` with default initialization `%s` must be before field `%s`",
v2.toChars(), v2._init.toChars(), vd.toChars());
errors = true;
}
}
}
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(Loc loc, Expressions* elements, bool ctorinit)
{
//printf("AggregateDeclaration::fill() %s\n", toChars());
assert(sizeok == Sizeok.done);
assert(elements);
const nfields = nonHiddenFields();
bool errors = false;
size_t dim = elements.dim;
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
{
vx.error(loc, "recursive initialization of field");
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 == TOK.error)
return false;
}
return !errors;
}
/****************************
* Do byte or word alignment as necessary.
* Align sizes of 0, as we may not know array sizes yet.
* Params:
* alignment = struct alignment that is in effect
* size = alignment requirement of field
* poffset = pointer to offset to be aligned
*/
extern (D) static void alignmember(structalign_t alignment, uint size, uint* poffset) pure nothrow @safe
{
//printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset);
switch (alignment)
{
case cast(structalign_t)1:
// No alignment
break;
case cast(structalign_t)STRUCTALIGN_DEFAULT:
// Alignment in Target::fieldalignsize must match what the
// corresponding C compiler's default alignment behavior is.
assert(size > 0 && !(size & (size - 1)));
*poffset = (*poffset + size - 1) & ~(size - 1);
break;
default:
// Align on alignment boundary, which must be a positive power of 2
assert(alignment > 0 && !(alignment & (alignment - 1)));
*poffset = (*poffset + alignment - 1) & ~(alignment - 1);
break;
}
}
/****************************************
* Place a member (mem) into an aggregate (agg), which can be a struct, union or class
* Returns:
* offset to place field at
*
* nextoffset: next location in aggregate
* memsize: size of member
* memalignsize: natural alignment of member
* alignment: alignment in effect for this member
* paggsize: size of aggregate (updated)
* paggalignsize: alignment of aggregate (updated)
* isunion: the aggregate is a union
*/
extern (D) static uint placeField(uint* nextoffset, uint memsize, uint memalignsize,
structalign_t alignment, uint* paggsize, uint* paggalignsize, bool isunion)
{
uint ofs = *nextoffset;
const uint actualAlignment =
alignment == STRUCTALIGN_DEFAULT ? memalignsize : alignment;
// Ensure no overflow
bool overflow;
const sz = addu(memsize, actualAlignment, overflow);
addu(ofs, sz, overflow);
if (overflow) assert(0);
alignmember(alignment, memalignsize, &ofs);
uint memoffset = ofs;
ofs += memsize;
if (ofs > *paggsize)
*paggsize = ofs;
if (!isunion)
*nextoffset = ofs;
if (*paggalignsize < actualAlignment)
*paggalignsize = actualAlignment;
return memoffset;
}
override final Type getType()
{
return type;
}
// is aggregate deprecated?
override final bool isDeprecated() const
{
return !!(this.storage_class & STC.deprecated_);
}
/// Flag this aggregate as deprecated
final void setDeprecated()
{
this.storage_class |= STC.deprecated_;
}
/****************************************
* Returns true if there's an extra member which is the 'this'
* pointer to the enclosing context (enclosing aggregate or function)
*/
final bool isNested() const
{
return enclosing !is null;
}
/* Append vthis field (this.tupleof[$-1]) to make this aggregate type nested.
*/
extern (D) final void makeNested()
{
if (enclosing) // if already nested
return;
if (sizeok == Sizeok.done)
return;
if (isUnionDeclaration() || isInterfaceDeclaration())
return;
if (storage_class & STC.static_)
return;
// If nested struct, add in hidden 'this' pointer to outer scope
auto s = toParentLocal();
if (!s)
s = toParent2();
if (!s)
return;
Type t = null;
if (auto fd = s.isFuncDeclaration())
{
enclosing = fd;
/* https://issues.dlang.org/show_bug.cgi?id=14422
* If a nested class parent is a function, its
* context pointer (== `outer`) should be void* always.
*/
t = Type.tvoidptr;
}
else if (auto ad = s.isAggregateDeclaration())
{
if (isClassDeclaration() && ad.isClassDeclaration())
{
enclosing = ad;
}
else if (isStructDeclaration())
{
if (auto ti = ad.parent.isTemplateInstance())
{
enclosing = ti.enclosing;
}
}
t = ad.handleType();
}
if (enclosing)
{
//printf("makeNested %s, enclosing = %s\n", toChars(), enclosing.toChars());
assert(t);
if (t.ty == Tstruct)
t = Type.tvoidptr; // t should not be a ref type
assert(!vthis);
vthis = new ThisDeclaration(loc, t);
//vthis.storage_class |= STC.ref_;
// Emulate vthis.addMember()
members.push(vthis);
// Emulate vthis.dsymbolSemantic()
vthis.storage_class |= STC.field;
vthis.parent = this;
vthis.visibility = Visibility(Visibility.Kind.public_);
vthis.alignment = t.alignment();
vthis.semanticRun = PASS.semanticdone;
if (sizeok == Sizeok.fwd)
fields.push(vthis);
makeNested2();
}
}
/* Append vthis2 field (this.tupleof[$-1]) to add a second context pointer.
*/
extern (D) final void makeNested2()
{
if (vthis2)
return;
if (!vthis)
makeNested(); // can't add second before first
if (!vthis)
return;
if (sizeok == Sizeok.done)
return;
if (isUnionDeclaration() || isInterfaceDeclaration())
return;
if (storage_class & STC.static_)
return;
auto s0 = toParentLocal();
auto s = toParent2();
if (!s || !s0 || s == s0)
return;
auto cd = s.isClassDeclaration();
Type t = cd ? cd.type : Type.tvoidptr;
vthis2 = new ThisDeclaration(loc, t);
//vthis2.storage_class |= STC.ref_;
// Emulate vthis2.addMember()
members.push(vthis2);
// Emulate vthis2.dsymbolSemantic()
vthis2.storage_class |= STC.field;
vthis2.parent = this;
vthis2.visibility = Visibility(Visibility.Kind.public_);
vthis2.alignment = t.alignment();
vthis2.semanticRun = PASS.semanticdone;
if (sizeok == Sizeok.fwd)
fields.push(vthis2);
}
override final bool isExport() const
{
return visibility.kind == Visibility.Kind.export_;
}
/*******************************************
* Look for constructor declaration.
*/
final Dsymbol searchCtor()
{
auto s = search(Loc.initial, Id.ctor);
if (s)
{
if (!(s.isCtorDeclaration() ||
s.isTemplateDeclaration() ||
s.isOverloadSet()))
{
s.error("is not a constructor; identifiers starting with `__` are reserved for the implementation");
errors = true;
s = null;
}
}
if (s && s.toParent() != this)
s = null; // search() looks through ancestor classes
if (s)
{
// Finish all constructors semantics to determine this.noDefaultCtor.
struct SearchCtor
{
extern (C++) static int fp(Dsymbol s, void* ctxt)
{
auto f = s.isCtorDeclaration();
if (f && f.semanticRun == PASS.init)
f.dsymbolSemantic(null);
return 0;
}
}
for (size_t i = 0; i < members.dim; i++)
{
auto sm = (*members)[i];
sm.apply(&SearchCtor.fp, null);
}
}
return s;
}
override final Visibility visible() pure nothrow @nogc @safe
{
return visibility;
}
// 'this' type
final Type handleType()
{
return type;
}
// Does this class have an invariant function?
final bool hasInvariant()
{
return invs.length != 0;
}
// Back end
void* sinit; /// initializer symbol
override final inout(AggregateDeclaration) isAggregateDeclaration() inout
{
return this;
}
override void accept(Visitor v)
{
v.visit(this);
}
}

View file

@ -10,12 +10,10 @@
#pragma once
#include "root/root.h"
#include "dsymbol.h"
#include "declaration.h"
#include "objc.h"
class AliasThis;
class Identifier;
class Type;
class TypeFunction;
@ -23,65 +21,51 @@ class Expression;
class FuncDeclaration;
class CtorDeclaration;
class DtorDeclaration;
class InvariantDeclaration;
class NewDeclaration;
class DeleteDeclaration;
class InterfaceDeclaration;
class TypeInfoClassDeclaration;
class VarDeclaration;
enum Sizeok
enum class Sizeok : uint8_t
{
SIZEOKnone, // size of aggregate is not yet able to compute
SIZEOKfwd, // size of aggregate is ready to compute
SIZEOKdone // size of aggregate is set correctly
none, // size of aggregate is not yet able to compute
fwd, // size of aggregate is ready to compute
inProcess, // in the midst of computing the size
done // size of aggregate is set correctly
};
enum Baseok
enum class Baseok : uint8_t
{
BASEOKnone, // base classes not computed yet
BASEOKin, // in process of resolving base classes
BASEOKdone, // all base classes are resolved
BASEOKsemanticdone // all base classes semantic done
none, // base classes not computed yet
in, // in process of resolving base classes
done, // all base classes are resolved
semanticdone // all base classes semantic done
};
enum StructPOD
enum class ThreeState : uint8_t
{
ISPODno, // struct is not POD
ISPODyes, // struct is POD
ISPODfwd // POD not yet computed
none, // value not yet computed
no, // value is false
yes, // value is true
};
enum Abstract
{
ABSfwdref = 0, // whether an abstract class is not yet computed
ABSyes, // is abstract class
ABSno // is not abstract class
};
FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc);
FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc);
bool needOpEquals(StructDeclaration *sd);
FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildXtoHash(StructDeclaration *ad, Scope *sc);
FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc);
FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc);
FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc);
FuncDeclaration *search_toString(StructDeclaration *sd);
struct ClassKind
enum class ClassKind : uint8_t
{
enum Type
{
/// the class is a d(efault) class
d,
/// the class is a C++ interface
cpp,
/// the class is an Objective-C class/interface
objc,
};
/// the aggregate is a d(efault) struct/class/interface
d,
/// the aggregate is a C++ struct/class/interface
cpp,
/// the aggregate is an Objective-C class/interface
objc,
/// the aggregate is a C struct
c,
};
struct MangleOverride
{
Dsymbol *agg;
Identifier *id;
};
class AggregateDeclaration : public ScopeDsymbol
@ -89,16 +73,16 @@ class AggregateDeclaration : public ScopeDsymbol
public:
Type *type;
StorageClass storage_class;
Prot protection;
unsigned structsize; // size of struct
unsigned alignsize; // size of struct for alignment purposes
VarDeclarations fields; // VarDeclaration fields
Sizeok sizeok; // set when structsize contains valid data
Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol
bool isdeprecated; // true if deprecated
ClassKind::Type classKind; // specifies the linkage type
ClassKind classKind; // specifies the linkage type
CPPMANGLE cppmangle;
// overridden symbol with pragma(mangle, "...")
MangleOverride *mangleOverride;
/* !=NULL if is nested
* pointing to the dsymbol that directly enclosing it.
* 1. The function that enclosing it (nested struct and class)
@ -108,11 +92,10 @@ public:
*/
Dsymbol *enclosing;
VarDeclaration *vthis; // 'this' parameter if this aggregate is nested
VarDeclaration *vthis2; // 'this' parameter if this aggregate is a template and is nested
// Special member functions
FuncDeclarations invs; // Array of invariants
FuncDeclaration *inv; // invariant
NewDeclaration *aggNew; // allocator
DeleteDeclaration *aggDelete; // deallocator
Dsymbol *ctor; // CtorDeclaration or TemplateDeclaration
@ -120,42 +103,44 @@ public:
// it would be stored in TypeInfo_Class.defaultConstructor
CtorDeclaration *defaultCtor;
Dsymbol *aliasthis; // forward unresolved lookups to aliasthis
bool noDefaultCtor; // no default construction
AliasThis *aliasthis; // forward unresolved lookups to aliasthis
FuncDeclarations dtors; // Array of destructors
FuncDeclaration *dtor; // aggregate destructor
DtorDeclarations dtors; // Array of destructors
DtorDeclaration *dtor; // aggregate destructor
DtorDeclaration *primaryDtor; // non-deleting C++ destructor, same as dtor for D
DtorDeclaration *tidtor; // aggregate destructor used in TypeInfo (must have extern(D) ABI)
FuncDeclaration *fieldDtor; // aggregate destructor for just the fields
Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this)
AggregateDeclaration(Loc loc, Identifier *id);
Visibility visibility;
bool noDefaultCtor; // no default construction
bool disableNew; // disallow allocations using `new`
Sizeok sizeok; // set when structsize contains valid data
virtual Scope *newScope(Scope *sc);
void setScope(Scope *sc);
bool determineFields();
size_t nonHiddenFields();
bool determineSize(Loc loc);
virtual void finalizeSize() = 0;
d_uns64 size(Loc loc);
bool checkOverlappedFields();
d_uns64 size(const Loc &loc);
bool fill(Loc loc, Expressions *elements, bool ctorinit);
static void alignmember(structalign_t salign, unsigned size, unsigned *poffset);
static unsigned placeField(unsigned *nextoffset,
unsigned memsize, unsigned memalignsize, structalign_t memalign,
unsigned *paggsize, unsigned *paggalignsize, bool isunion);
Type *getType();
bool isDeprecated(); // is aggregate deprecated?
bool isNested();
void makeNested();
bool isDeprecated() const; // is aggregate deprecated?
void setDeprecated();
bool isNested() const;
bool isExport() const;
Dsymbol *searchCtor();
Prot prot();
Visibility visible();
// 'this' type
Type *handleType() { return type; }
bool hasInvariant();
// Back end
Symbol *stag; // tag symbol for debug data
Symbol *sinit;
void *sinit;
AggregateDeclaration *isAggregateDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -163,8 +148,7 @@ public:
struct StructFlags
{
typedef unsigned Type;
enum Enum
enum Type
{
none = 0x0,
hasPointers = 0x1 // NB: should use noPointers as in ClassFlags
@ -174,9 +158,17 @@ struct StructFlags
class StructDeclaration : public AggregateDeclaration
{
public:
int zeroInit; // !=0 if initialize with 0 fill
bool zeroInit; // !=0 if initialize with 0 fill
bool hasIdentityAssign; // true if has identity opAssign
bool hasBlitAssign; // true if opAssign is a blit
bool hasIdentityEquals; // true if has identity opEquals
bool hasNoFields; // has no fields
bool hasCopyCtor; // copy constructor
// Even if struct is defined as non-root symbol, some built-in operations
// (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
// For those, today TypeInfo_Struct is generated in COMDAT.
bool requestTypeInfo;
FuncDeclarations postblits; // Array of postblit functions
FuncDeclaration *postblit; // aggregate postblit
@ -187,36 +179,30 @@ public:
static FuncDeclaration *xerrcmp; // object.xopCmp
structalign_t alignment; // alignment applied outside of the struct
StructPOD ispod; // if struct is POD
ThreeState ispod; // if struct is POD
// For 64 bit Efl function call/return ABI
Type *arg1type;
Type *arg2type;
// ABI-specific type(s) if the struct can be passed in registers
TypeTuple *argTypes;
// Even if struct is defined as non-root symbol, some built-in operations
// (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
// For those, today TypeInfo_Struct is generated in COMDAT.
bool requestTypeInfo;
StructDeclaration(Loc loc, Identifier *id, bool inObject);
static StructDeclaration *create(Loc loc, Identifier *id, bool inObject);
Dsymbol *syntaxCopy(Dsymbol *s);
void semanticTypeInfoMembers();
StructDeclaration *syntaxCopy(Dsymbol *s);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
const char *kind() const;
void finalizeSize();
bool fit(Loc loc, Scope *sc, Expressions *elements, Type *stype);
bool isPOD();
StructDeclaration *isStructDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
unsigned numArgTypes() const;
Type *argType(unsigned index);
bool hasRegularCtor(bool checkDisabled = false);
};
class UnionDeclaration : public StructDeclaration
{
public:
UnionDeclaration(Loc loc, Identifier *id);
Dsymbol *syntaxCopy(Dsymbol *s);
UnionDeclaration *syntaxCopy(Dsymbol *s);
const char *kind() const;
UnionDeclaration *isUnionDeclaration() { return this; }
@ -236,18 +222,14 @@ struct BaseClass
DArray<BaseClass> baseInterfaces; // if BaseClass is an interface, these
// are a copy of the InterfaceDeclaration::interfaces
BaseClass();
BaseClass(Type *type);
bool fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance);
void copyBaseInterfaces(BaseClasses *);
};
struct ClassFlags
{
typedef unsigned Type;
enum Enum
enum Type
{
none = 0x0,
isCOMclass = 0x1,
noPointers = 0x2,
hasOffTi = 0x4,
@ -286,15 +268,18 @@ public:
TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration
bool com; // true if this is a COM class (meaning it derives from IUnknown)
bool isscope; // true if this is a scope class
Abstract isabstract; // 0: fwdref, 1: is abstract class, 2: not abstract
int inuse; // to prevent recursive attempts
bool stack; // true if this is a scope class
int cppDtorVtblIndex; // slot reserved for the virtual destructor [extern(C++)]
bool inuse; // to prevent recursive attempts
ThreeState isabstract; // if abstract class
Baseok baseok; // set the progress of base classes resolving
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
ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject = false);
static ClassDeclaration *create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject);
Dsymbol *syntaxCopy(Dsymbol *s);
const char *toPrettyChars(bool QualifyTypes = false);
ClassDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
bool isBaseOf2(ClassDeclaration *cd);
@ -318,9 +303,11 @@ public:
const char *kind() const;
void addLocalClass(ClassDeclarations *);
void addObjcSymbols(ClassDeclarations *classes, ClassDeclarations *categories);
// Back end
Symbol *vtblsym;
Dsymbol *vtblsym;
Dsymbol *vtblSymbol();
ClassDeclaration *isClassDeclaration() { return (ClassDeclaration *)this; }
void accept(Visitor *v) { v->visit(this); }
@ -329,11 +316,9 @@ public:
class InterfaceDeclaration : public ClassDeclaration
{
public:
InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses);
Dsymbol *syntaxCopy(Dsymbol *s);
InterfaceDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
bool isBaseOf(ClassDeclaration *cd, int *poffset);
bool isBaseOf(BaseClass *bc, int *poffset);
const char *kind() const;
int vtblOffset() const;
bool isCPPinterface() const;

View file

@ -1,94 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 2009-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/aliasthis.c
*/
#include "root/dsystem.h"
#include "mars.h"
#include "identifier.h"
#include "aliasthis.h"
#include "scope.h"
#include "aggregate.h"
#include "dsymbol.h"
#include "mtype.h"
#include "declaration.h"
#include "expression.h"
#include "tokens.h"
Expression *resolveAliasThis(Scope *sc, Expression *e, bool gag)
{
AggregateDeclaration *ad = isAggregate(e->type);
if (ad && ad->aliasthis)
{
unsigned olderrors = gag ? global.startGagging() : 0;
Loc loc = e->loc;
Type *tthis = (e->op == TOKtype ? e->type : NULL);
e = new DotIdExp(loc, e, ad->aliasthis->ident);
e = expressionSemantic(e, sc);
if (tthis && ad->aliasthis->needThis())
{
if (e->op == TOKvar)
{
if (FuncDeclaration *fd = ((VarExp *)e)->var->isFuncDeclaration())
{
// Bugzilla 13009: Support better match for the overloaded alias this.
bool hasOverloads = false;
if (FuncDeclaration *f = fd->overloadModMatch(loc, tthis, hasOverloads))
{
if (!hasOverloads)
fd = f; // use exact match
e = new VarExp(loc, fd, hasOverloads);
e->type = f->type;
e = new CallExp(loc, e);
goto L1;
}
}
}
/* non-@property function is not called inside typeof(),
* so resolve it ahead.
*/
{
int save = sc->intypeof;
sc->intypeof = 1; // bypass "need this" error check
e = resolveProperties(sc, e);
sc->intypeof = save;
}
L1:
e = new TypeExp(loc, new TypeTypeof(loc, e));
e = expressionSemantic(e, sc);
}
e = resolveProperties(sc, e);
if (gag && global.endGagging(olderrors))
e = NULL;
}
return e;
}
AliasThis::AliasThis(Loc loc, Identifier *ident)
: Dsymbol(NULL) // it's anonymous (no identifier)
{
this->loc = loc;
this->ident = ident;
}
Dsymbol *AliasThis::syntaxCopy(Dsymbol *s)
{
assert(!s);
return new AliasThis(loc, ident);
}
const char *AliasThis::kind() const
{
return "alias this";
}

202
gcc/d/dmd/aliasthis.d Normal file
View file

@ -0,0 +1,202 @@
/**
* Implements the `alias this` symbol.
*
* Specification: $(LINK2 https://dlang.org/spec/class.html#alias-this, Alias This)
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.aliasthis;
import core.stdc.stdio;
import dmd.aggregate;
import dmd.dscope;
import dmd.dsymbol;
import dmd.expression;
import dmd.expressionsem;
import dmd.globals;
import dmd.identifier;
import dmd.mtype;
import dmd.opover;
import dmd.tokens;
import dmd.visitor;
/***********************************************************
* alias ident this;
*/
extern (C++) final class AliasThis : Dsymbol
{
Identifier ident;
/// The symbol this `alias this` resolves to
Dsymbol sym;
/// Whether this `alias this` is deprecated or not
bool isDeprecated_;
extern (D) this(const ref Loc loc, Identifier ident)
{
super(loc, null); // it's anonymous (no identifier)
this.ident = ident;
}
override AliasThis syntaxCopy(Dsymbol s)
{
assert(!s);
auto at = new AliasThis(loc, ident);
at.comment = comment;
return at;
}
override const(char)* kind() const
{
return "alias this";
}
AliasThis isAliasThis()
{
return this;
}
override void accept(Visitor v)
{
v.visit(this);
}
override bool isDeprecated() const
{
return this.isDeprecated_;
}
}
Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false)
{
for (AggregateDeclaration ad = isAggregate(e.type); ad;)
{
if (ad.aliasthis)
{
uint olderrors = gag ? global.startGagging() : 0;
Loc loc = e.loc;
Type tthis = (e.op == TOK.type ? e.type : null);
e = new DotIdExp(loc, e, ad.aliasthis.ident);
e = e.expressionSemantic(sc);
if (tthis && ad.aliasthis.sym.needThis())
{
if (e.op == TOK.variable)
{
if (auto fd = (cast(VarExp)e).var.isFuncDeclaration())
{
// https://issues.dlang.org/show_bug.cgi?id=13009
// Support better match for the overloaded alias this.
bool hasOverloads;
if (auto f = fd.overloadModMatch(loc, tthis, hasOverloads))
{
if (!hasOverloads)
fd = f; // use exact match
e = new VarExp(loc, fd, hasOverloads);
e.type = f.type;
e = new CallExp(loc, e);
goto L1;
}
}
}
/* non-@property function is not called inside typeof(),
* so resolve it ahead.
*/
{
int save = sc.intypeof;
sc.intypeof = 1; // bypass "need this" error check
e = resolveProperties(sc, e);
sc.intypeof = save;
}
L1:
e = new TypeExp(loc, new TypeTypeof(loc, e));
e = e.expressionSemantic(sc);
}
e = resolveProperties(sc, e);
if (!gag)
ad.aliasthis.checkDeprecatedAliasThis(loc, sc);
else if (global.endGagging(olderrors))
e = null;
}
import dmd.dclass : ClassDeclaration;
auto cd = ad.isClassDeclaration();
if ((!e || !ad.aliasthis) && cd && cd.baseClass && cd.baseClass != ClassDeclaration.object)
{
ad = cd.baseClass;
continue;
}
break;
}
return e;
}
/**
* Check if an `alias this` is deprecated
*
* Usually one would use `expression.checkDeprecated(scope, aliasthis)` to
* check if `expression` uses a deprecated `aliasthis`, but this calls
* `toPrettyChars` which lead to the following message:
* "Deprecation: alias this `fullyqualified.aggregate.__anonymous` is deprecated"
*
* Params:
* at = The `AliasThis` object to check
* loc = `Loc` of the expression triggering the access to `at`
* sc = `Scope` of the expression
* (deprecations do not trigger in deprecated scopes)
*
* Returns:
* Whether the alias this was reported as deprecated.
*/
bool checkDeprecatedAliasThis(AliasThis at, const ref Loc loc, Scope* sc)
{
import dmd.errors : deprecation, Classification;
import dmd.dsymbolsem : getMessage;
if (global.params.useDeprecated != DiagnosticReporting.off
&& at.isDeprecated() && !sc.isDeprecated())
{
const(char)* message = null;
for (Dsymbol p = at; p; p = p.parent)
{
message = p.depdecl ? p.depdecl.getMessage() : null;
if (message)
break;
}
if (message)
deprecation(loc, "`alias %s this` is deprecated - %s",
at.sym.toChars(), message);
else
deprecation(loc, "`alias %s this` is deprecated",
at.sym.toChars());
if (auto ti = sc.parent ? sc.parent.isInstantiated() : null)
ti.printInstantiationTrace(Classification.deprecation);
return true;
}
return false;
}
/**************************************
* Check and set 'att' if 't' is a recursive 'alias this' type
* Params:
* att = type reference used to detect recursion
* t = 'alias this' type
*
* Returns:
* Whether the 'alias this' is recursive or not
*/
bool isRecursiveAliasThis(ref Type att, Type t)
{
auto tb = t.toBasetype();
if (att && tb.equivalent(att))
return true;
else if (!att && tb.checkAliasThisRec())
att = tb;
return false;
}

View file

@ -5,11 +5,12 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/dlang/dmd/blob/master/src/aliasthis.h
* https://github.com/dlang/dmd/blob/master/src/dmd/aliasthis.h
*/
#pragma once
#include "globals.h"
#include "dsymbol.h"
/**************************************************************/
@ -19,11 +20,12 @@ class AliasThis : public Dsymbol
public:
// alias Identifier this;
Identifier *ident;
Dsymbol *sym;
bool isDeprecated_;
AliasThis(Loc loc, Identifier *ident);
Dsymbol *syntaxCopy(Dsymbol *);
AliasThis *syntaxCopy(Dsymbol *);
const char *kind() const;
AliasThis *isAliasThis() { return this; }
void accept(Visitor *v) { v->visit(this); }
bool isDeprecated() const { return this->isDeprecated_; }
};

View file

@ -1,149 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/apply.c
*/
#include "root/dsystem.h"
#include "mars.h"
#include "expression.h"
#include "template.h"
#include "visitor.h"
/**************************************
* 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.
*/
class PostorderExpressionVisitor : public StoppableVisitor
{
public:
StoppableVisitor *v;
PostorderExpressionVisitor(StoppableVisitor *v) : v(v) {}
bool doCond(Expression *e)
{
if (!stop && e)
e->accept(this);
return stop;
}
bool doCond(Expressions *e)
{
if (!e)
return false;
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;
}
void visit(Expression *e)
{
applyTo(e);
}
void visit(NewExp *e)
{
//printf("NewExp::apply(): %s\n", toChars());
doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e);
}
void visit(NewAnonClassExp *e)
{
//printf("NewAnonClassExp::apply(): %s\n", toChars());
doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e);
}
void visit(TypeidExp *e)
{
doCond(isExpression(e->obj)) || applyTo(e);
}
void visit(UnaExp *e)
{
doCond(e->e1) || applyTo(e);
}
void visit(BinExp *e)
{
doCond(e->e1) || doCond(e->e2) || applyTo(e);
}
void visit(AssertExp *e)
{
//printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
doCond(e->e1) || doCond(e->msg) || applyTo(e);
}
void visit(CallExp *e)
{
//printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
doCond(e->e1) || doCond(e->arguments) || applyTo(e);
}
void visit(ArrayExp *e)
{
//printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars());
doCond(e->e1) || doCond(e->arguments) || applyTo(e);
}
void visit(SliceExp *e)
{
doCond(e->e1) || doCond(e->lwr) || doCond(e->upr) || applyTo(e);
}
void visit(ArrayLiteralExp *e)
{
doCond(e->basis) || doCond(e->elements) || applyTo(e);
}
void visit(AssocArrayLiteralExp *e)
{
doCond(e->keys) || doCond(e->values) || applyTo(e);
}
void visit(StructLiteralExp *e)
{
if (e->stageflags & stageApply) return;
int old = e->stageflags;
e->stageflags |= stageApply;
doCond(e->elements) || applyTo(e);
e->stageflags = old;
}
void visit(TupleExp *e)
{
doCond(e->e0) || doCond(e->exps) || applyTo(e);
}
void visit(CondExp *e)
{
doCond(e->econd) || doCond(e->e1) || doCond(e->e2) || applyTo(e);
}
};
bool walkPostorder(Expression *e, StoppableVisitor *v)
{
PostorderExpressionVisitor pv(v);
e->accept(&pv);
return v->stop;
}

189
gcc/d/dmd/apply.d Normal file
View file

@ -0,0 +1,189 @@
/**
* A depth-first visitor for expressions.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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.apply;
import dmd.arraytypes;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.expression;
import dmd.visitor;
bool walkPostorder(Expression e, StoppableVisitor v)
{
scope PostorderExpressionVisitor pv = new PostorderExpressionVisitor(v);
e.accept(pv);
return v.stop;
}
/*********************************
* Iterate this dsymbol or members of this scoped dsymbol, then
* call `fp` with the found symbol and `params`.
* Params:
* symbol = the dsymbol or parent of members to call fp on
* fp = function pointer to process the iterated symbol.
* If it returns nonzero, the iteration will be aborted.
* params = any parameters passed to fp.
* Returns:
* nonzero if the iteration is aborted by the return value of fp,
* or 0 if it's completed.
*/
int apply(FP, Params...)(Dsymbol symbol, FP fp, Params params)
{
if (auto nd = symbol.isNspace())
{
return nd.members.foreachDsymbol( (s) { return s && s.apply(fp, params); } );
}
if (auto ad = symbol.isAttribDeclaration())
{
return ad.include(ad._scope).foreachDsymbol( (s) { return s && s.apply(fp, params); } );
}
if (auto tm = symbol.isTemplateMixin())
{
if (tm._scope) // if fwd reference
dsymbolSemantic(tm, null); // try to resolve it
return tm.members.foreachDsymbol( (s) { return s && s.apply(fp, params); } );
}
return fp(symbol, params);
}
/**************************************
* 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)
{
this.v = v;
}
bool doCond(Expression e)
{
if (!stop && e)
e.accept(this);
return stop;
}
bool doCond(Expressions* e)
{
if (!e)
return false;
for (size_t i = 0; i < e.dim && !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.newargs) || doCond(e.arguments) || applyTo(e);
}
override void visit(NewAnonClassExp e)
{
//printf("NewAnonClassExp::apply(): %s\n", toChars());
doCond(e.thisexp) || doCond(e.newargs) || doCond(e.arguments) || 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) || 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) || 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) || applyTo(e);
}
override void visit(AssocArrayLiteralExp e)
{
doCond(e.keys) || doCond(e.values) || applyTo(e);
}
override void visit(StructLiteralExp e)
{
if (e.stageflags & stageApply)
return;
int old = e.stageflags;
e.stageflags |= stageApply;
doCond(e.elements) || applyTo(e);
e.stageflags = old;
}
override void visit(TupleExp e)
{
doCond(e.e0) || doCond(e.exps) || applyTo(e);
}
override void visit(CondExp e)
{
doCond(e.econd) || doCond(e.e1) || doCond(e.e2) || applyTo(e);
}
}

View file

@ -1,634 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/arrayop.c
*/
#include "root/dsystem.h"
#include "root/rmem.h"
#include "root/aav.h"
#include "mars.h"
#include "expression.h"
#include "statement.h"
#include "mtype.h"
#include "declaration.h"
#include "scope.h"
#include "id.h"
#include "module.h"
#include "init.h"
#include "tokens.h"
void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments);
Expression *buildArrayLoop(Expression *e, Parameters *fparams);
/**************************************
* Hash table of array op functions already generated or known about.
*/
AA *arrayfuncs;
/**************************************
* Structure to contain information needed to insert an array op call
*/
FuncDeclaration *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc)
{
Parameters *fparams = new Parameters();
Expression *loopbody = buildArrayLoop(exp, fparams);
/* Construct the function body:
* foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++)
* loopbody;
* return p;
*/
Parameter *p = (*fparams)[0];
// foreach (i; 0 .. p.length)
Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach,
new Parameter(0, NULL, Id::p, NULL, NULL),
new IntegerExp(Loc(), 0, Type::tsize_t),
new ArrayLengthExp(Loc(), new IdentifierExp(Loc(), p->ident)),
new ExpStatement(Loc(), loopbody),
Loc());
//printf("%s\n", s1->toChars());
Statement *s2 = new ReturnStatement(Loc(), new IdentifierExp(Loc(), p->ident));
//printf("s2: %s\n", s2->toChars());
Statement *fbody = new CompoundStatement(Loc(), s1, s2);
// Built-in array ops should be @trusted, pure, nothrow and nogc
StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc;
/* Construct the function
*/
TypeFunction *ftype = new TypeFunction(ParameterList(fparams), exp->e1->type, LINKc, stc);
//printf("fd: %s %s\n", ident->toChars(), ftype->toChars());
FuncDeclaration *fd = new FuncDeclaration(Loc(), Loc(), ident, STCundefined, ftype);
fd->fbody = fbody;
fd->protection = Prot(Prot::public_);
fd->linkage = LINKc;
fd->isArrayOp = 1;
sc->_module->importedFrom->members->push(fd);
sc = sc->push();
sc->parent = sc->_module->importedFrom;
sc->stc = 0;
sc->linkage = LINKc;
dsymbolSemantic(fd, sc);
semantic2(fd, sc);
unsigned errors = global.startGagging();
semantic3(fd, sc);
if (global.endGagging(errors))
{
fd->type = Type::terror;
fd->errors = true;
fd->fbody = NULL;
}
sc->pop();
return fd;
}
/**********************************************
* Check that there are no uses of arrays without [].
*/
bool isArrayOpValid(Expression *e)
{
if (e->op == TOKslice)
return true;
if (e->op == TOKarrayliteral)
{
Type *t = e->type->toBasetype();
while (t->ty == Tarray || t->ty == Tsarray)
t = t->nextOf()->toBasetype();
return (t->ty != Tvoid);
}
Type *tb = e->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
if (isUnaArrayOp(e->op))
{
return isArrayOpValid(((UnaExp *)e)->e1);
}
if (isBinArrayOp(e->op) ||
isBinAssignArrayOp(e->op) ||
e->op == TOKassign)
{
BinExp *be = (BinExp *)e;
return isArrayOpValid(be->e1) && isArrayOpValid(be->e2);
}
if (e->op == TOKconstruct)
{
BinExp *be = (BinExp *)e;
return be->e1->op == TOKslice && isArrayOpValid(be->e2);
}
if (e->op == TOKcall)
{
return false; // TODO: Decide if [] is required after arrayop calls.
}
else
{
return false;
}
}
return true;
}
bool isNonAssignmentArrayOp(Expression *e)
{
if (e->op == TOKslice)
return isNonAssignmentArrayOp(((SliceExp *)e)->e1);
Type *tb = e->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
return (isUnaArrayOp(e->op) || isBinArrayOp(e->op));
}
return false;
}
bool checkNonAssignmentArrayOp(Expression *e, bool suggestion)
{
if (isNonAssignmentArrayOp(e))
{
const char *s = "";
if (suggestion)
s = " (possible missing [])";
e->error("array operation %s without destination memory not allowed%s", e->toChars(), s);
return true;
}
return false;
}
/***********************************
* Construct the array operation expression.
*/
Expression *arrayOp(BinExp *e, Scope *sc)
{
//printf("BinExp::arrayOp() %s\n", toChars());
Type *tb = e->type->toBasetype();
assert(tb->ty == Tarray || tb->ty == Tsarray);
Type *tbn = tb->nextOf()->toBasetype();
if (tbn->ty == Tvoid)
{
e->error("cannot perform array operations on void[] arrays");
return new ErrorExp();
}
if (!isArrayOpValid(e))
{
e->error("invalid array operation %s (possible missing [])", e->toChars());
return new ErrorExp();
}
Expressions *arguments = new Expressions();
/* The expression to generate an array operation for is mangled
* into a name to use as the array operation function name.
* Mangle in the operands and operators in RPN order, and type.
*/
OutBuffer buf;
buf.writestring("_array");
buildArrayIdent(e, &buf, arguments);
buf.writeByte('_');
/* Append deco of array element type
*/
buf.writestring(e->type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco);
char *name = buf.peekChars();
Identifier *ident = Identifier::idPool(name);
FuncDeclaration **pFd = (FuncDeclaration **)dmd_aaGet(&arrayfuncs, (void *)ident);
FuncDeclaration *fd = *pFd;
if (!fd)
fd = buildArrayOp(ident, e, sc);
if (fd && fd->errors)
{
const char *fmt;
if (tbn->ty == Tstruct || tbn->ty == Tclass)
fmt = "invalid array operation '%s' because %s doesn't support necessary arithmetic operations";
else if (!tbn->isscalar())
fmt = "invalid array operation '%s' because %s is not a scalar type";
else
fmt = "invalid array operation '%s' for element type %s";
e->error(fmt, e->toChars(), tbn->toChars());
return new ErrorExp();
}
*pFd = fd;
Expression *ev = new VarExp(e->loc, fd);
Expression *ec = new CallExp(e->loc, ev, arguments);
return expressionSemantic(ec, sc);
}
Expression *arrayOp(BinAssignExp *e, Scope *sc)
{
//printf("BinAssignExp::arrayOp() %s\n", toChars());
/* Check that the elements of e1 can be assigned to
*/
Type *tn = e->e1->type->toBasetype()->nextOf();
if (tn && (!tn->isMutable() || !tn->isAssignable()))
{
e->error("slice %s is not mutable", e->e1->toChars());
return new ErrorExp();
}
if (e->e1->op == TOKarrayliteral)
{
return e->e1->modifiableLvalue(sc, e->e1);
}
return arrayOp((BinExp *)e, sc);
}
/******************************************
* Construct the identifier for the array operation function,
* and build the argument list to pass to it.
*/
void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments)
{
class BuildArrayIdentVisitor : public Visitor
{
OutBuffer *buf;
Expressions *arguments;
public:
BuildArrayIdentVisitor(OutBuffer *buf, Expressions *arguments)
: buf(buf), arguments(arguments)
{
}
void visit(Expression *e)
{
buf->writestring("Exp");
arguments->shift(e);
}
void visit(CastExp *e)
{
Type *tb = e->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
e->e1->accept(this);
}
else
visit((Expression *)e);
}
void visit(ArrayLiteralExp *e)
{
buf->writestring("Slice");
arguments->shift(e);
}
void visit(SliceExp *e)
{
buf->writestring("Slice");
arguments->shift(e);
}
void visit(AssignExp *e)
{
/* Evaluate assign expressions right to left
*/
e->e2->accept(this);
e->e1->accept(this);
buf->writestring("Assign");
}
void visit(BinAssignExp *e)
{
/* Evaluate assign expressions right to left
*/
e->e2->accept(this);
e->e1->accept(this);
const char *s;
switch(e->op)
{
case TOKaddass: s = "Addass"; break;
case TOKminass: s = "Minass"; break;
case TOKmulass: s = "Mulass"; break;
case TOKdivass: s = "Divass"; break;
case TOKmodass: s = "Modass"; break;
case TOKxorass: s = "Xorass"; break;
case TOKandass: s = "Andass"; break;
case TOKorass: s = "Orass"; break;
case TOKpowass: s = "Powass"; break;
default: assert(0);
}
buf->writestring(s);
}
void visit(NegExp *e)
{
e->e1->accept(this);
buf->writestring("Neg");
}
void visit(ComExp *e)
{
e->e1->accept(this);
buf->writestring("Com");
}
void visit(BinExp *e)
{
/* Evaluate assign expressions left to right
*/
const char *s = NULL;
switch(e->op)
{
case TOKadd: s = "Add"; break;
case TOKmin: s = "Min"; break;
case TOKmul: s = "Mul"; break;
case TOKdiv: s = "Div"; break;
case TOKmod: s = "Mod"; break;
case TOKxor: s = "Xor"; break;
case TOKand: s = "And"; break;
case TOKor: s = "Or"; break;
case TOKpow: s = "Pow"; break;
default: break;
}
if (s)
{
Type *tb = e->type->toBasetype();
Type *t1 = e->e1->type->toBasetype();
Type *t2 = e->e2->type->toBasetype();
e->e1->accept(this);
if (t1->ty == Tarray &&
((t2->ty == Tarray && !t1->equivalent(tb)) ||
(t2->ty != Tarray && !t1->nextOf()->equivalent(e->e2->type))))
{
// Bugzilla 12780: if A is narrower than B
// A[] op B[]
// A[] op B
buf->writestring("Of");
buf->writestring(t1->nextOf()->mutableOf()->deco);
}
e->e2->accept(this);
if (t2->ty == Tarray &&
((t1->ty == Tarray && !t2->equivalent(tb)) ||
(t1->ty != Tarray && !t2->nextOf()->equivalent(e->e1->type))))
{
// Bugzilla 12780: if B is narrower than A:
// A[] op B[]
// A op B[]
buf->writestring("Of");
buf->writestring(t2->nextOf()->mutableOf()->deco);
}
buf->writestring(s);
}
else
visit((Expression *)e);
}
};
BuildArrayIdentVisitor v(buf, arguments);
e->accept(&v);
}
/******************************************
* Construct the inner loop for the array operation function,
* and build the parameter list.
*/
Expression *buildArrayLoop(Expression *e, Parameters *fparams)
{
class BuildArrayLoopVisitor : public Visitor
{
Parameters *fparams;
Expression *result;
public:
BuildArrayLoopVisitor(Parameters *fparams)
: fparams(fparams), result(NULL)
{
}
void visit(Expression *e)
{
Identifier *id = Identifier::generateId("c", fparams->length);
Parameter *param = new Parameter(0, e->type, id, NULL, NULL);
fparams->shift(param);
result = new IdentifierExp(Loc(), id);
}
void visit(CastExp *e)
{
Type *tb = e->type->toBasetype();
if (tb->ty == Tarray || tb->ty == Tsarray)
{
e->e1->accept(this);
}
else
visit((Expression *)e);
}
void visit(ArrayLiteralExp *e)
{
Identifier *id = Identifier::generateId("p", fparams->length);
Parameter *param = new Parameter(STCconst, e->type, id, NULL, NULL);
fparams->shift(param);
Expression *ie = new IdentifierExp(Loc(), id);
Expression *index = new IdentifierExp(Loc(), Id::p);
result = new ArrayExp(Loc(), ie, index);
}
void visit(SliceExp *e)
{
Identifier *id = Identifier::generateId("p", fparams->length);
Parameter *param = new Parameter(STCconst, e->type, id, NULL, NULL);
fparams->shift(param);
Expression *ie = new IdentifierExp(Loc(), id);
Expression *index = new IdentifierExp(Loc(), Id::p);
result = new ArrayExp(Loc(), ie, index);
}
void visit(AssignExp *e)
{
/* Evaluate assign expressions right to left
*/
Expression *ex2 = buildArrayLoop(e->e2);
/* Need the cast because:
* b = c + p[i];
* where b is a byte fails because (c + p[i]) is an int
* which cannot be implicitly cast to byte.
*/
ex2 = new CastExp(Loc(), ex2, e->e1->type->nextOf());
Expression *ex1 = buildArrayLoop(e->e1);
Parameter *param = (*fparams)[0];
param->storageClass = 0;
result = new AssignExp(Loc(), ex1, ex2);
}
void visit(BinAssignExp *e)
{
/* Evaluate assign expressions right to left
*/
Expression *ex2 = buildArrayLoop(e->e2);
Expression *ex1 = buildArrayLoop(e->e1);
Parameter *param = (*fparams)[0];
param->storageClass = 0;
switch(e->op)
{
case TOKaddass: result = new AddAssignExp(e->loc, ex1, ex2); return;
case TOKminass: result = new MinAssignExp(e->loc, ex1, ex2); return;
case TOKmulass: result = new MulAssignExp(e->loc, ex1, ex2); return;
case TOKdivass: result = new DivAssignExp(e->loc, ex1, ex2); return;
case TOKmodass: result = new ModAssignExp(e->loc, ex1, ex2); return;
case TOKxorass: result = new XorAssignExp(e->loc, ex1, ex2); return;
case TOKandass: result = new AndAssignExp(e->loc, ex1, ex2); return;
case TOKorass: result = new OrAssignExp(e->loc, ex1, ex2); return;
case TOKpowass: result = new PowAssignExp(e->loc, ex1, ex2); return;
default:
assert(0);
}
}
void visit(NegExp *e)
{
Expression *ex1 = buildArrayLoop(e->e1);
result = new NegExp(Loc(), ex1);
}
void visit(ComExp *e)
{
Expression *ex1 = buildArrayLoop(e->e1);
result = new ComExp(Loc(), ex1);
}
void visit(BinExp *e)
{
if (isBinArrayOp(e->op))
{
/* Evaluate assign expressions left to right
*/
BinExp *be = (BinExp *)e->copy();
be->e1 = buildArrayLoop(be->e1);
be->e2 = buildArrayLoop(be->e2);
be->type = NULL;
result = be;
return;
}
else
{
visit((Expression *)e);
return;
}
}
Expression *buildArrayLoop(Expression *e)
{
e->accept(this);
return result;
}
};
BuildArrayLoopVisitor v(fparams);
return v.buildArrayLoop(e);
}
/***********************************************
* Test if expression is a unary array op.
*/
bool isUnaArrayOp(TOK op)
{
switch (op)
{
case TOKneg:
case TOKtilde:
return true;
default:
break;
}
return false;
}
/***********************************************
* Test if expression is a binary array op.
*/
bool isBinArrayOp(TOK op)
{
switch (op)
{
case TOKadd:
case TOKmin:
case TOKmul:
case TOKdiv:
case TOKmod:
case TOKxor:
case TOKand:
case TOKor:
case TOKpow:
return true;
default:
break;
}
return false;
}
/***********************************************
* Test if expression is a binary assignment array op.
*/
bool isBinAssignArrayOp(TOK op)
{
switch (op)
{
case TOKaddass:
case TOKminass:
case TOKmulass:
case TOKdivass:
case TOKmodass:
case TOKxorass:
case TOKandass:
case TOKorass:
case TOKpowass:
return true;
default:
break;
}
return false;
}
/***********************************************
* Test if operand is a valid array op operand.
*/
bool isArrayOpOperand(Expression *e)
{
//printf("Expression::isArrayOpOperand() %s\n", e->toChars());
if (e->op == TOKslice)
return true;
if (e->op == TOKarrayliteral)
{
Type *t = e->type->toBasetype();
while (t->ty == Tarray || t->ty == Tsarray)
t = t->nextOf()->toBasetype();
return (t->ty != Tvoid);
}
Type *tb = e->type->toBasetype();
if (tb->ty == Tarray)
{
return (isUnaArrayOp(e->op) ||
isBinArrayOp(e->op) ||
isBinAssignArrayOp(e->op) ||
e->op == TOKassign);
}
return false;
}

387
gcc/d/dmd/arrayop.d Normal file
View file

@ -0,0 +1,387 @@
/**
* Implement array operations, such as `a[] = b[] + c[]`.
*
* Specification: $(LINK2 https://dlang.org/spec/arrays.html#array-operations, Array Operations)
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.arrayop;
import core.stdc.stdio;
import dmd.arraytypes;
import dmd.astenums;
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.root.outbuffer;
import dmd.statement;
import dmd.tokens;
import dmd.visitor;
/**********************************************
* Check that there are no uses of arrays without [].
*/
bool isArrayOpValid(Expression e)
{
//printf("isArrayOpValid() %s\n", e.toChars());
if (e.op == TOK.slice)
return true;
if (e.op == TOK.arrayLiteral)
{
Type t = e.type.toBasetype();
while (t.ty == Tarray || t.ty == Tsarray)
t = t.nextOf().toBasetype();
return (t.ty != Tvoid);
}
Type tb = e.type.toBasetype();
if (tb.ty == Tarray || tb.ty == Tsarray)
{
if (isUnaArrayOp(e.op))
{
return isArrayOpValid((cast(UnaExp)e).e1);
}
if (isBinArrayOp(e.op) || isBinAssignArrayOp(e.op) || e.op == TOK.assign)
{
BinExp be = cast(BinExp)e;
return isArrayOpValid(be.e1) && isArrayOpValid(be.e2);
}
if (e.op == TOK.construct)
{
BinExp be = cast(BinExp)e;
return be.e1.op == TOK.slice && isArrayOpValid(be.e2);
}
// if (e.op == TOK.call)
// {
// TODO: Decide if [] is required after arrayop calls.
// }
return false;
}
return true;
}
bool isNonAssignmentArrayOp(Expression e)
{
if (e.op == TOK.slice)
return isNonAssignmentArrayOp((cast(SliceExp)e).e1);
Type tb = e.type.toBasetype();
if (tb.ty == Tarray || tb.ty == Tsarray)
{
return (isUnaArrayOp(e.op) || isBinArrayOp(e.op));
}
return false;
}
bool checkNonAssignmentArrayOp(Expression e, bool suggestion = false)
{
if (isNonAssignmentArrayOp(e))
{
const(char)* s = "";
if (suggestion)
s = " (possible missing [])";
e.error("array operation `%s` without destination memory not allowed%s", e.toChars(), s);
return true;
}
return false;
}
/***********************************
* Construct the array operation expression, call object._arrayOp!(tiargs)(args).
*
* Encode operand types and operations into tiargs using reverse polish notation (RPN) to preserve precedence.
* Unary operations are prefixed with "u" (e.g. "u~").
* Pass operand values (slices or scalars) as args.
*
* Scalar expression sub-trees of `e` are evaluated before calling
* into druntime to hoist them out of the loop. This is a valid
* evaluation order as the actual array operations have no
* side-effect.
* References:
* https://github.com/dlang/druntime/blob/master/src/object.d#L3944
* https://github.com/dlang/druntime/blob/master/src/core/internal/array/operations.d
*/
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);
Type tbn = tb.nextOf().toBasetype();
if (tbn.ty == Tvoid)
{
e.error("cannot perform array operations on `void[]` arrays");
return ErrorExp.get();
}
if (!isArrayOpValid(e))
return arrayOpInvalidError(e);
auto tiargs = new Objects();
auto args = new Expressions();
buildArrayOp(sc, e, tiargs, args);
import dmd.dtemplate : TemplateDeclaration;
__gshared TemplateDeclaration arrayOp;
if (arrayOp is null)
{
// Create .object._arrayOp
Identifier idArrayOp = Identifier.idPool("_arrayOp");
Expression id = new IdentifierExp(e.loc, Id.empty);
id = new DotIdExp(e.loc, id, Id.object);
id = new DotIdExp(e.loc, id, idArrayOp);
id = id.expressionSemantic(sc);
if (auto te = id.isTemplateExp())
arrayOp = te.td;
else
ObjectNotFound(idArrayOp); // fatal error
}
auto fd = resolveFuncCall(e.loc, sc, arrayOp, tiargs, null, args, FuncResolveFlag.standard);
if (!fd || fd.errors)
return ErrorExp.get();
return new CallExp(e.loc, new VarExp(e.loc, fd, false), args).expressionSemantic(sc);
}
/// ditto
Expression arrayOp(BinAssignExp e, Scope* sc)
{
//printf("BinAssignExp.arrayOp() %s\n", toChars());
/* Check that the elements of e1 can be assigned to
*/
Type tn = e.e1.type.toBasetype().nextOf();
if (tn && (!tn.isMutable() || !tn.isAssignable()))
{
e.error("slice `%s` is not mutable", e.e1.toChars());
if (e.op == TOK.addAssign)
checkPossibleAddCatError!(AddAssignExp, CatAssignExp)(e.isAddAssignExp);
return ErrorExp.get();
}
if (e.e1.op == TOK.arrayLiteral)
{
return e.e1.modifiableLvalue(sc, e.e1);
}
return arrayOp(cast(BinExp)e, sc);
}
/******************************************
* Convert the expression tree e to template and function arguments,
* using reverse polish notation (RPN) to encode order of operations.
* Encode operations as string arguments, using a "u" prefix for unary operations.
*/
private void buildArrayOp(Scope* sc, Expression e, Objects* tiargs, Expressions* args)
{
extern (C++) final class BuildArrayOpVisitor : Visitor
{
alias visit = Visitor.visit;
Scope* sc;
Objects* tiargs;
Expressions* args;
public:
extern (D) this(Scope* sc, Objects* tiargs, Expressions* args)
{
this.sc = sc;
this.tiargs = tiargs;
this.args = args;
}
override void visit(Expression e)
{
tiargs.push(e.type);
args.push(e);
}
override void visit(SliceExp e)
{
visit(cast(Expression) e);
}
override void visit(CastExp e)
{
visit(cast(Expression) e);
}
override void visit(UnaExp e)
{
Type tb = e.type.toBasetype();
if (tb.ty != Tarray && tb.ty != Tsarray) // hoist scalar expressions
{
visit(cast(Expression) e);
}
else
{
// RPN, prefix unary ops with u
OutBuffer buf;
buf.writestring("u");
buf.writestring(Token.toString(e.op));
e.e1.accept(this);
tiargs.push(new StringExp(Loc.initial, buf.extractSlice()).expressionSemantic(sc));
}
}
override void visit(BinExp e)
{
Type tb = e.type.toBasetype();
if (tb.ty != Tarray && tb.ty != Tsarray) // hoist scalar expressions
{
visit(cast(Expression) e);
}
else
{
// RPN
e.e1.accept(this);
e.e2.accept(this);
tiargs.push(new StringExp(Loc.initial, Token.toString(e.op)).expressionSemantic(sc));
}
}
}
scope v = new BuildArrayOpVisitor(sc, tiargs, args);
e.accept(v);
}
/***********************************************
* Some implicit casting can be performed by the _arrayOp template.
* Params:
* tfrom = type converting from
* tto = type converting to
* Returns:
* true if can be performed by _arrayOp
*/
bool isArrayOpImplicitCast(TypeDArray tfrom, TypeDArray tto)
{
const tyf = tfrom.nextOf().toBasetype().ty;
const tyt = tto .nextOf().toBasetype().ty;
return tyf == tyt ||
tyf == Tint32 && tyt == Tfloat64;
}
/***********************************************
* Test if expression is a unary array op.
*/
bool isUnaArrayOp(TOK op)
{
switch (op)
{
case TOK.negate:
case TOK.tilde:
return true;
default:
break;
}
return false;
}
/***********************************************
* Test if expression is a binary array op.
*/
bool isBinArrayOp(TOK op)
{
switch (op)
{
case TOK.add:
case TOK.min:
case TOK.mul:
case TOK.div:
case TOK.mod:
case TOK.xor:
case TOK.and:
case TOK.or:
case TOK.pow:
return true;
default:
break;
}
return false;
}
/***********************************************
* Test if expression is a binary assignment array op.
*/
bool isBinAssignArrayOp(TOK op)
{
switch (op)
{
case TOK.addAssign:
case TOK.minAssign:
case TOK.mulAssign:
case TOK.divAssign:
case TOK.modAssign:
case TOK.xorAssign:
case TOK.andAssign:
case TOK.orAssign:
case TOK.powAssign:
return true;
default:
break;
}
return false;
}
/***********************************************
* Test if operand is a valid array op operand.
*/
bool isArrayOpOperand(Expression e)
{
//printf("Expression.isArrayOpOperand() %s\n", e.toChars());
if (e.op == TOK.slice)
return true;
if (e.op == TOK.arrayLiteral)
{
Type t = e.type.toBasetype();
while (t.ty == Tarray || t.ty == Tsarray)
t = t.nextOf().toBasetype();
return (t.ty != Tvoid);
}
Type tb = e.type.toBasetype();
if (tb.ty == Tarray)
{
return (isUnaArrayOp(e.op) ||
isBinArrayOp(e.op) ||
isBinAssignArrayOp(e.op) ||
e.op == TOK.assign);
}
return false;
}
/***************************************************
* Print error message about invalid array operation.
* Params:
* e = expression with the invalid array operation
* Returns:
* instance of ErrorExp
*/
ErrorExp arrayOpInvalidError(Expression e)
{
e.error("invalid array operation `%s` (possible missing [])", e.toChars());
if (e.op == TOK.add)
checkPossibleAddCatError!(AddExp, CatExp)(e.isAddExp());
else if (e.op == TOK.addAssign)
checkPossibleAddCatError!(AddAssignExp, CatAssignExp)(e.isAddAssignExp());
return ErrorExp.get();
}
private void checkPossibleAddCatError(AddT, CatT)(AddT ae)
{
if (!ae.e2.type || ae.e2.type.ty != Tarray || !ae.e2.type.implicitConvTo(ae.e1.type))
return;
CatT ce = new CatT(ae.loc, ae.e1, ae.e2);
ae.errorSupplemental("did you mean to concatenate (`%s`) instead ?", ce.toChars());
}

57
gcc/d/dmd/arraytypes.d Normal file
View file

@ -0,0 +1,57 @@
/**
* Provide aliases for arrays of certain declarations or statements.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.arraytypes;
import dmd.dclass;
import dmd.declaration;
import dmd.dmodule;
import dmd.dsymbol;
import dmd.dtemplate;
import dmd.expression;
import dmd.func;
import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.root.array;
import dmd.root.rootobject;
import dmd.statement;
alias Strings = Array!(const(char)*);
alias Identifiers = Array!(Identifier);
alias TemplateParameters = Array!(TemplateParameter);
alias Expressions = Array!(Expression);
alias Statements = Array!(Statement);
alias BaseClasses = Array!(BaseClass*);
alias ClassDeclarations = Array!(ClassDeclaration);
alias Dsymbols = Array!(Dsymbol);
alias Objects = Array!(RootObject);
alias DtorDeclarations = Array!(DtorDeclaration);
alias FuncDeclarations = Array!(FuncDeclaration);
alias Parameters = Array!(Parameter);
alias Initializers = Array!(Initializer);
alias VarDeclarations = Array!(VarDeclaration);
alias Types = Array!(Type);
alias Catches = Array!(Catch);
alias StaticDtorDeclarations = Array!(StaticDtorDeclaration);
alias SharedStaticDtorDeclarations = Array!(SharedStaticDtorDeclaration);
alias AliasDeclarations = Array!(AliasDeclaration);
alias Modules = Array!(Module);
alias CaseStatements = Array!(CaseStatement);
alias ScopeStatements = Array!(ScopeStatement);
alias GotoCaseStatements = Array!(GotoCaseStatement);
alias ReturnStatements = Array!(ReturnStatement);
alias GotoStatements = Array!(GotoStatement);
alias TemplateInstances = Array!(TemplateInstance);
alias Ensures = Array!(Ensure);
alias Designators = Array!(Designator);
alias DesigInits = Array!(DesigInit);

View file

@ -27,6 +27,8 @@ typedef Array<class Dsymbol *> Dsymbols;
typedef Array<class RootObject *> Objects;
typedef Array<class DtorDeclaration *> DtorDeclarations;
typedef Array<class FuncDeclaration *> FuncDeclarations;
typedef Array<class Parameter *> Parameters;
@ -48,8 +50,6 @@ typedef Array<class AliasDeclaration *> AliasDeclarations;
typedef Array<class Module *> Modules;
typedef Array<struct File *> Files;
typedef Array<class CaseStatement *> CaseStatements;
typedef Array<class ScopeStatement *> ScopeStatements;
@ -63,3 +63,8 @@ typedef Array<class GotoStatement *> GotoStatements;
typedef Array<class TemplateInstance *> TemplateInstances;
typedef Array<struct Ensure> Ensures;
typedef Array<struct Designator> Designators;
typedef Array<struct DesigInit> DesigInits;

26
gcc/d/dmd/ast_node.d Normal file
View file

@ -0,0 +1,26 @@
/**
* Defines the base class for all nodes which are part of the AST.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.ast_node;
import dmd.root.rootobject : RootObject;
import dmd.visitor : Visitor;
/// The base class of all AST nodes.
extern (C++) abstract class ASTNode : RootObject
{
/**
* Visits this AST node using the given visitor.
*
* Params:
* v = the visitor to use when visiting this node
*/
abstract void accept(Visitor v);
}

102
gcc/d/dmd/astcodegen.d Normal file
View file

@ -0,0 +1,102 @@
/**
* Defines AST nodes for the code generation stage.
*
* Documentation: https://dlang.org/phobos/dmd_astcodegen.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/astcodegen.d
*/
module dmd.astcodegen;
struct ASTCodegen
{
public import dmd.aggregate;
public import dmd.aliasthis;
public import dmd.arraytypes;
public import dmd.attrib;
public import dmd.cond;
public import dmd.dclass;
public import dmd.declaration;
public import dmd.denum;
public import dmd.dimport;
public import dmd.dmodule;
public import dmd.dstruct;
public import dmd.dsymbol;
public import dmd.dtemplate;
public import dmd.dversion;
public import dmd.expression;
public import dmd.func;
public import dmd.hdrgen;
public import dmd.init;
public import dmd.initsem;
public import dmd.mtype;
public import dmd.nspace;
public import dmd.statement;
public import dmd.staticassert;
public import dmd.typesem;
public import dmd.ctfeexpr;
public import dmd.init : Designator;
alias initializerToExpression = dmd.initsem.initializerToExpression;
alias typeToExpression = dmd.typesem.typeToExpression;
alias UserAttributeDeclaration = dmd.attrib.UserAttributeDeclaration;
alias Ensure = dmd.func.Ensure; // workaround for bug in older DMD frontends
alias ErrorExp = dmd.expression.ErrorExp;
alias MODFlags = dmd.mtype.MODFlags;
alias Type = dmd.mtype.Type;
alias Parameter = dmd.mtype.Parameter;
alias Tarray = dmd.mtype.Tarray;
alias Taarray = dmd.mtype.Taarray;
alias Tbool = dmd.mtype.Tbool;
alias Tchar = dmd.mtype.Tchar;
alias Tdchar = dmd.mtype.Tdchar;
alias Tdelegate = dmd.mtype.Tdelegate;
alias Tenum = dmd.mtype.Tenum;
alias Terror = dmd.mtype.Terror;
alias Tfloat32 = dmd.mtype.Tfloat32;
alias Tfloat64 = dmd.mtype.Tfloat64;
alias Tfloat80 = dmd.mtype.Tfloat80;
alias Tfunction = dmd.mtype.Tfunction;
alias Tpointer = dmd.mtype.Tpointer;
alias Treference = dmd.mtype.Treference;
alias Tident = dmd.mtype.Tident;
alias Tint8 = dmd.mtype.Tint8;
alias Tint16 = dmd.mtype.Tint16;
alias Tint32 = dmd.mtype.Tint32;
alias Tint64 = dmd.mtype.Tint64;
alias Tsarray = dmd.mtype.Tsarray;
alias Tstruct = dmd.mtype.Tstruct;
alias Tuns8 = dmd.mtype.Tuns8;
alias Tuns16 = dmd.mtype.Tuns16;
alias Tuns32 = dmd.mtype.Tuns32;
alias Tuns64 = dmd.mtype.Tuns64;
alias Tvoid = dmd.mtype.Tvoid;
alias Twchar = dmd.mtype.Twchar;
alias Tnoreturn = dmd.mtype.Tnoreturn;
alias Timaginary32 = dmd.mtype.Timaginary32;
alias Timaginary64 = dmd.mtype.Timaginary64;
alias Timaginary80 = dmd.mtype.Timaginary80;
alias Tcomplex32 = dmd.mtype.Tcomplex32;
alias Tcomplex64 = dmd.mtype.Tcomplex64;
alias Tcomplex80 = dmd.mtype.Tcomplex80;
alias ParameterList = dmd.mtype.ParameterList;
alias VarArg = dmd.mtype.VarArg;
alias STC = dmd.declaration.STC;
alias Dsymbol = dmd.dsymbol.Dsymbol;
alias Dsymbols = dmd.dsymbol.Dsymbols;
alias Visibility = dmd.dsymbol.Visibility;
alias stcToBuffer = dmd.hdrgen.stcToBuffer;
alias linkageToChars = dmd.hdrgen.linkageToChars;
alias visibilityToChars = dmd.hdrgen.visibilityToChars;
alias isType = dmd.dtemplate.isType;
alias isExpression = dmd.dtemplate.isExpression;
alias isTuple = dmd.dtemplate.isTuple;
alias IgnoreErrors = dmd.dsymbol.IgnoreErrors;
alias PASS = dmd.dsymbol.PASS;
}

391
gcc/d/dmd/astenums.d Normal file
View file

@ -0,0 +1,391 @@
/**
* Defines enums common to dmd and dmd as parse library.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.astenums;
enum Sizeok : ubyte
{
none, /// size of aggregate is not yet able to compute
fwd, /// size of aggregate is ready to compute
inProcess, /// in the midst of computing the size
done, /// size of aggregate is set correctly
}
enum Baseok : ubyte
{
none, /// base classes not computed yet
start, /// in process of resolving base classes
done, /// all base classes are resolved
semanticdone, /// all base classes semantic done
}
enum MODFlags : int
{
const_ = 1, // type is const
immutable_ = 4, // type is immutable
shared_ = 2, // type is shared
wild = 8, // type is wild
wildconst = (MODFlags.wild | MODFlags.const_), // type is wild const
mutable = 0x10, // type is mutable (only used in wildcard matching)
}
alias MOD = ubyte;
enum STC : ulong // transfer changes to declaration.h
{
undefined_ = 0,
static_ = 1, /// `static`
extern_ = 2, /// `extern`
const_ = 4, /// `const`
final_ = 8, /// `final`
abstract_ = 0x10, /// `abstract`
parameter = 0x20, /// is function parameter
field = 0x40, /// is field of struct, union or class
override_ = 0x80, /// `override`
auto_ = 0x100, /// `auto`
synchronized_ = 0x200, /// `synchronized`
deprecated_ = 0x400, /// `deprecated`
in_ = 0x800, /// `in` parameter
out_ = 0x1000, /// `out` parameter
lazy_ = 0x2000, /// `lazy` parameter
foreach_ = 0x4000, /// variable for foreach loop
variadic = 0x8000, /// the `variadic` parameter in: T foo(T a, U b, V variadic...)
ctorinit = 0x1_0000, /// can only be set inside constructor
templateparameter = 0x2_0000, /// template parameter
ref_ = 0x4_0000, /// `ref`
scope_ = 0x8_0000, /// `scope`
maybescope = 0x10_0000, /// parameter might be `scope`
scopeinferred = 0x20_0000, /// `scope` has been inferred and should not be part of mangling, `scope_` must also be set
return_ = 0x40_0000, /// 'return ref' or 'return scope' for function parameters
returnScope = 0x80_0000, /// if `ref return scope` then resolve to `ref` and `return scope`
returninferred = 0x100_0000, /// `return` has been inferred and should not be part of mangling, `return_` must also be set
immutable_ = 0x200_0000, /// `immutable`
init = 0x400_0000, /// has explicit initializer
manifest = 0x800_0000, /// manifest constant
nodtor = 0x1000_0000, /// do not run destructor
nothrow_ = 0x2000_0000, /// `nothrow` meaning never throws exceptions
pure_ = 0x4000_0000, /// `pure` function
tls = 0x8000_0000, /// thread local
alias_ = 0x1_0000_0000, /// `alias` parameter
shared_ = 0x2_0000_0000, /// accessible from multiple threads
gshared = 0x4_0000_0000, /// accessible from multiple threads, but not typed as `shared`
wild = 0x8_0000_0000, /// for wild type constructor
property = 0x10_0000_0000, /// `@property`
safe = 0x20_0000_0000, /// `@safe`
trusted = 0x40_0000_0000, /// `@trusted`
system = 0x80_0000_0000, /// `@system`
ctfe = 0x100_0000_0000, /// can be used in CTFE, even if it is static
disable = 0x200_0000_0000, /// for functions that are not callable
result = 0x400_0000_0000, /// for result variables passed to out contracts
nodefaultctor = 0x800_0000_0000, /// must be set inside constructor
temp = 0x1000_0000_0000, /// temporary variable
rvalue = 0x2000_0000_0000, /// force rvalue for variables
nogc = 0x4000_0000_0000, /// `@nogc`
autoref = 0x8000_0000_0000, /// Mark for the already deduced `auto ref` parameter
inference = 0x1_0000_0000_0000, /// do attribute inference
exptemp = 0x2_0000_0000_0000, /// temporary variable that has lifetime restricted to an expression
future = 0x4_0000_0000_0000, /// introducing new base class function
local = 0x8_0000_0000_0000, /// do not forward (see dmd.dsymbol.ForwardingScopeDsymbol).
live = 0x10_0000_0000_0000, /// function `@live` attribute
register = 0x20_0000_0000_0000, /// `register` storage class (ImportC)
volatile_ = 0x40_0000_0000_0000, /// destined for volatile in the back end
safeGroup = STC.safe | STC.trusted | STC.system,
IOR = STC.in_ | STC.ref_ | STC.out_,
TYPECTOR = (STC.const_ | STC.immutable_ | STC.shared_ | STC.wild),
FUNCATTR = (STC.ref_ | STC.nothrow_ | STC.nogc | STC.pure_ | STC.property | STC.live |
safeGroup),
/* These are visible to the user, i.e. are expressed by the user
*/
visibleStorageClasses =
(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.const_ | STC.final_ | STC.abstract_ | STC.synchronized_ |
STC.deprecated_ | STC.future | STC.override_ | STC.lazy_ | STC.alias_ | STC.out_ | STC.in_ | STC.manifest |
STC.immutable_ | STC.shared_ | STC.wild | STC.nothrow_ | STC.nogc | STC.pure_ | STC.ref_ | STC.return_ | STC.tls | STC.gshared |
STC.property | STC.safeGroup | STC.disable | STC.local | STC.live),
/* These storage classes "flow through" to the inner scope of a Dsymbol
*/
flowThruAggregate = STC.safeGroup, /// for an AggregateDeclaration
flowThruFunction = ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.abstract_ | STC.deprecated_ | STC.override_ |
STC.TYPECTOR | STC.final_ | STC.tls | STC.gshared | STC.ref_ | STC.return_ | STC.property |
STC.nothrow_ | STC.pure_ | STC.safe | STC.trusted | STC.system), /// for a FuncDeclaration
}
/********
* Determine if it's the ambigous case of where `return` attaches to.
* Params:
* stc = STC flags
* Returns:
* true if (`ref` | `out`) and `scope` and `return`
*/
@safe pure @nogc nothrow
bool isRefReturnScope(const ulong stc)
{
return (stc & (STC.scope_ | STC.return_)) == (STC.scope_ | STC.return_) &&
stc & (STC.ref_ | STC.out_);
}
/* This is different from the one in declaration.d, make that fix a separate PR */
static if (0)
extern (C++) __gshared const(StorageClass) STCStorageClass =
(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.const_ | STC.final_ |
STC.abstract_ | STC.synchronized_ | STC.deprecated_ | STC.override_ | STC.lazy_ |
STC.alias_ | STC.out_ | STC.in_ | STC.manifest | STC.immutable_ | STC.shared_ |
STC.wild | STC.nothrow_ | STC.nogc | STC.pure_ | STC.ref_ | STC.return_ | STC.tls |
STC.gshared | STC.property | STC.live |
STC.safeGroup | STC.disable);
enum TY : ubyte
{
Tarray, // slice array, aka T[]
Tsarray, // static array, aka T[dimension]
Taarray, // associative array, aka T[type]
Tpointer,
Treference,
Tfunction,
Tident,
Tclass,
Tstruct,
Tenum,
Tdelegate,
Tnone,
Tvoid,
Tint8,
Tuns8,
Tint16,
Tuns16,
Tint32,
Tuns32,
Tint64,
Tuns64,
Tfloat32,
Tfloat64,
Tfloat80,
Timaginary32,
Timaginary64,
Timaginary80,
Tcomplex32,
Tcomplex64,
Tcomplex80,
Tbool,
Tchar,
Twchar,
Tdchar,
Terror,
Tinstance,
Ttypeof,
Ttuple,
Tslice,
Treturn,
Tnull,
Tvector,
Tint128,
Tuns128,
Ttraits,
Tmixin,
Tnoreturn,
Ttag,
TMAX
}
alias Tarray = TY.Tarray;
alias Tsarray = TY.Tsarray;
alias Taarray = TY.Taarray;
alias Tpointer = TY.Tpointer;
alias Treference = TY.Treference;
alias Tfunction = TY.Tfunction;
alias Tident = TY.Tident;
alias Tclass = TY.Tclass;
alias Tstruct = TY.Tstruct;
alias Tenum = TY.Tenum;
alias Tdelegate = TY.Tdelegate;
alias Tnone = TY.Tnone;
alias Tvoid = TY.Tvoid;
alias Tint8 = TY.Tint8;
alias Tuns8 = TY.Tuns8;
alias Tint16 = TY.Tint16;
alias Tuns16 = TY.Tuns16;
alias Tint32 = TY.Tint32;
alias Tuns32 = TY.Tuns32;
alias Tint64 = TY.Tint64;
alias Tuns64 = TY.Tuns64;
alias Tfloat32 = TY.Tfloat32;
alias Tfloat64 = TY.Tfloat64;
alias Tfloat80 = TY.Tfloat80;
alias Timaginary32 = TY.Timaginary32;
alias Timaginary64 = TY.Timaginary64;
alias Timaginary80 = TY.Timaginary80;
alias Tcomplex32 = TY.Tcomplex32;
alias Tcomplex64 = TY.Tcomplex64;
alias Tcomplex80 = TY.Tcomplex80;
alias Tbool = TY.Tbool;
alias Tchar = TY.Tchar;
alias Twchar = TY.Twchar;
alias Tdchar = TY.Tdchar;
alias Terror = TY.Terror;
alias Tinstance = TY.Tinstance;
alias Ttypeof = TY.Ttypeof;
alias Ttuple = TY.Ttuple;
alias Tslice = TY.Tslice;
alias Treturn = TY.Treturn;
alias Tnull = TY.Tnull;
alias Tvector = TY.Tvector;
alias Tint128 = TY.Tint128;
alias Tuns128 = TY.Tuns128;
alias Ttraits = TY.Ttraits;
alias Tmixin = TY.Tmixin;
alias Tnoreturn = TY.Tnoreturn;
alias Ttag = TY.Ttag;
alias TMAX = TY.TMAX;
enum TFlags
{
integral = 1,
floating = 2,
unsigned = 4,
real_ = 8,
imaginary = 0x10,
complex = 0x20,
}
enum PKG : int
{
unknown, /// not yet determined whether it's a package.d or not
module_, /// already determined that's an actual package.d
package_, /// already determined that's an actual package
}
enum ThreeState : ubyte
{
none, /// state is not yet computed
no, /// state is false
yes, /// state is true
}
enum TRUST : ubyte
{
default_ = 0,
system = 1, // @system (same as TRUST.default)
trusted = 2, // @trusted
safe = 3, // @safe
}
enum PURE : ubyte
{
impure = 0, // not pure at all
fwdref = 1, // it's pure, but not known which level yet
weak = 2, // no mutable globals are read or written
const_ = 3, // parameters are values or const
strong = 4, // parameters are values or immutable
}
// Whether alias this dependency is recursive or not
enum AliasThisRec : int
{
no = 0, // no alias this recursion
yes = 1, // alias this has recursive dependency
fwdref = 2, // not yet known
typeMask = 3, // mask to read no/yes/fwdref
tracing = 0x4, // mark in progress of implicitConvTo/deduceWild
tracingDT = 0x8, // mark in progress of deduceType
}
/***************
* Variadic argument lists
* https://dlang.org/spec/function.html#variadic
*/
enum VarArg : ubyte
{
none = 0, /// fixed number of arguments
variadic = 1, /// (T t, ...) can be C-style (core.stdc.stdarg) or D-style (core.vararg)
typesafe = 2, /// (T t ...) typesafe https://dlang.org/spec/function.html#typesafe_variadic_functions
/// or https://dlang.org/spec/function.html#typesafe_variadic_functions
}
/*************************
* Identify Statement types with this enum rather than
* virtual functions
*/
enum STMT : ubyte
{
Error,
Peel,
Exp, DtorExp,
Compile,
Compound, CompoundDeclaration, CompoundAsm,
UnrolledLoop,
Scope,
Forwarding,
While,
Do,
For,
Foreach,
ForeachRange,
If,
Conditional,
StaticForeach,
Pragma,
StaticAssert,
Switch,
Case,
CaseRange,
Default,
GotoDefault,
GotoCase,
SwitchError,
Return,
Break,
Continue,
Synchronized,
With,
TryCatch,
TryFinally,
ScopeGuard,
Throw,
Debug,
Goto,
Label,
Asm, InlineAsm, GccAsm,
Import,
}
/**********************
* Discriminant for which kind of initializer
*/
enum InitKind : ubyte
{
void_,
error,
struct_,
array,
exp,
C_,
}

File diff suppressed because it is too large Load diff

1518
gcc/d/dmd/attrib.d Normal file

File diff suppressed because it is too large Load diff

View file

@ -10,13 +10,10 @@
#pragma once
#include "root/port.h"
#include "dsymbol.h"
class Expression;
class Statement;
class LabelDsymbol;
class Initializer;
class Module;
class Condition;
class StaticForeach;
@ -27,12 +24,7 @@ class AttribDeclaration : public Dsymbol
public:
Dsymbols *decl; // array of Dsymbol's
AttribDeclaration(Dsymbols *decl);
virtual Dsymbols *include(Scope *sc);
int apply(Dsymbol_apply_ft_t fp, void *param);
static Scope *createNewScope(Scope *sc,
StorageClass newstc, LINK linkage, CPPMANGLE cppmangle, Prot protection,
int explicitProtection, AlignDeclaration *aligndecl, PINLINE inlining);
virtual Scope *newScope(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
@ -40,7 +32,7 @@ public:
void addComment(const utf8_t *comment);
const char *kind() const;
bool oneMember(Dsymbol **ps, Identifier *ident);
void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
bool hasPointers();
bool hasStaticCtorOrDtor();
void checkCtorConstInit();
@ -55,8 +47,7 @@ class StorageClassDeclaration : public AttribDeclaration
public:
StorageClass stc;
StorageClassDeclaration(StorageClass stc, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
StorageClassDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
bool oneMember(Dsymbol **ps, Identifier *ident);
void addMember(Scope *sc, ScopeDsymbol *sds);
@ -71,11 +62,9 @@ public:
Expression *msg;
const char *msgstr;
DeprecatedDeclaration(Expression *msg, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
DeprecatedDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
void setScope(Scope *sc);
const char *getMessage();
void accept(Visitor *v) { v->visit(this); }
};
@ -84,11 +73,10 @@ class LinkDeclaration : public AttribDeclaration
public:
LINK linkage;
LinkDeclaration(LINK p, Dsymbols *decl);
static LinkDeclaration *create(LINK p, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
static LinkDeclaration *create(const Loc &loc, LINK p, Dsymbols *decl);
LinkDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
const char *toChars();
const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
@ -97,40 +85,48 @@ class CPPMangleDeclaration : public AttribDeclaration
public:
CPPMANGLE cppmangle;
CPPMangleDeclaration(CPPMANGLE p, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
CPPMangleDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
const char *toChars();
void setScope(Scope *sc);
const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
class ProtDeclaration : public AttribDeclaration
class CPPNamespaceDeclaration : public AttribDeclaration
{
public:
Prot protection;
Identifiers* pkg_identifiers;
Expression *exp;
ProtDeclaration(Loc loc, Prot p, Dsymbols *decl);
ProtDeclaration(Loc loc, Identifiers* pkg_identifiers, Dsymbols *decl);
CPPNamespaceDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
Dsymbol *syntaxCopy(Dsymbol *s);
class VisibilityDeclaration : public AttribDeclaration
{
public:
Visibility visibility;
DArray<Identifier*> pkg_identifiers;
VisibilityDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
const char *kind() const;
const char *toPrettyChars(bool unused);
VisibilityDeclaration *isVisibilityDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
class AlignDeclaration : public AttribDeclaration
{
public:
Expression *ealign;
Expressions *alignExps;
structalign_t salign;
AlignDeclaration(Loc loc, Expression *ealign, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
AlignDeclaration(const Loc &loc, Expression *ealign, Dsymbols *decl);
AlignDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
structalign_t getAlignment(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};
@ -143,10 +139,9 @@ public:
unsigned anonstructsize; // size of anonymous struct
unsigned anonalignsize; // size of anonymous struct for alignment purposes
AnonDeclaration(Loc loc, bool isunion, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
AnonDeclaration *syntaxCopy(Dsymbol *s);
void setScope(Scope *sc);
void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
const char *kind() const;
AnonDeclaration *isAnonDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -157,9 +152,9 @@ class PragmaDeclaration : public AttribDeclaration
public:
Expressions *args; // array of Expression's
PragmaDeclaration(Loc loc, Identifier *ident, Expressions *args, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
PragmaDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
PINLINE evalPragmaInline(Scope* sc);
const char *kind() const;
void accept(Visitor *v) { v->visit(this); }
};
@ -170,8 +165,7 @@ public:
Condition *condition;
Dsymbols *elsedecl; // array of Dsymbol's for else block
ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl);
Dsymbol *syntaxCopy(Dsymbol *s);
ConditionalDeclaration *syntaxCopy(Dsymbol *s);
bool oneMember(Dsymbol **ps, Identifier *ident);
Dsymbols *include(Scope *sc);
void addComment(const utf8_t *comment);
@ -186,8 +180,7 @@ public:
bool addisdone;
bool onStack;
StaticIfDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl);
Dsymbol *syntaxCopy(Dsymbol *s);
StaticIfDeclaration *syntaxCopy(Dsymbol *s);
Dsymbols *include(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
@ -205,8 +198,7 @@ public:
bool cached;
Dsymbols *cache;
StaticForeachDeclaration(StaticForeach *sfe, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
StaticForeachDeclaration *syntaxCopy(Dsymbol *s);
bool oneMember(Dsymbol **ps, Identifier *ident);
Dsymbols *include(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
@ -222,7 +214,6 @@ class ForwardingAttribDeclaration : public AttribDeclaration
public:
ForwardingScopeDsymbol *sym;
ForwardingAttribDeclaration(Dsymbols *decl);
Scope *newScope(Scope *sc);
void addMember(Scope *sc, ScopeDsymbol *sds);
ForwardingAttribDeclaration *isForwardingAttribDeclaration() { return this; }
@ -239,8 +230,7 @@ public:
ScopeDsymbol *scopesym;
bool compiled;
CompileDeclaration(Loc loc, Expressions *exps);
Dsymbol *syntaxCopy(Dsymbol *s);
CompileDeclaration *syntaxCopy(Dsymbol *s);
void addMember(Scope *sc, ScopeDsymbol *sds);
void setScope(Scope *sc);
const char *kind() const;
@ -256,11 +246,9 @@ class UserAttributeDeclaration : public AttribDeclaration
public:
Expressions *atts;
UserAttributeDeclaration(Expressions *atts, Dsymbols *decl);
Dsymbol *syntaxCopy(Dsymbol *s);
UserAttributeDeclaration *syntaxCopy(Dsymbol *s);
Scope *newScope(Scope *sc);
void setScope(Scope *sc);
static Expressions *concat(Expressions *udas1, Expressions *udas2);
Expressions *getAttributes();
const char *kind() const;
void accept(Visitor *v) { v->visit(this); }

View file

@ -1,506 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
*/
#include "statement.h"
#include "declaration.h"
#include "aggregate.h"
#include "id.h"
/* Only valid after semantic analysis
* If 'mustNotThrow' is true, generate an error if it throws
*/
int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow)
{
class BlockExit : public Visitor
{
public:
FuncDeclaration *func;
bool mustNotThrow;
int result;
BlockExit(FuncDeclaration *func, bool mustNotThrow)
: func(func), mustNotThrow(mustNotThrow)
{
result = BEnone;
}
void visit(Statement *s)
{
printf("Statement::blockExit(%p)\n", s);
printf("%s\n", s->toChars());
assert(0);
result = BEany;
}
void visit(ErrorStatement *)
{
result = BEany;
}
void visit(ExpStatement *s)
{
result = BEfallthru;
if (s->exp)
{
if (s->exp->op == TOKhalt)
{
result = BEhalt;
return;
}
if (s->exp->op == TOKassert)
{
AssertExp *a = (AssertExp *)s->exp;
if (a->e1->isBool(false)) // if it's an assert(0)
{
result = BEhalt;
return;
}
}
if (s->exp->type->toBasetype()->isTypeNoreturn())
result = BEhalt;
if (canThrow(s->exp, func, mustNotThrow))
result |= BEthrow;
}
}
void visit(CompileStatement *)
{
assert(global.errors);
result = BEfallthru;
}
void visit(CompoundStatement *cs)
{
//printf("CompoundStatement::blockExit(%p) %d result = x%X\n", cs, cs->statements->length, result);
result = BEfallthru;
Statement *slast = NULL;
for (size_t i = 0; i < cs->statements->length; i++)
{
Statement *s = (*cs->statements)[i];
if (s)
{
//printf("result = x%x\n", result);
//printf("s: %s\n", s->toChars());
if (result & BEfallthru && slast)
{
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();
if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement() || sc->statement->isErrorStatement()))
;
else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement() || sd->statement->isErrorStatement()))
;
else
{
const char *gototype = s->isCaseStatement() ? "case" : "default";
s->deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
}
}
}
if (!(result & BEfallthru) && !s->comeFrom())
{
if (blockExit(s, func, mustNotThrow) != BEhalt && s->hasCode())
s->warning("statement is not reachable");
}
else
{
result &= ~BEfallthru;
result |= blockExit(s, func, mustNotThrow);
}
slast = s;
}
}
}
void visit(UnrolledLoopStatement *uls)
{
result = BEfallthru;
for (size_t i = 0; i < uls->statements->length; i++)
{
Statement *s = (*uls->statements)[i];
if (s)
{
int r = blockExit(s, func, mustNotThrow);
result |= r & ~(BEbreak | BEcontinue | BEfallthru);
if ((r & (BEfallthru | BEcontinue | BEbreak)) == 0)
result &= ~BEfallthru;
}
}
}
void visit(ScopeStatement *s)
{
//printf("ScopeStatement::blockExit(%p)\n", s->statement);
result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru;
}
void visit(WhileStatement *)
{
assert(global.errors);
result = BEfallthru;
}
void visit(DoStatement *s)
{
if (s->_body)
{
result = blockExit(s->_body, func, mustNotThrow);
if (result == BEbreak)
{
result = BEfallthru;
return;
}
if (result & BEcontinue)
result |= BEfallthru;
}
else
result = BEfallthru;
if (result & BEfallthru)
{
if (canThrow(s->condition, func, mustNotThrow))
result |= BEthrow;
if (!(result & BEbreak) && s->condition->isBool(true))
result &= ~BEfallthru;
}
result &= ~(BEbreak | BEcontinue);
}
void visit(ForStatement *s)
{
result = BEfallthru;
if (s->_init)
{
result = blockExit(s->_init, func, mustNotThrow);
if (!(result & BEfallthru))
return;
}
if (s->condition)
{
if (canThrow(s->condition, func, mustNotThrow))
result |= BEthrow;
if (s->condition->isBool(true))
result &= ~BEfallthru;
else if (s->condition->isBool(false))
return;
}
else
result &= ~BEfallthru; // the body must do the exiting
if (s->_body)
{
int r = blockExit(s->_body, func, mustNotThrow);
if (r & (BEbreak | BEgoto))
result |= BEfallthru;
result |= r & ~(BEfallthru | BEbreak | BEcontinue);
}
if (s->increment && canThrow(s->increment, func, mustNotThrow))
result |= BEthrow;
}
void visit(ForeachStatement *s)
{
result = BEfallthru;
if (canThrow(s->aggr, func, mustNotThrow))
result |= BEthrow;
if (s->_body)
result |= blockExit(s->_body, func, mustNotThrow) & ~(BEbreak | BEcontinue);
}
void visit(ForeachRangeStatement *)
{
assert(global.errors);
result = BEfallthru;
}
void visit(IfStatement *s)
{
//printf("IfStatement::blockExit(%p)\n", s);
result = BEnone;
if (canThrow(s->condition, func, mustNotThrow))
result |= BEthrow;
if (s->condition->isBool(true))
{
if (s->ifbody)
result |= blockExit(s->ifbody, func, mustNotThrow);
else
result |= BEfallthru;
}
else if (s->condition->isBool(false))
{
if (s->elsebody)
result |= blockExit(s->elsebody, func, mustNotThrow);
else
result |= BEfallthru;
}
else
{
if (s->ifbody)
result |= blockExit(s->ifbody, func, mustNotThrow);
else
result |= BEfallthru;
if (s->elsebody)
result |= blockExit(s->elsebody, func, mustNotThrow);
else
result |= BEfallthru;
}
//printf("IfStatement::blockExit(%p) = x%x\n", s, result);
}
void visit(ConditionalStatement *s)
{
result = blockExit(s->ifbody, func, mustNotThrow);
if (s->elsebody)
result |= blockExit(s->elsebody, func, mustNotThrow);
}
void visit(PragmaStatement *)
{
result = BEfallthru;
}
void visit(StaticAssertStatement *)
{
result = BEfallthru;
}
void visit(SwitchStatement *s)
{
result = BEnone;
if (canThrow(s->condition, func, mustNotThrow))
result |= BEthrow;
if (s->_body)
{
result |= blockExit(s->_body, func, mustNotThrow);
if (result & BEbreak)
{
result |= BEfallthru;
result &= ~BEbreak;
}
}
else
result |= BEfallthru;
}
void visit(CaseStatement *s)
{
result = blockExit(s->statement, func, mustNotThrow);
}
void visit(DefaultStatement *s)
{
result = blockExit(s->statement, func, mustNotThrow);
}
void visit(GotoDefaultStatement *)
{
result = BEgoto;
}
void visit(GotoCaseStatement *)
{
result = BEgoto;
}
void visit(SwitchErrorStatement *)
{
// Switch errors are non-recoverable
result = BEhalt;
}
void visit(ReturnStatement *s)
{
result = BEreturn;
if (s->exp && canThrow(s->exp, func, mustNotThrow))
result |= BEthrow;
}
void visit(BreakStatement *s)
{
//printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak);
result = s->ident ? BEgoto : BEbreak;
}
void visit(ContinueStatement *s)
{
result = s->ident ? BEgoto : BEcontinue;
}
void visit(SynchronizedStatement *s)
{
result = s->_body ? blockExit(s->_body, func, mustNotThrow) : BEfallthru;
}
void visit(WithStatement *s)
{
result = BEnone;
if (canThrow(s->exp, func, mustNotThrow))
result = BEthrow;
if (s->_body)
result |= blockExit(s->_body, func, mustNotThrow);
else
result |= BEfallthru;
}
void visit(TryCatchStatement *s)
{
assert(s->_body);
result = blockExit(s->_body, func, false);
int catchresult = 0;
for (size_t i = 0; i < s->catches->length; i++)
{
Catch *c = (*s->catches)[i];
if (c->type == Type::terror)
continue;
int cresult;
if (c->handler)
cresult = blockExit(c->handler, func, mustNotThrow);
else
cresult = BEfallthru;
/* If we're catching Object, then there is no throwing
*/
Identifier *id = c->type->toBasetype()->isClassHandle()->ident;
if (c->internalCatch && (cresult & BEfallthru))
{
// Bugzilla 11542: leave blockExit flags of the body
cresult &= ~BEfallthru;
}
else if (id == Id::Object || id == Id::Throwable)
{
result &= ~(BEthrow | BEerrthrow);
}
else if (id == Id::Exception)
{
result &= ~BEthrow;
}
catchresult |= cresult;
}
if (mustNotThrow && (result & BEthrow))
{
// now explain why this is nothrow
blockExit(s->_body, func, mustNotThrow);
}
result |= catchresult;
}
void visit(TryFinallyStatement *s)
{
result = BEfallthru;
if (s->_body)
result = blockExit(s->_body, func, false);
// check finally body as well, it may throw (bug #4082)
int finalresult = BEfallthru;
if (s->finalbody)
finalresult = blockExit(s->finalbody, func, false);
// If either body or finalbody halts
if (result == BEhalt)
finalresult = BEnone;
if (finalresult == BEhalt)
result = BEnone;
if (mustNotThrow)
{
// now explain why this is nothrow
if (s->_body && (result & BEthrow))
blockExit(s->_body, func, mustNotThrow);
if (s->finalbody && (finalresult & BEthrow))
blockExit(s->finalbody, func, mustNotThrow);
}
#if 0
// Bugzilla 13201: Mask to prevent spurious warnings for
// destructor call, exit of synchronized statement, etc.
if (result == BEhalt && finalresult != BEhalt && s->finalbody &&
s->finalbody->hasCode())
{
s->finalbody->warning("statement is not reachable");
}
#endif
if (!(finalresult & BEfallthru))
result &= ~BEfallthru;
result |= finalresult & ~BEfallthru;
}
void visit(ScopeGuardStatement *)
{
// At this point, this statement is just an empty placeholder
result = BEfallthru;
}
void visit(ThrowStatement *s)
{
if (s->internalThrow)
{
// Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow.
result = BEfallthru;
return;
}
Type *t = s->exp->type->toBasetype();
ClassDeclaration *cd = t->isClassHandle();
assert(cd);
if (cd == ClassDeclaration::errorException ||
ClassDeclaration::errorException->isBaseOf(cd, NULL))
{
result = BEerrthrow;
return;
}
if (mustNotThrow)
s->error("%s is thrown but not caught", s->exp->type->toChars());
result = BEthrow;
}
void visit(GotoStatement *)
{
//printf("GotoStatement::blockExit(%p)\n", s);
result = BEgoto;
}
void visit(LabelStatement *s)
{
//printf("LabelStatement::blockExit(%p)\n", s);
result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru;
if (s->breaks)
result |= BEfallthru;
}
void visit(CompoundAsmStatement *s)
{
if (mustNotThrow && !(s->stc & STCnothrow))
s->deprecation("asm statement is assumed to throw - mark it with `nothrow` if it does not");
// Assume the worst
result = BEfallthru | BEreturn | BEgoto | BEhalt;
if (!(s->stc & STCnothrow)) result |= BEthrow;
}
void visit(ImportStatement *)
{
result = BEfallthru;
}
};
if (!s)
return BEfallthru;
BlockExit be(func, mustNotThrow);
s->accept(&be);
return be.result;
}

537
gcc/d/dmd/blockexit.d Normal file
View file

@ -0,0 +1,537 @@
/**
* Find out in what ways control flow can exit a statement block.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.blockexit;
import core.stdc.stdio;
import dmd.arraytypes;
import dmd.astenums;
import dmd.canthrow;
import dmd.dclass;
import dmd.declaration;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.mtype;
import dmd.statement;
import dmd.tokens;
import dmd.visitor;
/**
* BE stands for BlockExit.
*
* It indicates if a statement does transfer control to another block.
* A block is a sequence of statements enclosed in { }
*/
enum BE : int
{
none = 0,
fallthru = 1,
throw_ = 2,
return_ = 4,
goto_ = 8,
halt = 0x10,
break_ = 0x20,
continue_ = 0x40,
errthrow = 0x80,
any = (fallthru | throw_ | return_ | goto_ | halt),
}
/*********************************************
* Determine mask of ways that a statement can exit.
*
* Only valid after semantic analysis.
* Params:
* s = statement to check for block exit status
* func = function that statement s is in
* mustNotThrow = generate an error if it throws
* Returns:
* BE.xxxx
*/
int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
{
extern (C++) final class BlockExit : Visitor
{
alias visit = Visitor.visit;
public:
FuncDeclaration func;
bool mustNotThrow;
int result;
extern (D) this(FuncDeclaration func, bool mustNotThrow)
{
this.func = func;
this.mustNotThrow = mustNotThrow;
result = BE.none;
}
override void visit(Statement s)
{
printf("Statement::blockExit(%p)\n", s);
printf("%s\n", s.toChars());
assert(0);
}
override void visit(ErrorStatement s)
{
result = BE.none;
}
override void visit(ExpStatement s)
{
result = BE.fallthru;
if (s.exp)
{
if (s.exp.op == TOK.halt)
{
result = BE.halt;
return;
}
if (s.exp.op == TOK.assert_)
{
AssertExp a = cast(AssertExp)s.exp;
if (a.e1.isBool(false)) // if it's an assert(0)
{
result = BE.halt;
return;
}
}
if (s.exp.type.toBasetype().isTypeNoreturn())
result = BE.halt;
if (canThrow(s.exp, func, mustNotThrow))
result |= BE.throw_;
}
}
override void visit(CompileStatement s)
{
assert(global.errors);
result = BE.fallthru;
}
override void visit(CompoundStatement cs)
{
//printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.dim, result);
result = BE.fallthru;
Statement slast = null;
foreach (s; *cs.statements)
{
if (s)
{
//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()))
{
// Allow if last case/default was empty
CaseStatement sc = slast.isCaseStatement();
DefaultStatement sd = slast.isDefaultStatement();
if (sc && (!sc.statement.hasCode() || sc.statement.isCaseStatement() || sc.statement.isErrorStatement()))
{
}
else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement()))
{
}
else
{
const(char)* gototype = s.isCaseStatement() ? "case" : "default";
s.deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
}
}
}
if (!(result & BE.fallthru) && !s.comeFrom())
{
if (blockExit(s, func, mustNotThrow) != BE.halt && s.hasCode() &&
s.loc != Loc.initial) // don't emit warning for generated code
s.warning("statement is not reachable");
}
else
{
result &= ~BE.fallthru;
result |= blockExit(s, func, mustNotThrow);
}
slast = s;
}
}
}
override void visit(UnrolledLoopStatement uls)
{
result = BE.fallthru;
foreach (s; *uls.statements)
{
if (s)
{
int r = blockExit(s, func, mustNotThrow);
result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru);
if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0)
result &= ~BE.fallthru;
}
}
}
override void visit(ScopeStatement s)
{
//printf("ScopeStatement::blockExit(%p)\n", s.statement);
result = blockExit(s.statement, func, mustNotThrow);
}
override void visit(WhileStatement s)
{
assert(global.errors);
result = BE.fallthru;
}
override void visit(DoStatement s)
{
if (s._body)
{
result = blockExit(s._body, func, mustNotThrow);
if (result == BE.break_)
{
result = BE.fallthru;
return;
}
if (result & BE.continue_)
result |= BE.fallthru;
}
else
result = BE.fallthru;
if (result & BE.fallthru)
{
if (canThrow(s.condition, func, mustNotThrow))
result |= BE.throw_;
if (!(result & BE.break_) && s.condition.isBool(true))
result &= ~BE.fallthru;
}
result &= ~(BE.break_ | BE.continue_);
}
override void visit(ForStatement s)
{
result = BE.fallthru;
if (s._init)
{
result = blockExit(s._init, func, mustNotThrow);
if (!(result & BE.fallthru))
return;
}
if (s.condition)
{
if (canThrow(s.condition, func, mustNotThrow))
result |= BE.throw_;
if (s.condition.isBool(true))
result &= ~BE.fallthru;
else if (s.condition.isBool(false))
return;
}
else
result &= ~BE.fallthru; // the body must do the exiting
if (s._body)
{
int r = blockExit(s._body, func, mustNotThrow);
if (r & (BE.break_ | BE.goto_))
result |= BE.fallthru;
result |= r & ~(BE.fallthru | BE.break_ | BE.continue_);
}
if (s.increment && canThrow(s.increment, func, mustNotThrow))
result |= BE.throw_;
}
override void visit(ForeachStatement s)
{
result = BE.fallthru;
if (canThrow(s.aggr, func, mustNotThrow))
result |= BE.throw_;
if (s._body)
result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_);
}
override void visit(ForeachRangeStatement s)
{
assert(global.errors);
result = BE.fallthru;
}
override void visit(IfStatement s)
{
//printf("IfStatement::blockExit(%p)\n", s);
result = BE.none;
if (canThrow(s.condition, func, mustNotThrow))
result |= BE.throw_;
if (s.condition.isBool(true))
{
result |= blockExit(s.ifbody, func, mustNotThrow);
}
else if (s.condition.isBool(false))
{
result |= blockExit(s.elsebody, func, mustNotThrow);
}
else
{
result |= blockExit(s.ifbody, func, mustNotThrow);
result |= blockExit(s.elsebody, func, mustNotThrow);
}
//printf("IfStatement::blockExit(%p) = x%x\n", s, result);
}
override void visit(ConditionalStatement s)
{
result = blockExit(s.ifbody, func, mustNotThrow);
if (s.elsebody)
result |= blockExit(s.elsebody, func, mustNotThrow);
}
override void visit(PragmaStatement s)
{
result = BE.fallthru;
}
override void visit(StaticAssertStatement s)
{
result = BE.fallthru;
}
override void visit(SwitchStatement s)
{
result = BE.none;
if (canThrow(s.condition, func, mustNotThrow))
result |= BE.throw_;
if (s._body)
{
result |= blockExit(s._body, func, mustNotThrow);
if (result & BE.break_)
{
result |= BE.fallthru;
result &= ~BE.break_;
}
}
else
result |= BE.fallthru;
}
override void visit(CaseStatement s)
{
result = blockExit(s.statement, func, mustNotThrow);
}
override void visit(DefaultStatement s)
{
result = blockExit(s.statement, func, mustNotThrow);
}
override void visit(GotoDefaultStatement s)
{
result = BE.goto_;
}
override void visit(GotoCaseStatement s)
{
result = BE.goto_;
}
override void visit(SwitchErrorStatement s)
{
// Switch errors are non-recoverable
result = BE.halt;
}
override void visit(ReturnStatement s)
{
result = BE.return_;
if (s.exp && canThrow(s.exp, func, mustNotThrow))
result |= BE.throw_;
}
override void visit(BreakStatement s)
{
//printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_);
result = s.ident ? BE.goto_ : BE.break_;
}
override void visit(ContinueStatement s)
{
result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_;
}
override void visit(SynchronizedStatement s)
{
result = blockExit(s._body, func, mustNotThrow);
}
override void visit(WithStatement s)
{
result = BE.none;
if (canThrow(s.exp, func, mustNotThrow))
result = BE.throw_;
result |= blockExit(s._body, func, mustNotThrow);
}
override void visit(TryCatchStatement s)
{
assert(s._body);
result = blockExit(s._body, func, false);
int catchresult = 0;
foreach (c; *s.catches)
{
if (c.type == Type.terror)
continue;
int cresult = blockExit(c.handler, func, mustNotThrow);
/* If we're catching Object, then there is no throwing
*/
Identifier id = c.type.toBasetype().isClassHandle().ident;
if (c.internalCatch && (cresult & BE.fallthru))
{
// https://issues.dlang.org/show_bug.cgi?id=11542
// leave blockExit flags of the body
cresult &= ~BE.fallthru;
}
else if (id == Id.Object || id == Id.Throwable)
{
result &= ~(BE.throw_ | BE.errthrow);
}
else if (id == Id.Exception)
{
result &= ~BE.throw_;
}
catchresult |= cresult;
}
if (mustNotThrow && (result & BE.throw_))
{
// now explain why this is nothrow
blockExit(s._body, func, mustNotThrow);
}
result |= catchresult;
}
override void visit(TryFinallyStatement s)
{
result = BE.fallthru;
if (s._body)
result = blockExit(s._body, func, false);
// check finally body as well, it may throw (bug #4082)
int finalresult = BE.fallthru;
if (s.finalbody)
finalresult = blockExit(s.finalbody, func, false);
// If either body or finalbody halts
if (result == BE.halt)
finalresult = BE.none;
if (finalresult == BE.halt)
result = BE.none;
if (mustNotThrow)
{
// now explain why this is nothrow
if (s._body && (result & BE.throw_))
blockExit(s._body, func, mustNotThrow);
if (s.finalbody && (finalresult & BE.throw_))
blockExit(s.finalbody, func, mustNotThrow);
}
version (none)
{
// https://issues.dlang.org/show_bug.cgi?id=13201
// Mask to prevent spurious warnings for
// destructor call, exit of synchronized statement, etc.
if (result == BE.halt && finalresult != BE.halt && s.finalbody && s.finalbody.hasCode())
{
s.finalbody.warning("statement is not reachable");
}
}
if (!(finalresult & BE.fallthru))
result &= ~BE.fallthru;
result |= finalresult & ~BE.fallthru;
}
override void visit(ScopeGuardStatement s)
{
// At this point, this statement is just an empty placeholder
result = BE.fallthru;
}
override void visit(ThrowStatement s)
{
if (s.internalThrow)
{
// https://issues.dlang.org/show_bug.cgi?id=8675
// Allow throwing 'Throwable' object even if mustNotThrow.
result = BE.fallthru;
return;
}
Type t = s.exp.type.toBasetype();
ClassDeclaration cd = t.isClassHandle();
assert(cd);
if (cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null))
{
result = BE.errthrow;
return;
}
if (mustNotThrow)
s.error("`%s` is thrown but not caught", s.exp.type.toChars());
result = BE.throw_;
}
override void visit(GotoStatement s)
{
//printf("GotoStatement::blockExit(%p)\n", s);
result = BE.goto_;
}
override void visit(LabelStatement s)
{
//printf("LabelStatement::blockExit(%p)\n", s);
result = blockExit(s.statement, func, mustNotThrow);
if (s.breaks)
result |= BE.fallthru;
}
override void visit(CompoundAsmStatement s)
{
// Assume the worst
result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt;
if (!(s.stc & STC.nothrow_))
{
if (mustNotThrow && !(s.stc & STC.nothrow_))
s.deprecation("`asm` statement is assumed to throw - mark it with `nothrow` if it does not");
else
result |= BE.throw_;
}
}
override void visit(ImportStatement s)
{
result = BE.fallthru;
}
}
if (!s)
return BE.fallthru;
scope BlockExit be = new BlockExit(func, mustNotThrow);
s.accept(be);
return be.result;
}

33
gcc/d/dmd/builtin.d Normal file
View file

@ -0,0 +1,33 @@
/**
* Implement CTFE for intrinsic (builtin) functions.
*
* Currently includes functions from `std.math`, `core.math` and `core.bitop`.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.builtin;
import core.stdc.math;
import core.stdc.string;
import dmd.arraytypes;
import dmd.expression;
import dmd.func;
import dmd.globals;
/**********************************
* Determine if function is a builtin one that we can
* evaluate at compile time.
*/
public extern (C++) BUILTIN isBuiltin(FuncDeclaration fd);
/**************************************
* Evaluate builtin function.
* Return result; NULL if cannot evaluate it.
*/
public extern (C++) Expression eval_builtin(Loc loc, FuncDeclaration fd, Expressions* arguments);

View file

@ -1,316 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c
*/
#include "root/dsystem.h"
#include "mars.h"
#include "init.h"
#include "expression.h"
#include "template.h"
#include "statement.h"
#include "mtype.h"
#include "utf.h"
#include "declaration.h"
#include "aggregate.h"
#include "scope.h"
#include "attrib.h"
#include "tokens.h"
bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow);
bool walkPostorder(Expression *e, StoppableVisitor *v);
/********************************************
* Returns true if the expression may throw exceptions.
* If 'mustNotThrow' is true, generate an error if it throws
*/
bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow)
{
//printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
// stop walking if we determine this expression can throw
class CanThrow : public StoppableVisitor
{
FuncDeclaration *func;
bool mustNotThrow;
public:
CanThrow(FuncDeclaration *func, bool mustNotThrow)
: func(func), mustNotThrow(mustNotThrow)
{
}
void visit(Expression *)
{
}
void visit(DeclarationExp *de)
{
stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow);
}
void visit(CallExp *ce)
{
if (global.errors && !ce->e1->type)
return; // error recovery
/* If calling a function or delegate that is typed as nothrow,
* then this expression cannot throw.
* Note that pure functions can throw.
*/
Type *t = ce->e1->type->toBasetype();
if (ce->f && ce->f == func)
return;
if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow)
return;
if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow)
return;
if (mustNotThrow)
{
if (ce->f)
{
ce->error("%s `%s` is not nothrow",
ce->f->kind(), ce->f->toPrettyChars());
}
else
{
Expression *e1 = ce->e1;
if (e1->op == TOKstar) // print 'fp' if e1 is (*fp)
e1 = ((PtrExp *)e1)->e1;
ce->error("`%s` is not nothrow", e1->toChars());
}
}
stop = true;
}
void visit(NewExp *ne)
{
if (ne->member)
{
if (ne->allocator)
{
// Bugzilla 14407
Type *t = ne->allocator->type->toBasetype();
if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
{
if (mustNotThrow)
{
ne->error("%s `%s` is not nothrow",
ne->allocator->kind(), ne->allocator->toPrettyChars());
}
stop = true;
}
}
// See if constructor call can throw
Type *t = ne->member->type->toBasetype();
if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
{
if (mustNotThrow)
{
ne->error("%s `%s` is not nothrow",
ne->member->kind(), ne->member->toPrettyChars());
}
stop = true;
}
}
// regard storage allocation failures as not recoverable
}
void visit(DeleteExp *de)
{
Type *tb = de->e1->type->toBasetype();
AggregateDeclaration *ad = NULL;
switch (tb->ty)
{
case Tclass:
ad = ((TypeClass *)tb)->sym;
break;
case Tpointer:
tb = ((TypePointer *)tb)->next->toBasetype();
if (tb->ty == Tstruct)
ad = ((TypeStruct *)tb)->sym;
break;
case Tarray:
{
Type *tv = tb->nextOf()->baseElemOf();
if (tv->ty == Tstruct)
{
ad = ((TypeStruct *)tv)->sym;
break;
}
}
default:
break;
}
if (!ad)
return;
if (ad->dtor)
{
Type *t = ad->dtor->type->toBasetype();
if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
{
if (mustNotThrow)
{
de->error("%s `%s` is not nothrow",
ad->dtor->kind(), ad->dtor->toPrettyChars());
}
stop = true;
}
}
if (ad->aggDelete && tb->ty != Tarray)
{
Type *t = ad->aggDelete->type;
if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
{
if (mustNotThrow)
{
de->error("%s `%s` is not nothrow",
ad->aggDelete->kind(), ad->aggDelete->toPrettyChars());
}
stop = true;
}
}
}
void visit(AssignExp *ae)
{
// blit-init cannot throw
if (ae->op == TOKblit)
return;
/* Element-wise assignment could invoke postblits.
*/
Type *t;
if (ae->type->toBasetype()->ty == Tsarray)
{
if (!ae->e2->isLvalue())
return;
t = ae->type;
}
else if (ae->e1->op == TOKslice)
t = ((SliceExp *)ae->e1)->e1->type;
else
return;
Type *tv = t->baseElemOf();
if (tv->ty != Tstruct)
return;
StructDeclaration *sd = ((TypeStruct *)tv)->sym;
if (!sd->postblit || sd->postblit->type->ty != Tfunction)
return;
if (((TypeFunction *)sd->postblit->type)->isnothrow)
;
else
{
if (mustNotThrow)
{
ae->error("%s `%s` is not nothrow",
sd->postblit->kind(), sd->postblit->toPrettyChars());
}
stop = true;
}
}
void visit(NewAnonClassExp *)
{
assert(0); // should have been lowered by semantic()
}
};
CanThrow ct(func, mustNotThrow);
return walkPostorder(e, &ct);
}
/**************************************
* Does symbol, when initialized, throw?
* Mirrors logic in Dsymbol_toElem().
*/
bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow)
{
AttribDeclaration *ad;
VarDeclaration *vd;
TemplateMixin *tm;
TupleDeclaration *td;
//printf("Dsymbol_toElem() %s\n", s->toChars());
ad = s->isAttribDeclaration();
if (ad)
{
Dsymbols *decl = ad->include(NULL);
if (decl && decl->length)
{
for (size_t i = 0; i < decl->length; i++)
{
s = (*decl)[i];
if (Dsymbol_canThrow(s, func, mustNotThrow))
return true;
}
}
}
else if ((vd = s->isVarDeclaration()) != NULL)
{
s = s->toAlias();
if (s != vd)
return Dsymbol_canThrow(s, func, mustNotThrow);
if (vd->storage_class & STCmanifest)
;
else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared))
;
else
{
if (vd->_init)
{
ExpInitializer *ie = vd->_init->isExpInitializer();
if (ie && canThrow(ie->exp, func, mustNotThrow))
return true;
}
if (vd->needsScopeDtor())
return canThrow(vd->edtor, func, mustNotThrow);
}
}
else if ((tm = s->isTemplateMixin()) != NULL)
{
//printf("%s\n", tm->toChars());
if (tm->members)
{
for (size_t i = 0; i < tm->members->length; i++)
{
Dsymbol *sm = (*tm->members)[i];
if (Dsymbol_canThrow(sm, func, mustNotThrow))
return true;
}
}
}
else if ((td = s->isTupleDeclaration()) != NULL)
{
for (size_t i = 0; i < td->objects->length; i++)
{
RootObject *o = (*td->objects)[i];
if (o->dyncast() == DYNCAST_EXPRESSION)
{
Expression *eo = (Expression *)o;
if (eo->op == TOKdsymbol)
{
DsymbolExp *se = (DsymbolExp *)eo;
if (Dsymbol_canThrow(se->s, func, mustNotThrow))
return true;
}
}
}
}
return false;
}

244
gcc/d/dmd/canthrow.d Normal file
View file

@ -0,0 +1,244 @@
/**
* Perform checks for `nothrow`.
*
* Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions)
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.canthrow;
import dmd.aggregate;
import dmd.apply;
import dmd.arraytypes;
import dmd.attrib;
import dmd.astenums;
import dmd.declaration;
import dmd.dsymbol;
import dmd.expression;
import dmd.func;
import dmd.globals;
import dmd.init;
import dmd.mtype;
import dmd.root.rootobject;
import dmd.tokens;
import dmd.visitor;
/********************************************
* Returns true if the expression may throw exceptions.
* If 'mustNotThrow' is true, generate an error if it throws
*/
extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
{
//printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
// stop walking if we determine this expression can throw
extern (C++) final class CanThrow : StoppableVisitor
{
alias visit = typeof(super).visit;
FuncDeclaration func;
bool mustNotThrow;
public:
extern (D) this(FuncDeclaration func, bool mustNotThrow)
{
this.func = func;
this.mustNotThrow = mustNotThrow;
}
void checkFuncThrows(Expression e, FuncDeclaration f)
{
auto tf = f.type.toBasetype().isTypeFunction();
if (tf && !tf.isnothrow)
{
if (mustNotThrow)
{
e.error("%s `%s` is not `nothrow`",
f.kind(), f.toPrettyChars());
e.checkOverridenDtor(null, f, dd => dd.type.toTypeFunction().isnothrow, "not nothrow");
}
stop = true; // if any function throws, then the whole expression throws
}
}
override void visit(Expression)
{
}
override void visit(DeclarationExp de)
{
stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow);
}
override void visit(CallExp ce)
{
if (ce.inDebugStatement)
return;
if (global.errors && !ce.e1.type)
return; // error recovery
/* If calling a function or delegate that is typed as nothrow,
* then this expression cannot throw.
* Note that pure functions can throw.
*/
if (ce.f && ce.f == func)
return;
Type t = ce.e1.type.toBasetype();
auto tf = t.isTypeFunction();
if (tf && tf.isnothrow)
return;
else
{
auto td = t.isTypeDelegate();
if (td && td.nextOf().isTypeFunction().isnothrow)
return;
}
if (ce.f)
checkFuncThrows(ce, ce.f);
else if (mustNotThrow)
{
auto e1 = ce.e1;
if (auto pe = e1.isPtrExp()) // print 'fp' if e1 is (*fp)
e1 = pe.e1;
ce.error("`%s` is not `nothrow`", e1.toChars());
}
stop = true;
}
override void visit(NewExp ne)
{
if (ne.member)
{
// See if constructor call can throw
checkFuncThrows(ne, ne.member);
}
// regard storage allocation failures as not recoverable
}
override void visit(DeleteExp de)
{
Type tb = de.e1.type.toBasetype();
AggregateDeclaration ad = null;
switch (tb.ty)
{
case Tclass:
ad = tb.isTypeClass().sym;
break;
case Tpointer:
case Tarray:
auto ts = tb.nextOf().baseElemOf().isTypeStruct();
if (!ts)
return;
ad = ts.sym;
break;
default:
assert(0); // error should have been detected by semantic()
}
if (ad.dtor)
checkFuncThrows(de, ad.dtor);
}
override void visit(AssignExp ae)
{
// blit-init cannot throw
if (ae.op == TOK.blit)
return;
/* Element-wise assignment could invoke postblits.
*/
Type t;
if (ae.type.toBasetype().ty == Tsarray)
{
if (!ae.e2.isLvalue())
return;
t = ae.type;
}
else if (auto se = ae.e1.isSliceExp())
t = se.e1.type;
else
return;
if (auto ts = t.baseElemOf().isTypeStruct())
if (auto postblit = ts.sym.postblit)
checkFuncThrows(ae, postblit);
}
override void visit(NewAnonClassExp)
{
assert(0); // should have been lowered by semantic()
}
}
scope CanThrow ct = new CanThrow(func, mustNotThrow);
return walkPostorder(e, ct);
}
/**************************************
* Does symbol, when initialized, throw?
* Mirrors logic in Dsymbol_toElem().
*/
private bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
{
int symbolDg(Dsymbol s)
{
return Dsymbol_canThrow(s, func, mustNotThrow);
}
//printf("Dsymbol_toElem() %s\n", s.toChars());
if (auto vd = s.isVarDeclaration())
{
s = s.toAlias();
if (s != vd)
return Dsymbol_canThrow(s, func, mustNotThrow);
if (vd.storage_class & STC.manifest)
{
}
else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared))
{
}
else
{
if (vd._init)
{
if (auto ie = vd._init.isExpInitializer())
if (canThrow(ie.exp, func, mustNotThrow))
return true;
}
if (vd.needsScopeDtor())
return canThrow(vd.edtor, func, mustNotThrow);
}
}
else if (auto ad = s.isAttribDeclaration())
{
return ad.include(null).foreachDsymbol(&symbolDg) != 0;
}
else if (auto tm = s.isTemplateMixin())
{
return tm.members.foreachDsymbol(&symbolDg) != 0;
}
else if (auto td = s.isTupleDeclaration())
{
for (size_t i = 0; i < td.objects.dim; i++)
{
RootObject o = (*td.objects)[i];
if (o.dyncast() == DYNCAST.expression)
{
Expression eo = cast(Expression)o;
if (auto se = eo.isDsymbolExp())
{
if (Dsymbol_canThrow(se.s, func, mustNotThrow))
return true;
}
}
}
}
return false;
}

View file

@ -1,985 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
*/
// Check the arguments to `printf` and `scanf` against the `format` string.
#include "root/dsystem.h"
#include "root/dcompat.h"
#include "arraytypes.h"
#include "cond.h"
#include "errors.h"
#include "expression.h"
#include "globals.h"
#include "identifier.h"
#include "mtype.h"
#include "target.h"
/* Different kinds of formatting specifications, variations we don't
care about are merged. (Like we don't care about the difference between
f, e, g, a, etc.)
For `scanf`, every format is a pointer.
*/
enum Format
{
Format_d, // int
Format_hhd, // signed char
Format_hd, // short int
Format_ld, // long int
Format_lld, // long long int
Format_jd, // intmax_t
Format_zd, // size_t
Format_td, // ptrdiff_t
Format_u, // unsigned int
Format_hhu, // unsigned char
Format_hu, // unsigned short int
Format_lu, // unsigned long int
Format_llu, // unsigned long long int
Format_ju, // uintmax_t
Format_g, // float (scanf) / double (printf)
Format_lg, // double (scanf)
Format_Lg, // long double (both)
Format_s, // char string (both)
Format_ls, // wchar_t string (both)
Format_c, // char (printf)
Format_lc, // wint_t (printf)
Format_p, // pointer
Format_n, // pointer to int
Format_hhn, // pointer to signed char
Format_hn, // pointer to short
Format_ln, // pointer to long int
Format_lln, // pointer to long long int
Format_jn, // pointer to intmax_t
Format_zn, // pointer to size_t
Format_tn, // pointer to ptrdiff_t
Format_GNU_a, // GNU ext. : address to a string with no maximum size (scanf)
Format_GNU_m, // GNU ext. : string corresponding to the error code in errno (printf) / length modifier (scanf)
Format_percent, // %% (i.e. no argument)
Format_error, // invalid format specification
};
/**************************************
* Parse the *length specifier* and the *specifier* of the following form:
* `[length]specifier`
*
* Params:
* format = format string
* idx = index of of start of format specifier,
* which gets updated to index past the end of it,
* even if `Format_error` is returned
* genSpecifier = Generic specifier. For instance, it will be set to `d` if the
* format is `hdd`.
* Returns:
* Format
*/
static Format parseGenericFormatSpecifier(const char *format,
size_t &idx, char &genSpecifier, bool useGNUExts =
findCondition(global.versionids, Identifier::idPool("CRuntime_Glibc")))
{
genSpecifier = 0;
const size_t length = strlen(format);
/* Read the `length modifier`
*/
const char lm = format[idx];
bool lm1= false; // if jztL
bool lm2= false; // if `hh` or `ll`
if (lm == 'j' ||
lm == 'z' ||
lm == 't' ||
lm == 'L')
{
++idx;
if (idx == length)
return Format_error;
lm1 = true;
}
else if (lm == 'h' || lm == 'l')
{
++idx;
if (idx == length)
return Format_error;
lm2 = lm == format[idx];
if (lm2)
{
++idx;
if (idx == length)
return Format_error;
}
}
/* Read the `specifier`
*/
Format specifier;
const char sc = format[idx];
genSpecifier = sc;
switch (sc)
{
case 'd':
case 'i':
if (lm == 'L')
specifier = Format_error;
else
specifier = lm == 'h' && lm2 ? Format_hhd :
lm == 'h' ? Format_hd :
lm == 'l' && lm2 ? Format_lld :
lm == 'l' ? Format_ld :
lm == 'j' ? Format_jd :
lm == 'z' ? Format_zd :
lm == 't' ? Format_td :
Format_d;
break;
case 'u':
case 'o':
case 'x':
case 'X':
if (lm == 'L')
specifier = Format_error;
else
specifier = lm == 'h' && lm2 ? Format_hhu :
lm == 'h' ? Format_hu :
lm == 'l' && lm2 ? Format_llu :
lm == 'l' ? Format_lu :
lm == 'j' ? Format_ju :
lm == 'z' ? Format_zd :
lm == 't' ? Format_td :
Format_u;
break;
case 'a':
if (useGNUExts)
{
// https://www.gnu.org/software/libc/manual/html_node/Dynamic-String-Input.html
specifier = Format_GNU_a;
break;
}
/* fall through */
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
case 'A':
if (lm == 'L')
specifier = Format_Lg;
else if (lm1 || lm2 || lm == 'h')
specifier = Format_error;
else
specifier = lm == 'l' ? Format_lg : Format_g;
break;
case 'c':
if (lm1 || lm2 || lm == 'h')
specifier = Format_error;
else
specifier = lm == 'l' ? Format_lc : Format_c;
break;
case 's':
if (lm1 || lm2 || lm == 'h')
specifier = Format_error;
else
specifier = lm == 'l' ? Format_ls : Format_s;
break;
case 'p':
if (lm1 || lm2 || lm == 'h' || lm == 'l')
specifier = Format_error;
else
specifier = Format_p;
break;
case 'n':
if (lm == 'L')
specifier = Format_error;
else
specifier = lm == 'l' && lm2 ? Format_lln :
lm == 'l' ? Format_ln :
lm == 'h' && lm2 ? Format_hhn :
lm == 'h' ? Format_hn :
lm == 'j' ? Format_jn :
lm == 'z' ? Format_zn :
lm == 't' ? Format_tn :
Format_n;
break;
case 'm':
if (useGNUExts)
{
// http://www.gnu.org/software/libc/manual/html_node/Other-Output-Conversions.html
specifier = Format_GNU_m;
break;
}
goto Ldefault;
default:
Ldefault:
specifier = Format_error;
break;
}
++idx;
return specifier; // success
}
Format formatError(size_t &idx, size_t i)
{
idx = i;
return Format_error;
}
/**************************************
* Parse the *format specifier* which is of the form:
*
* `%[*][width][length]specifier`
*
* Params:
* format = format string
* idx = index of `%` of start of format specifier,
* which gets updated to index past the end of it,
* even if `Format_error` is returned
* asterisk = set if there is a `*` sub-specifier
* Returns:
* Format
*/
static Format parseScanfFormatSpecifier(const char *format, size_t &idx,
bool &asterisk)
{
asterisk = false;
size_t i = idx;
assert(format[i] == '%');
const size_t length = strlen(format);
++i;
if (i == length)
return formatError(idx, i);
if (format[i] == '%')
{
idx = i + 1;
return Format_percent;
}
// * sub-specifier
if (format[i] == '*')
{
++i;
if (i == length)
return formatError(idx, i);
asterisk = true;
}
// fieldWidth
while (isdigit(format[i]))
{
i++;
if (i == length)
return formatError(idx, i);
}
/* Read the scanset
* A scanset can be anything, so we just check that it is paired
*/
if (format[i] == '[')
{
while (i < length)
{
if (format[i] == ']')
break;
++i;
}
// no `]` found
if (i == length)
return formatError(idx, i);
++i;
// no specifier after `]`
// it could be mixed with the one above, but then idx won't have the right index
if (i == length)
return formatError(idx, i);
}
/* Read the specifier
*/
char genSpec;
Format specifier = parseGenericFormatSpecifier(format, i, genSpec);
if (specifier == Format_error)
return formatError(idx, i);
idx = i;
return specifier; // success
}
/**************************************
* Parse the *format specifier* which is of the form:
*
* `%[flags][field width][.precision][length modifier]specifier`
*
* Params:
* format = format string
* idx = index of `%` of start of format specifier,
* which gets updated to index past the end of it,
* even if `Format_error` is returned
* widthStar = set if * for width
* precisionStar = set if * for precision
* Returns:
* Format
*/
static Format parsePrintfFormatSpecifier(const char *format, size_t &idx,
bool &widthStar, bool &precisionStar)
{
widthStar = false;
precisionStar = false;
size_t i = idx;
assert(format[i] == '%');
const size_t format_length = strlen(format);
const size_t length = format_length;
bool hash = false;
bool zero = false;
bool flags = false;
bool width = false;
bool precision = false;
++i;
if (i == length)
return formatError(idx, i);
if (format[i] == '%')
{
idx = i + 1;
return Format_percent;
}
/* Read the `flags`
*/
while (1)
{
const char c = format[i];
if (c == '-' ||
c == '+' ||
c == ' ')
{
flags = true;
}
else if (c == '#')
{
hash = true;
}
else if (c == '0')
{
zero = true;
}
else
break;
++i;
if (i == length)
return formatError(idx, i);
}
/* Read the `field width`
*/
{
const char c = format[i];
if (c == '*')
{
width = true;
widthStar = true;
++i;
if (i == length)
return formatError(idx, i);
}
else if ('1' <= c && c <= '9')
{
width = true;
++i;
if (i == length)
return formatError(idx, i);
while ('0' <= format[i] && format[i] <= '9')
{
++i;
if (i == length)
return formatError(idx, i);
}
}
}
/* Read the `precision`
*/
if (format[i] == '.')
{
precision = true;
++i;
if (i == length)
return formatError(idx, i);
const char c = format[i];
if (c == '*')
{
precisionStar = true;
++i;
if (i == length)
return formatError(idx, i);
}
else if ('0' <= c && c <= '9')
{
++i;
if (i == length)
return formatError(idx, i);
while ('0' <= format[i] && format[i] <= '9')
{
++i;
if (i == length)
return formatError(idx, i);
}
}
}
/* Read the specifier
*/
char genSpec;
Format specifier = parseGenericFormatSpecifier(format, i, genSpec);
if (specifier == Format_error)
return formatError(idx, i);
switch (genSpec)
{
case 'c':
case 's':
if (hash || zero)
return formatError(idx, i);
break;
case 'd':
case 'i':
if (hash)
return formatError(idx, i);
break;
case 'n':
if (hash || zero || precision || width || flags)
return formatError(idx, i);
break;
default:
break;
}
idx = i;
return specifier; // success
}
/*******************************************/
static Expression *getNextPrintfArg(const Loc &loc, Expressions &args, size_t &n,
size_t gnu_m_count, bool &skip)
{
if (n == args.length)
{
if (args.length < (n + 1) - gnu_m_count)
deprecation(loc, "more format specifiers than %d arguments", (int)n);
else
skip = true;
return NULL;
}
return args[n++];
}
static void errorPrintfFormat(const char *prefix, DString &slice, Expression *arg,
const char *texpect, Type *tactual)
{
deprecation(arg->loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`",
prefix ? prefix : "", arg->toChars(), (int)slice.length, slice.ptr, texpect, tactual->toChars());
}
/******************************************
* Check that arguments to a printf format string are compatible
* with that string. Issue errors for incompatibilities.
*
* Follows the C99 specification for printf.
*
* Takes a generous, rather than strict, view of compatiblity.
* For example, an unsigned value can be formatted with a signed specifier.
*
* Diagnosed incompatibilities are:
*
* 1. incompatible sizes which will cause argument misalignment
* 2. deferencing arguments that are not pointers
* 3. insufficient number of arguments
* 4. struct arguments
* 5. array and slice arguments
* 6. non-pointer arguments to `s` specifier
* 7. non-standard formats
* 8. undefined behavior per C99
*
* Per the C Standard, extra arguments are ignored.
*
* No attempt is made to fix the arguments or the format string.
*
* Params:
* loc = location for error messages
* format = format string
* args = arguments to match with format string
* isVa_list = if a "v" function (format check only)
*
* Returns:
* `true` if errors occurred
* References:
* C99 7.19.6.1
* http://www.cplusplus.com/reference/cstdio/printf/
*/
bool checkPrintfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list)
{
//printf("checkPrintFormat('%s')\n", format);
size_t n = 0; // index in args
size_t gnu_m_count = 0; // number of Format_GNU_m
const size_t format_length = strlen(format);
for (size_t i = 0; i < format_length;)
{
if (format[i] != '%')
{
++i;
continue;
}
bool widthStar = false;
bool precisionStar = false;
size_t j = i;
const Format fmt = parsePrintfFormatSpecifier(format, j, widthStar, precisionStar);
DString slice = DString(j - i, format + i);
i = j;
if (fmt == Format_percent)
continue; // "%%", no arguments
if (isVa_list)
{
// format check only
if (fmt == Format_error)
deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
continue;
}
if (fmt == Format_GNU_m)
++gnu_m_count;
if (widthStar)
{
bool skip = false;
Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip);
if (skip)
continue;
if (!e)
return true;
Type *t = e->type->toBasetype();
if (t->ty != Tint32 && t->ty != Tuns32)
errorPrintfFormat("width ", slice, e, "int", t);
}
if (precisionStar)
{
bool skip = false;
Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip);
if (skip)
continue;
if (!e)
return true;
Type *t = e->type->toBasetype();
if (t->ty != Tint32 && t->ty != Tuns32)
errorPrintfFormat("precision ", slice, e, "int", t);
}
bool skip = false;
Expression *e = getNextPrintfArg(loc, args, n, gnu_m_count, skip);
if (skip)
continue;
if (!e)
return true;
Type *t = e->type->toBasetype();
Type *tnext = t->nextOf();
const unsigned c_longsize = target.c.longsize;
const unsigned ptrsize = target.ptrsize;
// Types which are promoted to int are allowed.
// Spec: C99 6.5.2.2.7
switch (fmt)
{
case Format_u: // unsigned int
case Format_d: // int
if (t->ty != Tint32 && t->ty != Tuns32)
errorPrintfFormat(NULL, slice, e, fmt == Format_u ? "uint" : "int", t);
break;
case Format_hhu: // unsigned char
case Format_hhd: // signed char
if (t->ty != Tint32 && t->ty != Tuns32 && t->ty != Tint8 && t->ty != Tuns8)
errorPrintfFormat(NULL, slice, e, fmt == Format_hhu ? "ubyte" : "byte", t);
break;
case Format_hu: // unsigned short int
case Format_hd: // short int
if (t->ty != Tint32 && t->ty != Tuns32 && t->ty != Tint16 && t->ty != Tuns16)
errorPrintfFormat(NULL, slice, e, fmt == Format_hu ? "ushort" : "short", t);
break;
case Format_lu: // unsigned long int
case Format_ld: // long int
if (!(t->isintegral() && t->size() == c_longsize))
{
if (fmt == Format_lu)
errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "uint" : "ulong"), t);
else
errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "int" : "long"), t);
}
break;
case Format_llu: // unsigned long long int
case Format_lld: // long long int
if (t->ty != Tint64 && t->ty != Tuns64)
errorPrintfFormat(NULL, slice, e, fmt == Format_llu ? "ulong" : "long", t);
break;
case Format_ju: // uintmax_t
case Format_jd: // intmax_t
if (t->ty != Tint64 && t->ty != Tuns64)
{
if (fmt == Format_ju)
errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.uintmax_t", t);
else
errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t", t);
}
break;
case Format_zd: // size_t
if (!(t->isintegral() && t->size() == ptrsize))
errorPrintfFormat(NULL, slice, e, "size_t", t);
break;
case Format_td: // ptrdiff_t
if (!(t->isintegral() && t->size() == ptrsize))
errorPrintfFormat(NULL, slice, e, "ptrdiff_t", t);
break;
case Format_GNU_a: // Format_GNU_a is only for scanf
case Format_lg:
case Format_g: // double
if (t->ty != Tfloat64 && t->ty != Timaginary64)
errorPrintfFormat(NULL, slice, e, "double", t);
break;
case Format_Lg: // long double
if (t->ty != Tfloat80 && t->ty != Timaginary80)
errorPrintfFormat(NULL, slice, e, "real", t);
break;
case Format_p: // pointer
if (t->ty != Tpointer && t->ty != Tnull && t->ty != Tclass && t->ty != Tdelegate && t->ty != Taarray)
errorPrintfFormat(NULL, slice, e, "void*", t);
break;
case Format_n: // pointer to int
if (!(t->ty == Tpointer && tnext->ty == Tint32))
errorPrintfFormat(NULL, slice, e, "int*", t);
break;
case Format_ln: // pointer to long int
if (!(t->ty == Tpointer && tnext->isintegral() && !tnext->isunsigned() && tnext->size() == c_longsize))
errorPrintfFormat(NULL, slice, e, (c_longsize == 4 ? "int*" : "long*"), t);
break;
case Format_lln: // pointer to long long int
if (!(t->ty == Tpointer && tnext->ty == Tint64))
errorPrintfFormat(NULL, slice, e, "long*", t);
break;
case Format_hn: // pointer to short
if (!(t->ty == Tpointer && tnext->ty == Tint16))
errorPrintfFormat(NULL, slice, e, "short*", t);
break;
case Format_hhn: // pointer to signed char
if (!(t->ty == Tpointer && tnext->ty == Tint16))
errorPrintfFormat(NULL, slice, e, "byte*", t);
break;
case Format_jn: // pointer to intmax_t
if (!(t->ty == Tpointer && tnext->ty == Tint64))
errorPrintfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t*", t);
break;
case Format_zn: // pointer to size_t
if (!(t->ty == Tpointer && tnext->isintegral() && tnext->isunsigned() && tnext->size() == ptrsize))
errorPrintfFormat(NULL, slice, e, "size_t*", t);
break;
case Format_tn: // pointer to ptrdiff_t
if (!(t->ty == Tpointer && tnext->isintegral() && !tnext->isunsigned() && tnext->size() == ptrsize))
errorPrintfFormat(NULL, slice, e, "ptrdiff_t*", t);
break;
case Format_c: // char
if (t->ty != Tint32 && t->ty != Tuns32)
errorPrintfFormat(NULL, slice, e, "char", t);
break;
case Format_lc: // wint_t
if (t->ty != Tint32 && t->ty != Tuns32)
errorPrintfFormat(NULL, slice, e, "wchar_t", t);
break;
case Format_s: // pointer to char string
if (!(t->ty == Tpointer && (tnext->ty == Tchar || tnext->ty == Tint8 || tnext->ty == Tuns8)))
errorPrintfFormat(NULL, slice, e, "char*", t);
break;
case Format_ls: // pointer to wchar_t string
{
if (!(t->ty == Tpointer && tnext == target.c.twchar_t))
errorPrintfFormat(NULL, slice, e, "wchar_t*", t);
break;
}
case Format_error:
deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
break;
case Format_GNU_m:
break; // not assert(0) because it may go through it if there are extra arguments
case Format_percent:
default:
assert(0);
}
}
return false;
}
/*******************************************/
static Expression *getNextScanfArg(const Loc &loc, Expressions &args, size_t &n, bool asterisk)
{
if (n == args.length)
{
if (!asterisk)
deprecation(loc, "more format specifiers than %d arguments", (int)n);
return NULL;
}
return args[n++];
}
static void errorScanfFormat(const char *prefix, DString &slice,
Expression *arg, const char *texpect, Type *tactual)
{
deprecation(arg->loc, "%sargument `%s` for format specification `\"%.*s\"` must be `%s`, not `%s`",
prefix ? prefix : "", arg->toChars(), (int)slice.length, slice.ptr, texpect, tactual->toChars());
}
/******************************************
* Check that arguments to a scanf format string are compatible
* with that string. Issue errors for incompatibilities.
*
* Follows the C99 specification for scanf.
*
* Takes a generous, rather than strict, view of compatiblity.
* For example, an unsigned value can be formatted with a signed specifier.
*
* Diagnosed incompatibilities are:
*
* 1. incompatible sizes which will cause argument misalignment
* 2. deferencing arguments that are not pointers
* 3. insufficient number of arguments
* 4. struct arguments
* 5. array and slice arguments
* 6. non-standard formats
* 7. undefined behavior per C99
*
* Per the C Standard, extra arguments are ignored.
*
* No attempt is made to fix the arguments or the format string.
*
* Params:
* loc = location for error messages
* format = format string
* args = arguments to match with format string
* isVa_list = if a "v" function (format check only)
*
* Returns:
* `true` if errors occurred
* References:
* C99 7.19.6.2
* http://www.cplusplus.com/reference/cstdio/scanf/
*/
bool checkScanfFormat(const Loc &loc, const char *format, Expressions &args, bool isVa_list)
{
size_t n = 0;
const size_t format_length = strlen(format);
for (size_t i = 0; i < format_length;)
{
if (format[i] != '%')
{
++i;
continue;
}
bool asterisk = false;
size_t j = i;
const Format fmt = parseScanfFormatSpecifier(format, j, asterisk);
DString slice = DString(j - i, format + i);
i = j;
if (fmt == Format_percent || asterisk)
continue; // "%%", "%*": no arguments
if (isVa_list)
{
// format check only
if (fmt == Format_error)
deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
continue;
}
Expression *e = getNextScanfArg(loc, args, n, asterisk);
if (!e)
return true;
Type *t = e->type->toBasetype();
Type *tnext = t->nextOf();
const unsigned c_longsize = target.c.longsize;
const unsigned ptrsize = target.ptrsize;
switch (fmt)
{
case Format_n:
case Format_d: // pointer to int
if (!(t->ty == Tpointer && tnext->ty == Tint32))
errorScanfFormat(NULL, slice, e, "int*", t);
break;
case Format_hhn:
case Format_hhd: // pointer to signed char
if (!(t->ty == Tpointer && tnext->ty == Tint16))
errorScanfFormat(NULL, slice, e, "byte*", t);
break;
case Format_hn:
case Format_hd: // pointer to short
if (!(t->ty == Tpointer && tnext->ty == Tint16))
errorScanfFormat(NULL, slice, e, "short*", t);
break;
case Format_ln:
case Format_ld: // pointer to long int
if (!(t->ty == Tpointer && tnext->isintegral() && tnext->size() == c_longsize))
errorScanfFormat(NULL, slice, e, (c_longsize == 4 ? "int*" : "long*"), t);
break;
case Format_lln:
case Format_lld: // pointer to long long int
if (!(t->ty == Tpointer && tnext->ty == Tint64))
errorScanfFormat(NULL, slice, e, "long*", t);
break;
case Format_jn:
case Format_jd: // pointer to intmax_t
if (!(t->ty == Tpointer && tnext->ty == Tint64))
errorScanfFormat(NULL, slice, e, "core.stdc.stdint.intmax_t*", t);
break;
case Format_zn:
case Format_zd: // pointer to size_t
if (!(t->ty == Tpointer && tnext->isintegral() && tnext->isunsigned() && tnext->size() == ptrsize))
errorScanfFormat(NULL, slice, 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))
errorScanfFormat(NULL, slice, e, "ptrdiff_t*", t);
break;
case Format_u: // pointer to unsigned int
if (!(t->ty == Tpointer && tnext->ty == Tuns32))
errorScanfFormat(NULL, slice, e, "uint*", t);
break;
case Format_hhu: // pointer to unsigned char
if (!(t->ty == Tpointer && tnext->ty == Tuns8))
errorScanfFormat(NULL, slice, e, "ubyte*", t);
break;
case Format_hu: // pointer to unsigned short int
if (!(t->ty == Tpointer && tnext->ty == Tuns16))
errorScanfFormat(NULL, slice, e, "ushort*", t);
break;
case Format_lu: // pointer to unsigned long int
if (!(t->ty == Tpointer && tnext->isintegral() && tnext->isunsigned() && tnext->size() == c_longsize))
errorScanfFormat(NULL, slice, e, (c_longsize == 4 ? "uint*" : "ulong*"), t);
break;
case Format_llu: // pointer to unsigned long long int
if (!(t->ty == Tpointer && tnext->ty == Tuns64))
errorScanfFormat(NULL, slice, e, "ulong*", t);
break;
case Format_ju: // pointer to uintmax_t
if (!(t->ty == Tpointer && tnext->ty == Tuns64))
errorScanfFormat(NULL, slice, e, "ulong*", t);
break;
case Format_g: // pointer to float
if (!(t->ty == Tpointer && tnext->ty == Tfloat32))
errorScanfFormat(NULL, slice, e, "float*", t);
break;
case Format_lg: // pointer to double
if (!(t->ty == Tpointer && tnext->ty == Tfloat64))
errorScanfFormat(NULL, slice, e, "double*", t);
break;
case Format_Lg: // pointer to long double
if (!(t->ty == Tpointer && tnext->ty == Tfloat80))
errorScanfFormat(NULL, slice, e, "real*", t);
break;
case Format_GNU_a:
case Format_GNU_m:
case Format_c:
case Format_s: // pointer to char string
if (!(t->ty == Tpointer && (tnext->ty == Tchar || tnext->ty == Tint8 || tnext->ty == Tuns8)))
errorScanfFormat(NULL, slice, e, "char*", t);
break;
case Format_lc:
case Format_ls: // pointer to wchar_t string
{
if (!(t->ty == Tpointer && tnext == target.c.twchar_t))
errorScanfFormat(NULL, slice, e, "wchar_t*", t);
break;
}
case Format_p: // double pointer
if (!(t->ty == Tpointer && tnext->ty == Tpointer))
errorScanfFormat(NULL, slice, e, "void**", t);
break;
case Format_error:
deprecation(loc, "format specifier `\"%.*s\"` is invalid", (int)slice.length, slice.ptr);
break;
case Format_percent:
default:
assert(0);
}
}
return false;
}

1364
gcc/d/dmd/chkformat.d Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1695
gcc/d/dmd/clone.d Normal file

File diff suppressed because it is too large Load diff

57
gcc/d/dmd/compiler.d Normal file
View file

@ -0,0 +1,57 @@
/**
* Describes a back-end compiler and implements compiler-specific actions.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.compiler;
import dmd.arraytypes;
import dmd.dmodule;
import dmd.dscope;
import dmd.expression;
import dmd.mtype;
import dmd.root.array;
extern (C++) __gshared
{
bool includeImports = false;
// array of module patterns used to include/exclude imported modules
Array!(const(char)*) includeModulePatterns;
Modules compiledImports;
}
/**
* A data structure that describes a back-end compiler and implements
* compiler-specific actions.
*/
extern (C++) struct Compiler
{
/******************************
* Encode the given expression, which is assumed to be an rvalue literal
* as another type for use in CTFE.
* This corresponds roughly to the idiom *(Type *)&e.
*/
extern (C++) static Expression paintAsType(UnionExp* pue, Expression e, Type type);
/******************************
* For the given module, perform any post parsing analysis.
* Certain compiler backends (ie: GDC) have special placeholder
* modules whose source are empty, but code gets injected
* immediately after loading.
*/
extern (C++) static void onParseModule(Module m);
/**
* A callback function that is called once an imported module is
* parsed. If the callback returns true, then it tells the
* frontend that the driver intends on compiling the import.
*/
extern (C++) static bool onImport(Module m);
}

View file

@ -22,11 +22,6 @@ class Type;
struct Scope;
struct UnionExp;
// DMD-generated module `__entrypoint` where the C main resides
extern Module *entrypoint;
// Module in which the D main is
extern Module *rootHasMain;
extern bool includeImports;
// array of module patterns used to include/exclude imported modules
extern Array<const char*> includeModulePatterns;
@ -37,7 +32,6 @@ struct Compiler
// CTFE support for cross-compilation.
static Expression *paintAsType(UnionExp *, Expression *, Type *);
// Backend
static void genCmain(Scope *);
static bool onImport(Module *);
static void onParseModule(Module *);
};

112
gcc/d/dmd/complex.d Normal file
View file

@ -0,0 +1,112 @@
/**
* Implements a complex number type.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/complex.d, _complex.d)
* Documentation: https://dlang.org/phobos/dmd_complex.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/complex.d
*/
module dmd.complex;
import dmd.root.ctfloat;
extern (C++) struct complex_t
{
real_t re;
real_t im;
this() @disable;
this(real_t re)
{
this(re, CTFloat.zero);
}
this(real_t re, real_t im)
{
this.re = re;
this.im = im;
}
extern (D) complex_t opBinary(string op)(complex_t y)
if (op == "+")
{
return complex_t(re + y.re, im + y.im);
}
extern (D) complex_t opBinary(string op)(complex_t y)
if (op == "-")
{
return complex_t(re - y.re, im - y.im);
}
extern (D) complex_t opUnary(string op)()
if (op == "-")
{
return complex_t(-re, -im);
}
extern (D) complex_t opBinary(string op)(complex_t y)
if (op == "*")
{
return complex_t(re * y.re - im * y.im, im * y.re + re * y.im);
}
extern (D) complex_t opBinaryRight(string op)(real_t x)
if (op == "*")
{
return complex_t(x) * this;
}
extern (D) complex_t opBinary(string op)(real_t y)
if (op == "*")
{
return this * complex_t(y);
}
extern (D) complex_t opBinary(string op)(real_t y)
if (op == "/")
{
return this / complex_t(y);
}
extern (D) complex_t opBinary(string op)(complex_t y)
if (op == "/")
{
if (CTFloat.fabs(y.re) < CTFloat.fabs(y.im))
{
const r = y.re / y.im;
const den = y.im + r * y.re;
return complex_t((re * r + im) / den, (im * r - re) / den);
}
else
{
const r = y.im / y.re;
const den = y.re + r * y.im;
return complex_t((re + r * im) / den, (im - r * re) / den);
}
}
extern (D) bool opCast(T : bool)() const
{
return re || im;
}
int opEquals(complex_t y) const
{
return re == y.re && im == y.im;
}
}
extern (C++) real_t creall(complex_t x)
{
return x.re;
}
extern (C++) real_t cimagl(complex_t x)
{
return x.im;
}

View file

@ -20,7 +20,7 @@ struct complex_t
real_t re;
real_t im;
complex_t(real_t re) : re(re), im(ldouble(0)) {}
complex_t(real_t re) : re(re), im(CTFloat::zero) {}
complex_t(real_t re, real_t im) : re(re), im(im) {}
complex_t operator + (complex_t y) { return complex_t(re + y.re, im + y.im); }
@ -52,7 +52,7 @@ struct complex_t
int operator != (complex_t y) { return re != y.re || im != y.im; }
private:
complex_t() : re(ldouble(0)), im(ldouble(0)) {}
complex_t() : re(CTFloat::zero), im(CTFloat::zero) {}
};
inline complex_t operator * (real_t x, complex_t y) { return complex_t(x) * y; }

View file

@ -1,738 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/cond.c
*/
#include "root/dsystem.h" // strcmp()
#include "mars.h"
#include "id.h"
#include "init.h"
#include "aggregate.h"
#include "declaration.h"
#include "identifier.h"
#include "expression.h"
#include "cond.h"
#include "module.h"
#include "template.h"
#include "mtype.h"
#include "scope.h"
#include "statement.h"
#include "arraytypes.h"
#include "tokens.h"
bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors);
int findCondition(Identifiers *ids, Identifier *ident)
{
if (ids)
{
for (size_t i = 0; i < ids->length; i++)
{
Identifier *id = (*ids)[i];
if (id == ident)
return true;
}
}
return false;
}
/* ============================================================ */
Condition::Condition(Loc loc)
{
this->loc = loc;
inc = 0;
}
/* ============================================================ */
StaticForeach::StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe)
{
assert(!!aggrfe ^ !!rangefe);
this->loc = loc;
this->aggrfe = aggrfe;
this->rangefe = rangefe;
this->needExpansion = false;
}
StaticForeach *StaticForeach::syntaxCopy()
{
return new StaticForeach(
loc,
aggrfe ? (ForeachStatement *)aggrfe->syntaxCopy() : NULL,
rangefe ? (ForeachRangeStatement *)rangefe->syntaxCopy() : NULL
);
}
/*****************************************
* 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)) { ... }
*/
static void lowerArrayAggregate(StaticForeach *sfe, Scope *sc)
{
Expression *aggr = sfe->aggrfe->aggr;
Expression *el = new ArrayLengthExp(aggr->loc, aggr);
sc = sc->startCTFE();
el = expressionSemantic(el, sc);
sc = sc->endCTFE();
el = el->optimize(WANTvalue);
el = el->ctfeInterpret();
if (el->op == TOKint64)
{
Expressions *es;
if (ArrayLiteralExp *ale = aggr->isArrayLiteralExp())
{
// Directly use the elements of the array for the TupleExp creation
es = ale->elements;
}
else
{
size_t length = (size_t)el->toInteger();
es = new Expressions();
es->setDim(length);
for (size_t i = 0; i < length; i++)
{
IntegerExp *index = new IntegerExp(sfe->loc, i, Type::tsize_t);
Expression *value = new IndexExp(aggr->loc, aggr, index);
(*es)[i] = value;
}
}
sfe->aggrfe->aggr = new TupleExp(aggr->loc, es);
sfe->aggrfe->aggr = expressionSemantic(sfe->aggrfe->aggr, sc);
sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue);
sfe->aggrfe->aggr = sfe->aggrfe->aggr->ctfeInterpret();
}
else
{
sfe->aggrfe->aggr = new ErrorExp();
}
}
/*****************************************
* Wrap a statement into a function literal and call it.
*
* Params:
* loc = The source location.
* s = The statement.
* Returns:
* AST of the expression `(){ s; }()` with location loc.
*/
static Expression *wrapAndCall(Loc loc, Statement *s)
{
TypeFunction *tf = new TypeFunction(ParameterList(), NULL, LINKdefault, 0);
FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, loc, tf, TOKreserved, NULL);
fd->fbody = s;
FuncExp *fe = new FuncExp(loc, fd);
Expression *ce = new CallExp(loc, fe, new Expressions());
return ce;
}
/*****************************************
* Create a `foreach` statement from `aggrefe/rangefe` with given
* `foreach` variables and body `s`.
*
* Params:
* loc = The source location.
* parameters = The foreach variables.
* s = The `foreach` body.
* Returns:
* `foreach (parameters; aggregate) s;` or
* `foreach (parameters; lower .. upper) s;`
* Where aggregate/lower, upper are as for the current StaticForeach.
*/
static Statement *createForeach(StaticForeach *sfe, Loc loc, Parameters *parameters, Statement *s)
{
if (sfe->aggrfe)
{
return new ForeachStatement(loc, sfe->aggrfe->op, parameters, sfe->aggrfe->aggr->syntaxCopy(), s, loc);
}
else
{
assert(sfe->rangefe && parameters->length == 1);
return new ForeachRangeStatement(loc, sfe->rangefe->op, (*parameters)[0],
sfe->rangefe->lwr->syntaxCopy(),
sfe->rangefe->upr->syntaxCopy(), s, loc);
}
}
/*****************************************
* For a `static foreach` with multiple loop variables, the
* aggregate is lowered to an array of tuples. As D does not have
* built-in tuples, we need a suitable tuple type. This generates
* a `struct` that serves as the tuple type. This type is only
* used during CTFE and hence its typeinfo will not go to the
* object file.
*
* Params:
* loc = The source location.
* e = The expressions we wish to store in the tuple.
* sc = The current scope.
* Returns:
* A struct type of the form
* struct Tuple
* {
* typeof(AliasSeq!(e)) tuple;
* }
*/
static TypeStruct *createTupleType(Loc loc, Expressions *e)
{ // TODO: move to druntime?
Identifier *sid = Identifier::generateId("Tuple");
StructDeclaration *sdecl = new StructDeclaration(loc, sid, false);
sdecl->storage_class |= STCstatic;
sdecl->members = new Dsymbols();
Identifier *fid = Identifier::idPool("tuple");
Type *ty = new TypeTypeof(loc, new TupleExp(loc, e));
sdecl->members->push(new VarDeclaration(loc, ty, fid, NULL));
TypeStruct *r = (TypeStruct *)sdecl->type;
if (global.params.useTypeInfo && Type::dtypeinfo)
r->vtinfo = TypeInfoStructDeclaration::create(r); // prevent typeinfo from going to object file
return r;
}
/*****************************************
* Create the AST for an instantiation of a suitable tuple type.
*
* Params:
* loc = The source location.
* type = A Tuple type, created with createTupleType.
* e = The expressions we wish to store in the tuple.
* Returns:
* An AST for the expression `Tuple(e)`.
*/
static Expression *createTuple(Loc loc, TypeStruct *type, Expressions *e)
{ // 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.
*/
static void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc)
{
size_t nvars = sfe->aggrfe ? sfe->aggrfe->parameters->length : 1;
Loc 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 *pparams[3] = {new Parameters(), new Parameters(), new Parameters()};
for (size_t i = 0; i < nvars; i++)
{
for (size_t j = 0; j < 3; j++)
{
Parameters *params = pparams[j];
Parameter *p = sfe->aggrfe ? (*sfe->aggrfe->parameters)[i] : sfe->rangefe->prm;
params->push(new Parameter(p->storageClass, p->type, p->ident, NULL, NULL));
}
}
Expression *res[2];
TypeStruct *tplty = NULL;
if (nvars == 1) // only one `static foreach` variable, generate identifiers.
{
for (size_t i = 0; i < 2; i++)
{
res[i] = new IdentifierExp(aloc, (*pparams[i])[0]->ident);
}
}
else // multiple `static foreach` variables, generate tuples.
{
for (size_t i = 0; i < 2; i++)
{
Expressions *e = new Expressions();
for (size_t j = 0; j < pparams[0]->length; j++)
{
Parameter *p = (*pparams[i])[j];
e->push(new IdentifierExp(aloc, p->ident));
}
if (!tplty)
{
tplty = createTupleType(aloc, e);
}
res[i] = 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 = expressionSemantic(sfe->rangefe->lwr, sc);
sfe->rangefe->lwr = resolveProperties(sc, sfe->rangefe->lwr);
sfe->rangefe->upr = expressionSemantic(sfe->rangefe->upr, 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();
}
Statements *s1 = new Statements();
Statements *sfebody = new Statements();
if (tplty) sfebody->push(new ExpStatement(sfe->loc, tplty->sym));
sfebody->push(new ReturnStatement(aloc, res[0]));
s1->push(createForeach(sfe, aloc, pparams[0], new CompoundStatement(aloc, sfebody)));
s1->push(new ExpStatement(aloc, new AssertExp(aloc, new IntegerExp(aloc, 0, Type::tint32))));
Type *ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
Type *aty = ety->arrayOf();
Identifier *idres = Identifier::generateId("__res");
VarDeclaration *vard = new VarDeclaration(aloc, aty, idres, NULL);
Statements *s2 = new Statements();
// Run 'typeof' gagged to avoid duplicate errors and if it fails just create
// an empty foreach to expose them.
unsigned olderrors = global.startGagging();
ety = typeSemantic(ety, aloc, sc);
if (global.endGagging(olderrors))
s2->push(createForeach(sfe, aloc, pparams[1], NULL));
else
{
s2->push(new ExpStatement(aloc, vard));
Expression *catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
s2->push(createForeach(sfe, aloc, pparams[1], new ExpStatement(aloc, catass)));
s2->push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
}
Expression *aggr;
Type *indexty;
if (sfe->rangefe && (indexty = ety)->isintegral())
{
sfe->rangefe->lwr->type = indexty;
sfe->rangefe->upr->type = indexty;
IntRange lwrRange = getIntRange(sfe->rangefe->lwr);
IntRange uprRange = getIntRange(sfe->rangefe->upr);
const dinteger_t lwr = sfe->rangefe->lwr->toInteger();
dinteger_t upr = sfe->rangefe->upr->toInteger();
size_t length = 0;
if (lwrRange.imin <= uprRange.imax)
length = (size_t)(upr - lwr);
Expressions *exps = new Expressions();
exps->setDim(length);
if (sfe->rangefe->op == TOKforeach)
{
for (size_t i = 0; i < length; i++)
(*exps)[i] = new IntegerExp(aloc, lwr + i, indexty);
}
else
{
--upr;
for (size_t i = 0; i < length; i++)
(*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 = expressionSemantic(aggr, 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, TOKforeach, pparams[2], aggr,
sfe->aggrfe ? sfe->aggrfe->_body : sfe->rangefe->_body,
sfe->aggrfe ? sfe->aggrfe->endloc : sfe->rangefe->endloc);
sfe->rangefe = NULL;
lowerArrayAggregate(sfe, sc); // finally, turn generated array into expression tuple
}
/*****************************************
* Perform `static foreach` lowerings that are necessary in order
* to finally expand the `static foreach` using
* `ddmd.statementsem.makeTupleForeach`.
*/
void staticForeachPrepare(StaticForeach *sfe, Scope *sc)
{
assert(sc);
if (sfe->aggrfe)
{
sc = sc->startCTFE();
sfe->aggrfe->aggr = expressionSemantic(sfe->aggrfe->aggr, sc);
sc = sc->endCTFE();
sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue);
}
if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Terror)
{
return;
}
if (!staticForeachReady(sfe))
{
if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Tarray)
{
lowerArrayAggregate(sfe, sc);
}
else
{
lowerNonArrayAggregate(sfe, sc);
}
}
}
/*****************************************
* Returns:
* `true` iff ready to call `ddmd.statementsem.makeTupleForeach`.
*/
bool staticForeachReady(StaticForeach *sfe)
{
return sfe->aggrfe && sfe->aggrfe->aggr && sfe->aggrfe->aggr->type &&
sfe->aggrfe->aggr->type->toBasetype()->ty == Ttuple;
}
/* ============================================================ */
DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident)
: Condition(Loc())
{
this->mod = mod;
this->level = level;
this->ident = ident;
}
Condition *DVCondition::syntaxCopy()
{
return this; // don't need to copy
}
/* ============================================================ */
void DebugCondition::addGlobalIdent(const char *ident)
{
if (!global.debugids)
global.debugids = new Identifiers();
global.debugids->push(Identifier::idPool(ident));
}
DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident)
: DVCondition(mod, level, ident)
{
}
// Helper for printing dependency information
void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType)
{
if (!global.params.moduleDeps || global.params.moduleDepsFile.length)
return;
OutBuffer *ob = global.params.moduleDeps;
Module* imod = sc ? sc->instantiatingModule() : condition->mod;
if (!imod)
return;
ob->writestring(depType);
ob->writestring(imod->toPrettyChars());
ob->writestring(" (");
escapePath(ob, imod->srcfile->toChars());
ob->writestring(") : ");
if (condition->ident)
ob->printf("%s\n", condition->ident->toChars());
else
ob->printf("%d\n", condition->level);
}
int DebugCondition::include(Scope *sc)
{
//printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
if (inc == 0)
{
inc = 2;
bool definedInModule = false;
if (ident)
{
if (findCondition(mod->debugids, ident))
{
inc = 1;
definedInModule = true;
}
else if (findCondition(global.debugids, ident))
inc = 1;
else
{ if (!mod->debugidsNot)
mod->debugidsNot = new Identifiers();
mod->debugidsNot->push(ident);
}
}
else if (level <= global.params.debuglevel || level <= mod->debuglevel)
inc = 1;
if (!definedInModule)
printDepsConditional(sc, this, "depsDebug ");
}
return (inc == 1);
}
/* ============================================================ */
static bool isReserved(const char *ident)
{
static const char* reserved[] =
{
"DigitalMars",
"GNU",
"LDC",
"SDC",
"Windows",
"Win32",
"Win64",
"linux",
"OSX",
"FreeBSD",
"OpenBSD",
"NetBSD",
"DragonFlyBSD",
"BSD",
"Solaris",
"Posix",
"AIX",
"Haiku",
"SkyOS",
"SysV3",
"SysV4",
"Hurd",
"Android",
"PlayStation",
"PlayStation4",
"Cygwin",
"MinGW",
"FreeStanding",
"X86",
"X86_64",
"ARM",
"ARM_Thumb",
"ARM_SoftFloat",
"ARM_SoftFP",
"ARM_HardFloat",
"AArch64",
"Epiphany",
"PPC",
"PPC_SoftFloat",
"PPC_HardFloat",
"PPC64",
"IA64",
"MIPS32",
"MIPS64",
"MIPS_O32",
"MIPS_N32",
"MIPS_O64",
"MIPS_N64",
"MIPS_EABI",
"MIPS_SoftFloat",
"MIPS_HardFloat",
"MSP430",
"NVPTX",
"NVPTX64",
"RISCV32",
"RISCV64",
"SPARC",
"SPARC_V8Plus",
"SPARC_SoftFloat",
"SPARC_HardFloat",
"SPARC64",
"S390",
"S390X",
"HPPA",
"HPPA64",
"SH",
"Alpha",
"Alpha_SoftFloat",
"Alpha_HardFloat",
"LittleEndian",
"BigEndian",
"ELFv1",
"ELFv2",
"CRuntime_Digitalmars",
"CRuntime_Glibc",
"CRuntime_Microsoft",
"CRuntime_Musl",
"CRuntime_UClibc",
"CppRuntime_Clang",
"CppRuntime_DigitalMars",
"CppRuntime_Gcc",
"CppRuntime_Microsoft",
"CppRuntime_Sun",
"D_Coverage",
"D_Ddoc",
"D_InlineAsm_X86",
"D_InlineAsm_X86_64",
"D_LP64",
"D_X32",
"D_HardFloat",
"D_SoftFloat",
"D_PIC",
"D_SIMD",
"D_Version2",
"D_NoBoundsChecks",
"unittest",
"assert",
"all",
"none",
NULL
};
for (unsigned i = 0; reserved[i]; i++)
{
if (strcmp(ident, reserved[i]) == 0)
return true;
}
if (ident[0] == 'D' && ident[1] == '_')
return true;
return false;
}
void checkReserved(Loc loc, const char *ident)
{
if (isReserved(ident))
error(loc, "version identifier `%s` is reserved and cannot be set", ident);
}
void VersionCondition::addGlobalIdent(const char *ident)
{
checkReserved(Loc(), ident);
addPredefinedGlobalIdent(ident);
}
void VersionCondition::addPredefinedGlobalIdent(const char *ident)
{
if (!global.versionids)
global.versionids = new Identifiers();
global.versionids->push(Identifier::idPool(ident));
}
VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident)
: DVCondition(mod, level, ident)
{
}
int VersionCondition::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 == 0)
{
inc = 2;
bool definedInModule=false;
if (ident)
{
if (findCondition(mod->versionids, ident))
{
inc = 1;
definedInModule = true;
}
else if (findCondition(global.versionids, ident))
inc = 1;
else
{
if (!mod->versionidsNot)
mod->versionidsNot = new Identifiers();
mod->versionidsNot->push(ident);
}
}
else if (level <= global.params.versionlevel || level <= mod->versionlevel)
inc = 1;
if (!definedInModule && (!ident || (!isReserved(ident->toChars()) && ident != Id::_unittest && ident != Id::_assert)))
printDepsConditional(sc, this, "depsVersion ");
}
return (inc == 1);
}
/**************************** StaticIfCondition *******************************/
StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp)
: Condition(loc)
{
this->exp = exp;
}
Condition *StaticIfCondition::syntaxCopy()
{
return new StaticIfCondition(loc, exp->syntaxCopy());
}
int StaticIfCondition::include(Scope *sc)
{
if (inc == 0)
{
if (!sc)
{
error(loc, "static if conditional cannot be at global scope");
inc = 2;
return 0;
}
sc = sc->push(sc->scopesym);
bool errors = false;
if (!exp)
goto Lerror;
bool result = evalStaticCondition(sc, exp, exp, errors);
sc->pop();
// Prevent repeated condition evaluation.
// See: fail_compilation/fail7815.d
if (inc != 0)
return (inc == 1);
if (errors)
goto Lerror;
if (result)
inc = 1;
else
inc = 2;
}
return (inc == 1);
Lerror:
if (!global.gag)
inc = 2; // so we don't see the error message again
return 0;
}

1004
gcc/d/dmd/cond.d Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,26 +16,24 @@
class Expression;
class Identifier;
struct OutBuffer;
class Module;
struct Scope;
class ScopeDsymbol;
class DebugCondition;
class ForeachStatement;
class ForeachRangeStatement;
int findCondition(Identifiers *ids, Identifier *ident);
enum Include
{
INCLUDEnotComputed, /// not computed yet
INCLUDEyes, /// include the conditional code
INCLUDEno /// do not include the conditional code
};
class Condition : public ASTNode
{
public:
Loc loc;
// 0: not computed yet
// 1: include
// 2: do not include
int inc;
Condition(Loc loc);
Include inc;
virtual Condition *syntaxCopy() = 0;
virtual int include(Scope *sc) = 0;
@ -54,13 +52,9 @@ public:
bool needExpansion;
StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe);
StaticForeach *syntaxCopy();
};
void staticForeachPrepare(StaticForeach *sfe, Scope *sc);
bool staticForeachReady(StaticForeach *sfe);
class DVCondition : public Condition
{
public:
@ -68,9 +62,7 @@ public:
Identifier *ident;
Module *mod;
DVCondition(Module *mod, unsigned level, Identifier *ident);
Condition *syntaxCopy();
DVCondition *syntaxCopy();
void accept(Visitor *v) { v->visit(this); }
};
@ -79,8 +71,6 @@ class DebugCondition : public DVCondition
public:
static void addGlobalIdent(const char *ident);
DebugCondition(Module *mod, unsigned level, Identifier *ident);
int include(Scope *sc);
DebugCondition *isDebugCondition() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -92,8 +82,6 @@ public:
static void addGlobalIdent(const char *ident);
static void addPredefinedGlobalIdent(const char *ident);
VersionCondition(Module *mod, unsigned level, Identifier *ident);
int include(Scope *sc);
VersionCondition *isVersionCondition() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -104,8 +92,7 @@ class StaticIfCondition : public Condition
public:
Expression *exp;
StaticIfCondition(Loc loc, Expression *exp);
Condition *syntaxCopy();
StaticIfCondition *syntaxCopy();
int include(Scope *sc);
void accept(Visitor *v) { v->visit(this); }
};

File diff suppressed because it is too large Load diff

1825
gcc/d/dmd/constfold.d Normal file

File diff suppressed because it is too large Load diff

4249
gcc/d/dmd/cparse.d Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2540
gcc/d/dmd/cppmangle.d Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,30 +5,14 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/dlang/dmd/blob/master/src/ctfe.h
* https://github.com/dlang/dmd/blob/master/src/dmd/ctfe.h
*/
#pragma once
#include "arraytypes.h"
#include "tokens.h"
#include "expression.h"
/**
Global status of the CTFE engine. Mostly used for performance diagnostics
*/
struct CtfeStatus
{
static int callDepth; // current number of recursive calls
/* When printing a stack trace,
* suppress this number of calls
*/
static int stackTraceCallsToSuppress;
static int maxCallDepth; // highest number of recursive calls
static int numArrayAllocs; // Number of allocated arrays
static int numAssignments; // total number of assignments executed
};
/**
A reference to a class, or an interface. We need this when we
point to a base class (we must record what the type is).
@ -37,55 +21,35 @@ class ClassReferenceExp : public Expression
{
public:
StructLiteralExp *value;
ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type);
ClassDeclaration *originalClass();
/// Return index of the field, or -1 if not found
int getFieldIndex(Type *fieldtype, unsigned fieldoffset);
/// Return index of the field, or -1 if not found
/// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
int findFieldIndexByName(VarDeclaration *v);
void accept(Visitor *v) { v->visit(this); }
};
// The various functions are used only to detect compiler CTFE bugs
Expression *getValue(VarDeclaration *vd);
bool hasValue(VarDeclaration *vd);
void setValueNull(VarDeclaration *vd);
void setValueWithoutChecking(VarDeclaration *vd, Expression *newval);
void setValue(VarDeclaration *vd, Expression *newval);
/// Return index of the field, or -1 if not found
/// Same as getFieldIndex, but checks for a direct match with the VarDeclaration
int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v);
/** An uninitialized value
/**
An uninitialized value
*/
class VoidInitExp : public Expression
{
public:
VarDeclaration *var;
VoidInitExp(VarDeclaration *var, Type *type);
const char *toChars();
const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
// Create an appropriate void initializer
UnionExp voidInitLiteral(Type *t, VarDeclaration *var);
/** Fake class which holds the thrown exception.
Used for implementing exception handling.
/**
Fake class which holds the thrown exception.
Used for implementing exception handling.
*/
class ThrownExceptionExp : public Expression
{
public:
ClassReferenceExp *thrown; // the thing being tossed
ThrownExceptionExp(Loc loc, ClassReferenceExp *victim);
const char *toChars();
/// Generate an error message when this exception is not caught
void generateUncaughtError();
const char *toChars() const;
void accept(Visitor *v) { v->visit(this); }
};
@ -96,173 +60,5 @@ public:
class CTFEExp : public Expression
{
public:
CTFEExp(TOK tok);
const char *toChars();
// Handy instances to share
static CTFEExp *cantexp;
static CTFEExp *voidexp;
static CTFEExp *breakexp;
static CTFEExp *continueexp;
static CTFEExp *gotoexp;
static bool isCantExp(Expression *e) { return e && e->op == TOKcantexp; }
static bool isGotoExp(Expression *e) { return e && e->op == TOKgoto; }
const char *toChars() const;
};
/****************************************************************/
/// True if 'e' is TOKcantexp, or an exception
bool exceptionOrCantInterpret(Expression *e);
// Used for debugging only
void showCtfeExpr(Expression *e, int level = 0);
/// Return true if this is a valid CTFE expression
bool isCtfeValueValid(Expression *newval);
bool isCtfeReferenceValid(Expression *newval);
/// Given expr, which evaluates to an array/AA/string literal,
/// return true if it needs to be copied
bool needToCopyLiteral(Expression *expr);
/// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral.
/// This value will be used for in-place modification.
UnionExp copyLiteral(Expression *e);
/// Set this literal to the given type, copying it if necessary
Expression *paintTypeOntoLiteral(Type *type, Expression *lit);
Expression *paintTypeOntoLiteral(UnionExp *pue, Type *type, Expression *lit);
UnionExp paintTypeOntoLiteralCopy(Type *type, Expression *lit);
/// Convert from a CTFE-internal slice, into a normal Expression
Expression *resolveSlice(Expression *e, UnionExp *pue = NULL);
/// Determine the array length, without interpreting the expression.
uinteger_t resolveArrayLength(Expression *e);
/// Create an array literal consisting of 'elem' duplicated 'dim' times.
ArrayLiteralExp *createBlockDuplicatedArrayLiteral(UnionExp *pue, Loc loc, Type *type,
Expression *elem, size_t dim);
/// Create a string literal consisting of 'value' duplicated 'dim' times.
StringExp *createBlockDuplicatedStringLiteral(UnionExp *pue, Loc loc, Type *type,
unsigned value, size_t dim, unsigned char sz);
/* Set dest = src, where both dest and src are container value literals
* (ie, struct literals, or static arrays (can be an array literal or a string)
* Assignment is recursively in-place.
* Purpose: any reference to a member of 'dest' will remain valid after the
* assignment.
*/
void assignInPlace(Expression *dest, Expression *src);
/// Duplicate the elements array, then set field 'indexToChange' = newelem.
Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem);
/// Given an AA literal aae, set arr[index] = newval and return the new array.
Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae,
Expression *index, Expression *newval);
/// 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.
UnionExp changeArrayLiteralLength(Loc loc, TypeArray *arrayType,
Expression *oldval, size_t oldlen, size_t newlen);
/// Return true if t is a pointer (not a function pointer)
bool isPointer(Type *t);
// For CTFE only. Returns true if 'e' is TRUE or a non-null pointer.
bool isTrueBool(Expression *e);
/// Is it safe to convert from srcPointee* to destPointee* ?
/// srcPointee is the genuine type (never void).
/// destPointee may be void.
bool isSafePointerCast(Type *srcPointee, Type *destPointee);
/// Given pointer e, return the memory block expression it points to,
/// and set ofs to the offset within that memory block.
Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs);
/// Return true if agg1 and agg2 are pointers to the same memory block
bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2);
// return e1 - e2 as an integer, or error if not possible
UnionExp pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2);
/// Return 1 if true, 0 if false
/// -1 if comparison is illegal because they point to non-comparable memory blocks
int comparePointers(TOK op, Expression *agg1, dinteger_t ofs1, Expression *agg2, dinteger_t ofs2);
// Return eptr op e2, where eptr is a pointer, e2 is an integer,
// and op is TOKadd or TOKmin
UnionExp pointerArithmetic(Loc loc, TOK op, Type *type,
Expression *eptr, Expression *e2);
// True if conversion from type 'from' to 'to' involves a reinterpret_cast
// floating point -> integer or integer -> floating point
bool isFloatIntPaint(Type *to, Type *from);
// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'.
Expression *paintFloatInt(UnionExp *pue, Expression *fromVal, Type *to);
/// Return true if t is an AA
bool isAssocArray(Type *t);
/// Given a template AA type, extract the corresponding built-in AA type
TypeAArray *toBuiltinAAType(Type *t);
/* Given an AA literal 'ae', and a key 'e2':
* Return ae[e2] if present, or NULL if not found.
* Return TOKcantexp on error.
*/
Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2);
/// True if type is TypeInfo_Class
bool isTypeInfo_Class(Type *type);
/***********************************************
COW const-folding operations
***********************************************/
/// Return true if non-pointer expression e can be compared
/// with >,is, ==, etc, using ctfeCmp, ctfeEquals, ctfeIdentity
bool isCtfeComparable(Expression *e);
/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1
int ctfeEqual(Loc loc, TOK op, Expression *e1, Expression *e2);
/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1
int ctfeIdentity(Loc loc, TOK op, Expression *e1, Expression *e2);
/// Returns rawCmp OP 0; where OP is ==, !=, <, >=, etc. Result is 0 or 1
int specificCmp(TOK op, int rawCmp);
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
int intUnsignedCmp(TOK op, dinteger_t n1, dinteger_t n2);
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
int intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2);
/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1
int realCmp(TOK op, real_t r1, real_t r2);
/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1
int ctfeCmp(Loc loc, TOK op, Expression *e1, Expression *e2);
/// Returns e1 ~ e2. Resolves slices before concatenation.
UnionExp ctfeCat(Loc loc, Type *type, Expression *e1, Expression *e2);
/// Same as for constfold.Index, except that it only works for static arrays,
/// dynamic arrays, and strings.
Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx);
/// Cast 'e' of type 'type' to type 'to'.
Expression *ctfeCast(UnionExp *pue, Loc loc, Type *type, Type *to, Expression *e);

File diff suppressed because it is too large Load diff

2096
gcc/d/dmd/ctfeexpr.d Normal file

File diff suppressed because it is too large Load diff

225
gcc/d/dmd/ctorflow.d Normal file
View file

@ -0,0 +1,225 @@
/**
* Manage flow analysis for constructors.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.ctorflow;
import core.stdc.stdio;
import dmd.root.rmem;
import dmd.globals : Loc;
enum CSX : ushort
{
none = 0,
this_ctor = 0x01, /// called this()
super_ctor = 0x02, /// called super()
label = 0x04, /// seen a label
return_ = 0x08, /// seen a return statement
any_ctor = 0x10, /// either this() or super() was called
halt = 0x20, /// assert(0)
}
/// Individual field in the Ctor with information about its callees and location.
struct FieldInit
{
CSX csx; /// information about the field's callees
Loc loc; /// location of the field initialization
}
/***********
* Primitive flow analysis for constructors
*/
struct CtorFlow
{
CSX callSuper; /// state of calling other constructors
FieldInit[] fieldinit; /// state of field initializations
void allocFieldinit(size_t dim)
{
fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim];
}
void freeFieldinit()
{
if (fieldinit.ptr)
mem.xfree(fieldinit.ptr);
fieldinit = null;
}
/***********************
* Create a deep copy of `this`
* Returns:
* a copy
*/
CtorFlow clone()
{
return CtorFlow(callSuper, fieldinit.arraydup);
}
/**********************************
* Set CSX bits in flow analysis state
* Params:
* csx = bits to set
*/
void orCSX(CSX csx) nothrow pure
{
callSuper |= csx;
foreach (ref u; fieldinit)
u.csx |= csx;
}
/******************************
* OR CSX bits to `this`
* Params:
* ctorflow = bits to OR in
*/
void OR(const ref CtorFlow ctorflow) pure nothrow
{
callSuper |= ctorflow.callSuper;
if (fieldinit.length && ctorflow.fieldinit.length)
{
assert(fieldinit.length == ctorflow.fieldinit.length);
foreach (i, u; ctorflow.fieldinit)
{
auto fi = &fieldinit[i];
fi.csx |= u.csx;
if (fi.loc is Loc.init)
fi.loc = u.loc;
}
}
}
}
/****************************************
* Merge `b` flow analysis results into `a`.
* Params:
* a = the path to merge `b` into
* b = the other path
* Returns:
* false means one of the paths skips construction
*/
bool mergeCallSuper(ref CSX a, const CSX b) pure nothrow
{
// This does a primitive flow analysis to support the restrictions
// regarding when and how constructors can appear.
// It merges the results of two paths.
// The two paths are `a` and `b`; the result is merged into `a`.
if (b == a)
return true;
// Have ALL branches called a constructor?
const aAll = (a & (CSX.this_ctor | CSX.super_ctor)) != 0;
const bAll = (b & (CSX.this_ctor | CSX.super_ctor)) != 0;
// Have ANY branches called a constructor?
const aAny = (a & CSX.any_ctor) != 0;
const bAny = (b & CSX.any_ctor) != 0;
// Have any branches returned?
const aRet = (a & CSX.return_) != 0;
const bRet = (b & CSX.return_) != 0;
// Have any branches halted?
const aHalt = (a & CSX.halt) != 0;
const bHalt = (b & CSX.halt) != 0;
if (aHalt && bHalt)
{
a = CSX.halt;
}
else if ((!bHalt && bRet && !bAny && aAny) || (!aHalt && aRet && !aAny && bAny))
{
// If one has returned without a constructor call, there must not
// be ctor calls in the other.
return false;
}
else if (bHalt || bRet && bAll)
{
// If one branch has called a ctor and then exited, anything the
// other branch has done is OK (except returning without a
// ctor call, but we already checked that).
a |= b & (CSX.any_ctor | CSX.label);
}
else if (aHalt || aRet && aAll)
{
a = cast(CSX)(b | (a & (CSX.any_ctor | CSX.label)));
}
else if (aAll != bAll) // both branches must have called ctors, or both not
return false;
else
{
// If one returned without a ctor, remember that
if (bRet && !bAny)
a |= CSX.return_;
a |= b & (CSX.any_ctor | CSX.label);
}
return true;
}
/****************************************
* Merge `b` flow analysis results into `a`.
* Params:
* a = the path to merge `b` into
* b = the other path
* Returns:
* false means either `a` or `b` skips initialization
*/
bool mergeFieldInit(ref CSX a, const CSX b) pure nothrow
{
if (b == a)
return true;
// Have any branches returned?
const aRet = (a & CSX.return_) != 0;
const bRet = (b & CSX.return_) != 0;
// Have any branches halted?
const aHalt = (a & CSX.halt) != 0;
const bHalt = (b & CSX.halt) != 0;
if (aHalt && bHalt)
{
a = CSX.halt;
return true;
}
// The logic here is to prefer the branch that neither halts nor returns.
bool ok;
if (!bHalt && bRet)
{
// Branch b returns, no merging required.
ok = (b & CSX.this_ctor);
}
else if (!aHalt && aRet)
{
// Branch a returns, but b doesn't, b takes precedence.
ok = (a & CSX.this_ctor);
a = b;
}
else if (bHalt)
{
// Branch b halts, no merging required.
ok = (a & CSX.this_ctor);
}
else if (aHalt)
{
// Branch a halts, but b doesn't, b takes precedence.
ok = (b & CSX.this_ctor);
a = b;
}
else
{
// Neither branch returns nor halts, merge flags.
ok = !((a ^ b) & CSX.this_ctor);
a |= b;
}
return ok;
}

File diff suppressed because it is too large Load diff

3741
gcc/d/dmd/dcast.d Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1139
gcc/d/dmd/dclass.d Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2323
gcc/d/dmd/declaration.d Normal file

File diff suppressed because it is too large Load diff

View file

@ -13,115 +13,98 @@
#include "dsymbol.h"
#include "mtype.h"
#include "objc.h"
#include "tokens.h"
class Expression;
class Statement;
class LabelDsymbol;
class Initializer;
class Module;
class ForeachStatement;
struct Ensure
{
Identifier *id;
Statement *ensure;
Ensure();
Ensure(Identifier *id, Statement *ensure);
Ensure syntaxCopy();
static Ensures *arraySyntaxCopy(Ensures *a);
};
class AliasDeclaration;
class FuncDeclaration;
class ExpInitializer;
class StructDeclaration;
struct InterState;
struct CompiledCtfeFunction;
struct ObjcSelector;
struct IntRange;
enum LINK;
enum TOK;
enum MATCH;
enum PURE;
enum PINLINE;
//enum STC : ulong from astenums.d:
#define STCundefined 0ULL
#define STCstatic 1ULL /// `static`
#define STCextern 2ULL /// `extern`
#define STCconst 4ULL /// `const`
#define STCfinal 8ULL /// `final`
#define STCabstract 0x10ULL /// `abstract`
#define STCparameter 0x20ULL /// is function parameter
#define STCfield 0x40ULL /// is field of struct, union or class
#define STCoverride 0x80ULL /// `override`
#define STCauto 0x100ULL /// `auto`
#define STCsynchronized 0x200ULL /// `synchronized`
#define STCdeprecated 0x400ULL /// `deprecated`
#define STCin 0x800ULL /// `in` parameter
#define STCout 0x1000ULL /// `out` parameter
#define STClazy 0x2000ULL /// `lazy` parameter
#define STCforeach 0x4000ULL /// variable for foreach loop
#define STCvariadic 0x8000ULL /// the `variadic` parameter in: T foo(T a, U b, V variadic...)
#define STCctorinit 0x10000ULL /// can only be set inside constructor
#define STCtemplateparameter 0x20000ULL /// template parameter
#define STCref 0x40000ULL /// `ref`
#define STCscope 0x80000ULL /// `scope`
#define STCmaybescope 0x100000ULL /// parameter might be `scope`
#define STCscopeinferred 0x200000ULL /// `scope` has been inferred and should not be part of mangling, `scope` must also be set
#define STCreturn 0x400000ULL /// 'return ref' or 'return scope' for function parameters
#define STCreturnScope 0x800000ULL /// if `ref return scope` then resolve to `ref` and `return scope`
#define STCreturninferred 0x1000000ULL /// `return` has been inferred and should not be part of mangling, `return` must also be set
#define STCimmutable 0x2000000ULL /// `immutable`
#define STCinit 0x4000000ULL /// has explicit initializer
#define STCmanifest 0x8000000ULL /// manifest constant
#define STCnodtor 0x10000000ULL /// do not run destructor
#define STCnothrow 0x20000000ULL /// `nothrow` meaning never throws exceptions
#define STCpure 0x40000000ULL /// `pure` function
#define STCtls 0x80000000ULL /// thread local
#define STCalias 0x100000000ULL /// `alias` parameter
#define STCshared 0x200000000ULL /// accessible from multiple threads
#define STCgshared 0x400000000ULL /// accessible from multiple threads, but not typed as `shared`
#define STCwild 0x800000000ULL /// for wild type constructor
#define STCproperty 0x1000000000ULL /// `@property`
#define STCsafe 0x2000000000ULL /// `@safe`
#define STCtrusted 0x4000000000ULL /// `@trusted`
#define STCsystem 0x8000000000ULL /// `@system`
#define STCctfe 0x10000000000ULL /// can be used in CTFE, even if it is static
#define STCdisable 0x20000000000ULL /// for functions that are not callable
#define STCresult 0x40000000000ULL /// for result variables passed to out contracts
#define STCnodefaultctor 0x80000000000ULL /// must be set inside constructor
#define STCtemp 0x100000000000ULL /// temporary variable
#define STCrvalue 0x200000000000ULL /// force rvalue for variables
#define STCnogc 0x400000000000ULL /// `@nogc`
#define STCautoref 0x800000000000ULL /// Mark for the already deduced `auto ref` parameter
#define STCinference 0x1000000000000ULL /// do attribute inference
#define STCexptemp 0x2000000000000ULL /// temporary variable that has lifetime restricted to an expression
#define STCfuture 0x4000000000000ULL /// introducing new base class function
#define STClocal 0x8000000000000ULL /// do not forward (see dmd.dsymbol.ForwardingScopeDsymbol).
#define STClive 0x10000000000000ULL /// function `@live` attribute
#define STCregister 0x20000000000000ULL /// `register` storage class (ImportC)
#define STCvolatile 0x40000000000000ULL /// destined for volatile in the back end
#define STCundefined 0LL
#define STCstatic 1LL
#define STCextern 2LL
#define STCconst 4LL
#define STCfinal 8LL
#define STCabstract 0x10LL
#define STCparameter 0x20LL
#define STCfield 0x40LL
#define STCoverride 0x80LL
#define STCauto 0x100LL
#define STCsynchronized 0x200LL
#define STCdeprecated 0x400LL
#define STCin 0x800LL // in parameter
#define STCout 0x1000LL // out parameter
#define STClazy 0x2000LL // lazy parameter
#define STCforeach 0x4000LL // variable for foreach loop
#define STCvariadic 0x10000LL // the 'variadic' parameter in: T foo(T a, U b, V variadic...)
#define STCctorinit 0x20000LL // can only be set inside constructor
#define STCtemplateparameter 0x40000LL // template parameter
#define STCscope 0x80000LL
#define STCimmutable 0x100000LL
#define STCref 0x200000LL
#define STCinit 0x400000LL // has explicit initializer
#define STCmanifest 0x800000LL // manifest constant
#define STCnodtor 0x1000000LL // don't run destructor
#define STCnothrow 0x2000000LL // never throws exceptions
#define STCpure 0x4000000LL // pure function
#define STCtls 0x8000000LL // thread local
#define STCalias 0x10000000LL // alias parameter
#define STCshared 0x20000000LL // accessible from multiple threads
// accessible from multiple threads
// but not typed as "shared"
#define STCgshared 0x40000000LL
#define STCwild 0x80000000LL // for "wild" type constructor
#define STC_TYPECTOR (STCconst | STCimmutable | STCshared | STCwild)
#define STC_FUNCATTR (STCref | STCnothrow | STCnogc | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem)
#define STCproperty 0x100000000LL
#define STCsafe 0x200000000LL
#define STCtrusted 0x400000000LL
#define STCsystem 0x800000000LL
#define STCctfe 0x1000000000LL // can be used in CTFE, even if it is static
#define STCdisable 0x2000000000LL // for functions that are not callable
#define STCresult 0x4000000000LL // for result variables passed to out contracts
#define STCnodefaultctor 0x8000000000LL // must be set inside constructor
#define STCtemp 0x10000000000LL // temporary variable
#define STCrvalue 0x20000000000LL // force rvalue for variables
#define STCnogc 0x40000000000LL // @nogc
#define STCvolatile 0x80000000000LL // destined for volatile in the back end
#define STCreturn 0x100000000000LL // 'return ref' or 'return scope' for function parameters
#define STCautoref 0x200000000000LL // Mark for the already deduced 'auto ref' parameter
#define STCinference 0x400000000000LL // do attribute inference
#define STCexptemp 0x800000000000LL // temporary variable that has lifetime restricted to an expression
#define STCmaybescope 0x1000000000000LL // parameter might be 'scope'
#define STCscopeinferred 0x2000000000000LL // 'scope' has been inferred and should not be part of mangling
#define STCfuture 0x4000000000000LL // introducing new base class function
#define STClocal 0x8000000000000LL // do not forward (see ddmd.dsymbol.ForwardingScopeDsymbol).
const StorageClass STCStorageClass = (STCauto | STCscope | STCstatic | STCextern | STCconst | STCfinal |
STCabstract | STCsynchronized | STCdeprecated | STCfuture | STCoverride | STClazy | STCalias |
STCout | STCin |
STCmanifest | STCimmutable | STCshared | STCwild | STCnothrow | STCnogc | STCpure | STCref | STCtls |
STCgshared | STCproperty | STCsafe | STCtrusted | STCsystem | STCdisable | STClocal);
struct Match
{
int count; // number of matches found
MATCH last; // match level of lastf
FuncDeclaration *lastf; // last matching function we found
FuncDeclaration *nextf; // current matching function
FuncDeclaration *anyf; // pick a func, any func, to use for error recovery
};
void functionResolve(Match *m, Dsymbol *fd, Loc loc, Scope *sc, Objects *tiargs, Type *tthis, Expressions *fargs, const char **pMessage = NULL);
int overloadApply(Dsymbol *fstart, void *param, int (*fp)(void *, Dsymbol *));
void aliasSemantic(AliasDeclaration *ds, Scope *sc);
void ObjectNotFound(Identifier *id);
/**************************************************************/
@ -132,47 +115,45 @@ public:
Type *type;
Type *originalType; // before semantic analysis
StorageClass storage_class;
Prot protection;
Visibility visibility;
LINK linkage;
int inuse; // used to detect cycles
short inuse; // used to detect cycles
uint8_t adFlags;
DString mangleOverride; // overridden symbol with pragma(mangle, "...")
Declaration(Identifier *id);
const char *kind() const;
d_uns64 size(Loc loc);
bool checkDisabled(Loc loc, Scope *sc, bool isAliasedDeclaration = false);
int checkModify(Loc loc, Scope *sc, Type *t, Expression *e1, int flag);
d_uns64 size(const Loc &loc);
Dsymbol *search(const Loc &loc, Identifier *ident, int flags = SearchLocalsOnly);
bool isStatic() { return (storage_class & STCstatic) != 0; }
bool isStatic() const { return (storage_class & STCstatic) != 0; }
virtual bool isDelete();
virtual bool isDataseg();
virtual bool isThreadlocal();
virtual bool isCodeseg() const;
bool isCtorinit() { return (storage_class & STCctorinit) != 0; }
bool isFinal() { return (storage_class & STCfinal) != 0; }
bool isAbstract() { return (storage_class & STCabstract) != 0; }
bool isConst() { return (storage_class & STCconst) != 0; }
bool isImmutable() { return (storage_class & STCimmutable) != 0; }
bool isWild() { return (storage_class & STCwild) != 0; }
bool isAuto() { return (storage_class & STCauto) != 0; }
bool isScope() { return (storage_class & STCscope) != 0; }
bool isSynchronized() { return (storage_class & STCsynchronized) != 0; }
bool isParameter() { return (storage_class & STCparameter) != 0; }
bool isDeprecated() { return (storage_class & STCdeprecated) != 0; }
bool isDisabled() { return (storage_class & STCdisable) != 0; }
bool isOverride() { return (storage_class & STCoverride) != 0; }
bool isResult() { return (storage_class & STCresult) != 0; }
bool isField() { return (storage_class & STCfield) != 0; }
bool isCtorinit() const { return (storage_class & STCctorinit) != 0; }
bool isFinal() const { return (storage_class & STCfinal) != 0; }
virtual bool isAbstract() { return (storage_class & STCabstract) != 0; }
bool isConst() const { return (storage_class & STCconst) != 0; }
bool isImmutable() const { return (storage_class & STCimmutable) != 0; }
bool isWild() const { return (storage_class & STCwild) != 0; }
bool isAuto() const { return (storage_class & STCauto) != 0; }
bool isScope() const { return (storage_class & STCscope) != 0; }
bool isSynchronized() const { return (storage_class & STCsynchronized) != 0; }
bool isParameter() const { return (storage_class & STCparameter) != 0; }
bool isDeprecated() const { return (storage_class & STCdeprecated) != 0; }
bool isOverride() const { return (storage_class & STCoverride) != 0; }
bool isResult() const { return (storage_class & STCresult) != 0; }
bool isField() const { return (storage_class & STCfield) != 0; }
bool isIn() { return (storage_class & STCin) != 0; }
bool isOut() { return (storage_class & STCout) != 0; }
bool isRef() { return (storage_class & STCref) != 0; }
bool isIn() const { return (storage_class & STCin) != 0; }
bool isOut() const { return (storage_class & STCout) != 0; }
bool isRef() const { return (storage_class & STCref) != 0; }
bool isReference() const { return (storage_class & (STCref | STCout)) != 0; }
bool isFuture() { return (storage_class & STCfuture) != 0; }
bool isFuture() const { return (storage_class & STCfuture) != 0; }
Prot prot();
Visibility visible();
Declaration *isDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -188,8 +169,7 @@ public:
TypeTuple *tupletype; // !=NULL if this is a type tuple
TupleDeclaration(Loc loc, Identifier *ident, Objects *objects);
Dsymbol *syntaxCopy(Dsymbol *);
TupleDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
Type *getType();
Dsymbol *toAlias2();
@ -208,16 +188,14 @@ public:
Dsymbol *overnext; // next in overload list
Dsymbol *_import; // !=NULL if unresolved internal alias for selective import
AliasDeclaration(Loc loc, Identifier *ident, Type *type);
AliasDeclaration(Loc loc, Identifier *ident, Dsymbol *s);
static AliasDeclaration *create(Loc loc, Identifier *id, Type *type);
Dsymbol *syntaxCopy(Dsymbol *);
AliasDeclaration *syntaxCopy(Dsymbol *);
bool overloadInsert(Dsymbol *s);
const char *kind() const;
Type *getType();
Dsymbol *toAlias();
Dsymbol *toAlias2();
bool isOverloadable();
bool isOverloadable() const;
AliasDeclaration *isAliasDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -230,16 +208,14 @@ class OverDeclaration : public Declaration
public:
Dsymbol *overnext; // next in overload list
Dsymbol *aliassym;
bool hasOverloads;
OverDeclaration(Identifier *ident, Dsymbol *s, bool hasOverloads = true);
const char *kind() const;
bool equals(RootObject *o);
bool equals(const RootObject *o) const;
bool overloadInsert(Dsymbol *s);
Dsymbol *toAlias();
Dsymbol *isUnique();
bool isOverloadable();
bool isOverloadable() const;
OverDeclaration *isOverDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -251,33 +227,40 @@ class VarDeclaration : public Declaration
{
public:
Initializer *_init;
FuncDeclarations nestedrefs; // referenced by these lexically nested functions
Dsymbol *aliassym; // if redone as alias to another symbol
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;
unsigned sequenceNumber; // order the variables are declared
FuncDeclarations nestedrefs; // referenced by these lexically nested functions
bool isargptr; // if parameter that _argptr points to
structalign_t alignment;
// When interpreting, these point to the value (NULL if value not determinable)
// The index of this variable on the CTFE stack, ~0u if not allocated
unsigned ctfeAdrOnStack;
bool isargptr; // if parameter that _argptr points to
bool ctorinit; // it has been initialized in a ctor
bool iscatchvar; // this is the exception object variable in catch() clause
bool isowner; // this is an Owner, despite it being `scope`
bool onstack; // it is a class that was allocated on the stack
bool mynew; // it is a class new'd with custom operator new
int canassign; // it can be assigned to
char canassign; // it can be assigned to
bool overlapped; // if it is a field and has overlapping
bool overlapUnsafe; // if it is an overlapping field and the overlaps are unsafe
bool doNotInferScope; // do not infer 'scope' for this variable
bool doNotInferReturn; // do not infer 'return' for this variable
unsigned char isdataseg; // private data for isDataseg
Dsymbol *aliassym; // if redone as alias to another symbol
VarDeclaration *lastVar; // Linked list of variables for goto-skips-init detection
unsigned endlinnum; // line number of end of scope that this var lives in
bool isArgDtorVar; // temporary created to handle scope destruction of a function argument
// When interpreting, these point to the value (NULL if value not determinable)
// The index of this variable on the CTFE stack, -1 if not allocated
int ctfeAdrOnStack;
Expression *edtor; // if !=NULL, does the destruction of the variable
IntRange *range; // if !NULL, the variable is known to be within the range
VarDeclaration(Loc loc, Type *t, Identifier *id, Initializer *init);
static VarDeclaration *create(Loc loc, Type *t, Identifier *id, Initializer *init);
Dsymbol *syntaxCopy(Dsymbol *);
void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion);
public:
static VarDeclaration *create(const Loc &loc, Type *t, Identifier *id, Initializer *init, StorageClass storage_class = STCundefined);
VarDeclaration *syntaxCopy(Dsymbol *);
void setFieldOffset(AggregateDeclaration *ad, FieldState& fieldState, bool isunion);
const char *kind() const;
AggregateDeclaration *isThis();
bool needThis();
@ -291,11 +274,7 @@ public:
bool canTakeAddressOf();
bool needsScopeDtor();
bool enclosesLifetimeOf(VarDeclaration *v) const;
Expression *callScopeDtor(Scope *sc);
Expression *getConstInitializer(bool needFullType = true);
Expression *expandInitializer(Loc loc);
void checkCtorConstInit();
bool checkNestedReference(Scope *sc, Loc loc);
Dsymbol *toAlias();
// Eliminate need for dynamic_cast
VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; }
@ -304,6 +283,21 @@ public:
/**************************************************************/
class BitFieldDeclaration : public VarDeclaration
{
public:
Expression *width;
unsigned fieldWidth;
unsigned bitOffset;
BitFieldDeclaration *syntaxCopy(Dsymbol*);
BitFieldDeclaration *isBitFieldDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
/**************************************************************/
// This is a shell around a back end symbol
class SymbolDeclaration : public Declaration
@ -311,8 +305,6 @@ class SymbolDeclaration : public Declaration
public:
StructDeclaration *dsym;
SymbolDeclaration(Loc loc, StructDeclaration *dsym);
// Eliminate need for dynamic_cast
SymbolDeclaration *isSymbolDeclaration() { return (SymbolDeclaration *)this; }
void accept(Visitor *v) { v->visit(this); }
@ -323,10 +315,9 @@ class TypeInfoDeclaration : public VarDeclaration
public:
Type *tinfo;
TypeInfoDeclaration(Type *tinfo);
static TypeInfoDeclaration *create(Type *tinfo);
Dsymbol *syntaxCopy(Dsymbol *);
const char *toChars();
TypeInfoDeclaration *syntaxCopy(Dsymbol *);
const char *toChars() const;
TypeInfoDeclaration *isTypeInfoDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -335,7 +326,6 @@ public:
class TypeInfoStructDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoStructDeclaration(Type *tinfo);
static TypeInfoStructDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -344,7 +334,6 @@ public:
class TypeInfoClassDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoClassDeclaration(Type *tinfo);
static TypeInfoClassDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -353,7 +342,6 @@ public:
class TypeInfoInterfaceDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoInterfaceDeclaration(Type *tinfo);
static TypeInfoInterfaceDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -362,7 +350,6 @@ public:
class TypeInfoPointerDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoPointerDeclaration(Type *tinfo);
static TypeInfoPointerDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -371,7 +358,6 @@ public:
class TypeInfoArrayDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoArrayDeclaration(Type *tinfo);
static TypeInfoArrayDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -380,7 +366,6 @@ public:
class TypeInfoStaticArrayDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoStaticArrayDeclaration(Type *tinfo);
static TypeInfoStaticArrayDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -389,7 +374,6 @@ public:
class TypeInfoAssociativeArrayDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoAssociativeArrayDeclaration(Type *tinfo);
static TypeInfoAssociativeArrayDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -398,7 +382,6 @@ public:
class TypeInfoEnumDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoEnumDeclaration(Type *tinfo);
static TypeInfoEnumDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -407,7 +390,6 @@ public:
class TypeInfoFunctionDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoFunctionDeclaration(Type *tinfo);
static TypeInfoFunctionDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -416,7 +398,6 @@ public:
class TypeInfoDelegateDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoDelegateDeclaration(Type *tinfo);
static TypeInfoDelegateDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -425,7 +406,6 @@ public:
class TypeInfoTupleDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoTupleDeclaration(Type *tinfo);
static TypeInfoTupleDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -434,7 +414,6 @@ public:
class TypeInfoConstDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoConstDeclaration(Type *tinfo);
static TypeInfoConstDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -443,7 +422,6 @@ public:
class TypeInfoInvariantDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoInvariantDeclaration(Type *tinfo);
static TypeInfoInvariantDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -452,7 +430,6 @@ public:
class TypeInfoSharedDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoSharedDeclaration(Type *tinfo);
static TypeInfoSharedDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -461,7 +438,6 @@ public:
class TypeInfoWildDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoWildDeclaration(Type *tinfo);
static TypeInfoWildDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -470,7 +446,6 @@ public:
class TypeInfoVectorDeclaration : public TypeInfoDeclaration
{
public:
TypeInfoVectorDeclaration(Type *tinfo);
static TypeInfoVectorDeclaration *create(Type *tinfo);
void accept(Visitor *v) { v->visit(this); }
@ -481,13 +456,12 @@ public:
class ThisDeclaration : public VarDeclaration
{
public:
ThisDeclaration(Loc loc, Type *t);
Dsymbol *syntaxCopy(Dsymbol *);
ThisDeclaration *syntaxCopy(Dsymbol *);
ThisDeclaration *isThisDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
enum ILS
enum class ILS : unsigned char
{
ILSuninitialized, // not computed yet
ILSno, // cannot inline
@ -496,68 +470,53 @@ enum ILS
/**************************************************************/
enum BUILTIN
enum class BUILTIN : unsigned char
{
BUILTINunknown = 255, /// not known if this is a builtin
BUILTINunimp = 0, /// this is not a builtin
BUILTINgcc, /// this is a GCC builtin
BUILTINllvm, /// this is an LLVM builtin
BUILTINsin,
BUILTINcos,
BUILTINtan,
BUILTINsqrt,
BUILTINfabs,
BUILTINldexp,
BUILTINlog,
BUILTINlog2,
BUILTINlog10,
BUILTINexp,
BUILTINexpm1,
BUILTINexp2,
BUILTINround,
BUILTINfloor,
BUILTINceil,
BUILTINtrunc,
BUILTINcopysign,
BUILTINpow,
BUILTINfmin,
BUILTINfmax,
BUILTINfma,
BUILTINisnan,
BUILTINisinfinity,
BUILTINisfinite,
BUILTINbsf,
BUILTINbsr,
BUILTINbswap,
BUILTINpopcnt,
BUILTINyl2x,
BUILTINyl2xp1,
BUILTINtoPrecFloat,
BUILTINtoPrecDouble,
BUILTINtoPrecReal
unknown = 255, /// not known if this is a builtin
unimp = 0, /// this is not a builtin
gcc, /// this is a GCC builtin
llvm, /// this is an LLVM builtin
sin,
cos,
tan,
sqrt,
fabs,
ldexp,
log,
log2,
log10,
exp,
expm1,
exp2,
round,
floor,
ceil,
trunc,
copysign,
pow,
fmin,
fmax,
fma,
isnan,
isinfinity,
isfinite,
bsf,
bsr,
bswap,
popcnt,
yl2x,
yl2xp1,
toPrecFloat,
toPrecDouble,
toPrecReal
};
Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments);
BUILTIN isBuiltin(FuncDeclaration *fd);
typedef Expression *(*builtin_fp)(Loc loc, FuncDeclaration *fd, Expressions *arguments);
void add_builtin(const char *mangle, builtin_fp fp);
void builtin_init();
#define FUNCFLAGpurityInprocess 1 // working on determining purity
#define FUNCFLAGsafetyInprocess 2 // working on determining safety
#define FUNCFLAGnothrowInprocess 4 // working on determining nothrow
#define FUNCFLAGnogcInprocess 8 // working on determining @nogc
#define FUNCFLAGreturnInprocess 0x10 // working on inferring 'return' for parameters
#define FUNCFLAGinlineScanned 0x20 // function has been scanned for inline possibilities
#define FUNCFLAGinferScope 0x40 // infer 'scope' for parameters
#define FUNCFLAGprintf 0x200 // is a printf-like function
#define FUNCFLAGscanf 0x400 // is a scanf-like function
class FuncDeclaration : public Declaration
{
public:
Types *fthrows; // Array of Type's of exceptions (not used)
Statements *frequires; // in contracts
Ensures *fensures; // out contracts
Statement *frequire; // lowered in contract
@ -568,6 +527,9 @@ public:
FuncDeclaration *fdrequire; // function that does the in contract
FuncDeclaration *fdensure; // function that does the out contract
Expressions *fdrequireParams; // argument list for __require
Expressions *fdensureParams; // argument list for __ensure
const char *mangleString; // mangled symbol created from mangleExact()
VarDeclaration *vresult; // result variable for out contracts
@ -577,8 +539,9 @@ public:
// scopes from having the same name
DsymbolTable *localsymtab;
VarDeclaration *vthis; // 'this' parameter (member and nested)
bool isThis2; // has a dual-context 'this' parameter
VarDeclaration *v_arguments; // '_arguments' parameter
ObjcSelector* selector; // Objective-C method selector (member function only)
VarDeclaration *v_argptr; // '_argptr' variable
VarDeclarations *parameters; // Array of VarDeclaration's for parameters
DsymbolTable *labtab; // statement label symbol table
@ -589,13 +552,16 @@ public:
bool naked; // true if naked
bool generated; // true if function was generated by the compiler rather than
// supplied by the user
bool hasAlwaysInlines; // contains references to functions that must be inlined
unsigned char isCrtCtorDtor; // has attribute pragma(crt_constructor(1)/crt_destructor(2))
// not set before the glue layer
ILS inlineStatusStmt;
ILS inlineStatusExp;
PINLINE inlining;
CompiledCtfeFunction *ctfeCode; // Compiled code for interpreter
int inlineNest; // !=0 if nested inline
bool isArrayOp; // true if array operation
bool eh_none; /// true if no exception unwinding is needed
// true if errors in semantic3 this function's frame ptr
bool semantic3Errors;
ForeachStatement *fes; // if foreach body, this is the foreach
@ -635,6 +601,12 @@ public:
// local variables in this function which are referenced by nested functions
VarDeclarations closureVars;
/** Outer variables which are referenced by this nested function
* (the inverse of closureVars)
*/
VarDeclarations outerVars;
// Sibling nested functions which called this one
FuncDeclarations siblingCallers;
@ -642,76 +614,62 @@ public:
unsigned flags; // FUNCFLAGxxxxx
FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type);
static FuncDeclaration *create(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type);
Dsymbol *syntaxCopy(Dsymbol *);
// 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);
FuncDeclaration *syntaxCopy(Dsymbol *);
bool functionSemantic();
bool functionSemantic3();
bool checkForwardRef(Loc loc);
// called from semantic3
VarDeclaration *declareThis(Scope *sc, AggregateDeclaration *ad);
bool equals(RootObject *o);
bool equals(const RootObject *o) const;
int overrides(FuncDeclaration *fd);
int findVtblIndex(Dsymbols *vtbl, int dim, bool fix17349 = true);
int findVtblIndex(Dsymbols *vtbl, int dim);
BaseClass *overrideInterface();
bool overloadInsert(Dsymbol *s);
FuncDeclaration *overloadExactMatch(Type *t);
FuncDeclaration *overloadModMatch(Loc loc, Type *tthis, bool &hasOverloads);
TemplateDeclaration *findTemplateDeclRoot();
bool inUnittest();
MATCH leastAsSpecialized(FuncDeclaration *g);
LabelDsymbol *searchLabel(Identifier *ident);
int getLevel(Loc loc, Scope *sc, FuncDeclaration *fd); // lexical nesting level difference
LabelDsymbol *searchLabel(Identifier *ident, const Loc &loc);
int getLevel(FuncDeclaration *fd, int intypeof); // lexical nesting level difference
int getLevelAndCheck(const Loc &loc, Scope *sc, FuncDeclaration *fd);
const char *toPrettyChars(bool QualifyTypes = false);
const char *toFullSignature(); // for diagnostics, e.g. 'int foo(int x, int y) pure'
bool isMain();
bool isCMain();
bool isWinMain();
bool isDllMain();
bool isMain() const;
bool isCMain() const;
bool isWinMain() const;
bool isDllMain() const;
bool isExport() const;
bool isImportedSymbol() const;
bool isCodeseg() const;
bool isOverloadable();
bool isOverloadable() const;
bool isAbstract();
PURE isPure();
PURE isPureBypassingInference();
bool setImpure();
bool isSafe();
bool isSafeBypassingInference();
bool isTrusted();
bool setUnsafe();
bool isNogc();
bool isNogcBypassingInference();
bool setGC();
void printGCUsage(Loc loc, const char *warn);
bool isolateReturn();
bool parametersIntersect(Type *t);
virtual bool isNested();
virtual bool isNested() const;
AggregateDeclaration *isThis();
bool needThis();
bool isVirtualMethod();
virtual bool isVirtual();
virtual bool isFinalFunc();
virtual bool isVirtual() const;
bool isFinalFunc() const;
virtual bool addPreInvariant();
virtual bool addPostInvariant();
const char *kind() const;
FuncDeclaration *isUnique();
bool checkNestedReference(Scope *sc, Loc loc);
bool isUnique();
bool needsClosure();
bool checkClosure();
bool hasNestedFrameRefs();
void buildResultVar(Scope *sc, Type *tret);
Statement *mergeFrequire(Statement *);
static bool needsFensure(FuncDeclaration *fd);
void buildEnsureRequire();
Statement *mergeFensure(Statement *, Identifier *oid);
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);
void checkDmain();
bool checkNRVO();
FuncDeclaration *isFuncDeclaration() { return this; }
@ -720,20 +678,12 @@ public:
void accept(Visitor *v) { v->visit(this); }
};
FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s,
Objects *tiargs,
Type *tthis,
Expressions *arguments,
int flags = 0);
class FuncAliasDeclaration : public FuncDeclaration
{
public:
FuncDeclaration *funcalias;
bool hasOverloads;
FuncAliasDeclaration(Identifier *ident, FuncDeclaration *funcalias, bool hasOverloads = true);
FuncAliasDeclaration *isFuncAliasDeclaration() { return this; }
const char *kind() const;
@ -750,12 +700,10 @@ public:
// backend
bool deferToObj;
FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type, TOK tok,
ForeachStatement *fes, Identifier *id = NULL);
Dsymbol *syntaxCopy(Dsymbol *);
bool isNested();
FuncLiteralDeclaration *syntaxCopy(Dsymbol *);
bool isNested() const;
AggregateDeclaration *isThis();
bool isVirtual();
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@ -770,11 +718,11 @@ public:
class CtorDeclaration : public FuncDeclaration
{
public:
CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type);
Dsymbol *syntaxCopy(Dsymbol *);
bool isCpCtor;
CtorDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
const char *toChars();
bool isVirtual();
const char *toChars() const;
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@ -785,9 +733,8 @@ public:
class PostBlitDeclaration : public FuncDeclaration
{
public:
PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id);
Dsymbol *syntaxCopy(Dsymbol *);
bool isVirtual();
PostBlitDeclaration *syntaxCopy(Dsymbol *);
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
bool overloadInsert(Dsymbol *s);
@ -799,12 +746,10 @@ public:
class DtorDeclaration : public FuncDeclaration
{
public:
DtorDeclaration(Loc loc, Loc endloc);
DtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id);
Dsymbol *syntaxCopy(Dsymbol *);
DtorDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
const char *toChars();
bool isVirtual();
const char *toChars() const;
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
bool overloadInsert(Dsymbol *s);
@ -816,11 +761,9 @@ public:
class StaticCtorDeclaration : public FuncDeclaration
{
public:
StaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
StaticCtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc);
Dsymbol *syntaxCopy(Dsymbol *);
StaticCtorDeclaration *syntaxCopy(Dsymbol *);
AggregateDeclaration *isThis();
bool isVirtual();
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
bool hasStaticCtorOrDtor();
@ -832,8 +775,7 @@ public:
class SharedStaticCtorDeclaration : public StaticCtorDeclaration
{
public:
SharedStaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
Dsymbol *syntaxCopy(Dsymbol *);
SharedStaticCtorDeclaration *syntaxCopy(Dsymbol *);
SharedStaticCtorDeclaration *isSharedStaticCtorDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -844,11 +786,9 @@ class StaticDtorDeclaration : public FuncDeclaration
public:
VarDeclaration *vgate; // 'gate' variable
StaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
StaticDtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc);
Dsymbol *syntaxCopy(Dsymbol *);
StaticDtorDeclaration *syntaxCopy(Dsymbol *);
AggregateDeclaration *isThis();
bool isVirtual();
bool isVirtual() const;
bool hasStaticCtorOrDtor();
bool addPreInvariant();
bool addPostInvariant();
@ -860,8 +800,7 @@ public:
class SharedStaticDtorDeclaration : public StaticDtorDeclaration
{
public:
SharedStaticDtorDeclaration(Loc loc, Loc endloc, StorageClass stc);
Dsymbol *syntaxCopy(Dsymbol *);
SharedStaticDtorDeclaration *syntaxCopy(Dsymbol *);
SharedStaticDtorDeclaration *isSharedStaticDtorDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
@ -870,9 +809,8 @@ public:
class InvariantDeclaration : public FuncDeclaration
{
public:
InvariantDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id = NULL);
Dsymbol *syntaxCopy(Dsymbol *);
bool isVirtual();
InvariantDeclaration *syntaxCopy(Dsymbol *);
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@ -888,10 +826,9 @@ public:
// toObjFile() these nested functions after this one
FuncDeclarations deferredNested;
UnitTestDeclaration(Loc loc, Loc endloc, StorageClass stc, char *codedoc);
Dsymbol *syntaxCopy(Dsymbol *);
UnitTestDeclaration *syntaxCopy(Dsymbol *);
AggregateDeclaration *isThis();
bool isVirtual();
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
@ -905,31 +842,12 @@ public:
Parameters *parameters;
VarArg varargs;
NewDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *arguments, VarArg varargs);
Dsymbol *syntaxCopy(Dsymbol *);
NewDeclaration *syntaxCopy(Dsymbol *);
const char *kind() const;
bool isVirtual();
bool isVirtual() const;
bool addPreInvariant();
bool addPostInvariant();
NewDeclaration *isNewDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};
class DeleteDeclaration : public FuncDeclaration
{
public:
Parameters *parameters;
DeleteDeclaration(Loc loc, Loc endloc, StorageClass stc, Parameters *arguments);
Dsymbol *syntaxCopy(Dsymbol *);
const char *kind() const;
bool isDelete();
bool isVirtual();
bool addPreInvariant();
bool addPostInvariant();
DeleteDeclaration *isDeleteDeclaration() { return this; }
void accept(Visitor *v) { v->visit(this); }
};

View file

@ -1,208 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/delegatize.c
*/
#include "root/dsystem.h"
#include "mars.h"
#include "expression.h"
#include "statement.h"
#include "mtype.h"
#include "utf.h"
#include "declaration.h"
#include "aggregate.h"
#include "scope.h"
#include "init.h"
#include "tokens.h"
bool walkPostorder(Expression *e, StoppableVisitor *v);
void lambdaSetParent(Expression *e, Scope *sc);
bool lambdaCheckForNestedRef(Expression *e, Scope *sc);
/********************************************
* Convert from expression to delegate that returns the expression,
* i.e. convert:
* expr
* to:
* typeof(expr) delegate() { return expr; }
*/
Expression *toDelegate(Expression *e, Type* t, Scope *sc)
{
//printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), e->toChars());
Loc loc = e->loc;
TypeFunction *tf = new TypeFunction(ParameterList(), t, LINKd);
if (t->hasWild())
tf->mod = MODwild;
FuncLiteralDeclaration *fld =
new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, NULL);
sc = sc->push();
sc->parent = fld; // set current function to be the delegate
lambdaSetParent(e, sc);
bool r = lambdaCheckForNestedRef(e, sc);
sc = sc->pop();
if (r)
return new ErrorExp();
Statement *s;
if (t->ty == Tvoid)
s = new ExpStatement(loc, e);
else
s = new ReturnStatement(loc, e);
fld->fbody = s;
e = new FuncExp(loc, fld);
e = expressionSemantic(e, sc);
return e;
}
/******************************************
* Patch the parent of declarations to be the new function literal.
*/
void lambdaSetParent(Expression *e, Scope *sc)
{
class LambdaSetParent : public StoppableVisitor
{
Scope *sc;
public:
LambdaSetParent(Scope *sc) : sc(sc) {}
void visit(Expression *)
{
}
void visit(DeclarationExp *e)
{
e->declaration->parent = sc->parent;
}
void visit(IndexExp *e)
{
if (e->lengthVar)
{
//printf("lengthVar\n");
e->lengthVar->parent = sc->parent;
}
}
void visit(SliceExp *e)
{
if (e->lengthVar)
{
//printf("lengthVar\n");
e->lengthVar->parent = sc->parent;
}
}
};
LambdaSetParent lsp(sc);
walkPostorder(e, &lsp);
}
/*******************************************
* Look for references to variables in a scope enclosing the new function literal.
* Returns true if error occurs.
*/
bool lambdaCheckForNestedRef(Expression *e, Scope *sc)
{
class LambdaCheckForNestedRef : public StoppableVisitor
{
public:
Scope *sc;
bool result;
LambdaCheckForNestedRef(Scope *sc)
: sc(sc), result(false)
{
}
void visit(Expression *)
{
}
void visit(SymOffExp *e)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
result = v->checkNestedReference(sc, Loc());
}
void visit(VarExp *e)
{
VarDeclaration *v = e->var->isVarDeclaration();
if (v)
result = v->checkNestedReference(sc, Loc());
}
void visit(ThisExp *e)
{
if (e->var)
result = e->var->checkNestedReference(sc, Loc());
}
void visit(DeclarationExp *e)
{
VarDeclaration *v = e->declaration->isVarDeclaration();
if (v)
{
result = v->checkNestedReference(sc, Loc());
if (result)
return;
/* Some expressions cause the frontend to create a temporary.
* For example, structs with cpctors replace the original
* expression e with:
* __cpcttmp = __cpcttmp.cpctor(e);
*
* In this instance, we need to ensure that the original
* expression e does not have any nested references by
* checking the declaration initializer too.
*/
if (v->_init && v->_init->isExpInitializer())
{
Expression *ie = initializerToExpression(v->_init);
result = lambdaCheckForNestedRef(ie, sc);
}
}
}
};
LambdaCheckForNestedRef v(sc);
walkPostorder(e, &v);
return v.result;
}
bool checkNestedRef(Dsymbol *s, Dsymbol *p)
{
while (s)
{
if (s == p) // hit!
return false;
if (FuncDeclaration *fd = s->isFuncDeclaration())
{
if (!fd->isThis() && !fd->isNested())
break;
// Bugzilla 15332: change to delegate if fd is actually nested.
if (FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration())
fld->tok = TOKdelegate;
}
if (AggregateDeclaration *ad = s->isAggregateDeclaration())
{
if (ad->storage_class & STCstatic)
break;
}
s = s->toParent2();
}
return true;
}

305
gcc/d/dmd/delegatize.d Normal file
View file

@ -0,0 +1,305 @@
/**
* Implements conversion from expressions to delegates for lazy parameters.
*
* Specification: $(LINK2 https://dlang.org/spec/function.html#lazy-params, Lazy Parameters)
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.delegatize;
import core.stdc.stdio;
import dmd.apply;
import dmd.astenums;
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
import dmd.expression;
import dmd.expressionsem;
import dmd.func;
import dmd.globals;
import dmd.init;
import dmd.initsem;
import dmd.mtype;
import dmd.statement;
import dmd.tokens;
import dmd.visitor;
/*********************************
* Convert expression into a delegate.
*
* Used to convert the argument to a lazy parameter.
*
* Params:
* e = argument to convert to a delegate
* t = the type to be returned by the delegate
* sc = context
* Returns:
* A delegate literal
*/
Expression toDelegate(Expression e, Type t, Scope* sc)
{
//printf("Expression::toDelegate(t = %s) %s\n", t.toChars(), e.toChars());
Loc loc = e.loc;
auto tf = new TypeFunction(ParameterList(), t, LINK.d);
if (t.hasWild())
tf.mod = MODFlags.wild;
auto fld = new FuncLiteralDeclaration(loc, loc, tf, TOK.delegate_, null);
lambdaSetParent(e, fld);
sc = sc.push();
sc.parent = fld; // set current function to be the delegate
bool r = lambdaCheckForNestedRef(e, sc);
sc = sc.pop();
if (r)
return ErrorExp.get();
Statement s;
if (t.ty == Tvoid)
s = new ExpStatement(loc, e);
else
s = new ReturnStatement(loc, e);
fld.fbody = s;
e = new FuncExp(loc, fld);
e = e.expressionSemantic(sc);
return e;
}
/******************************************
* Patch the parent of declarations to be the new function literal.
*
* Since the expression is going to be moved into a function literal,
* the parent for declarations in the expression needs to be
* reset to that function literal.
* Params:
* e = expression to check
* fd = function literal symbol (the new parent)
*/
private void lambdaSetParent(Expression e, FuncDeclaration fd)
{
extern (C++) final class LambdaSetParent : StoppableVisitor
{
alias visit = typeof(super).visit;
FuncDeclaration fd;
private void setParent(Dsymbol s)
{
VarDeclaration vd = s.isVarDeclaration();
FuncDeclaration pfd = s.parent ? s.parent.isFuncDeclaration() : null;
s.parent = fd;
if (!vd || !pfd)
return;
// move to fd's closure when applicable
foreach (i; 0 .. pfd.closureVars.dim)
{
if (vd == pfd.closureVars[i])
{
pfd.closureVars.remove(i);
fd.closureVars.push(vd);
break;
}
}
}
public:
extern (D) this(FuncDeclaration fd)
{
this.fd = fd;
}
override void visit(Expression)
{
}
override void visit(DeclarationExp e)
{
setParent(e.declaration);
e.declaration.accept(this);
}
override void visit(IndexExp e)
{
if (e.lengthVar)
{
//printf("lengthVar\n");
setParent(e.lengthVar);
e.lengthVar.accept(this);
}
}
override void visit(SliceExp e)
{
if (e.lengthVar)
{
//printf("lengthVar\n");
setParent(e.lengthVar);
e.lengthVar.accept(this);
}
}
override void visit(Dsymbol)
{
}
override void visit(VarDeclaration v)
{
if (v._init)
v._init.accept(this);
}
override void visit(Initializer)
{
}
override void visit(ExpInitializer ei)
{
walkPostorder(ei.exp ,this);
}
override void visit(StructInitializer si)
{
foreach (i, const id; si.field)
if (Initializer iz = si.value[i])
iz.accept(this);
}
override void visit(ArrayInitializer ai)
{
foreach (i, ex; ai.index)
{
if (ex)
walkPostorder(ex, this);
if (Initializer iz = ai.value[i])
iz.accept(this);
}
}
}
scope LambdaSetParent lsp = new LambdaSetParent(fd);
walkPostorder(e, lsp);
}
/*******************************************
* Look for references to variables in a scope enclosing the new function literal.
*
* Essentially just calls `checkNestedReference() for each variable reference in `e`.
* Params:
* sc = context
* e = expression to check
* Returns:
* true if error occurs.
*/
bool lambdaCheckForNestedRef(Expression e, Scope* sc)
{
extern (C++) final class LambdaCheckForNestedRef : StoppableVisitor
{
alias visit = typeof(super).visit;
public:
Scope* sc;
bool result;
extern (D) this(Scope* sc)
{
this.sc = sc;
}
override void visit(Expression)
{
}
override void visit(SymOffExp e)
{
VarDeclaration v = e.var.isVarDeclaration();
if (v)
result = v.checkNestedReference(sc, Loc.initial);
}
override void visit(VarExp e)
{
VarDeclaration v = e.var.isVarDeclaration();
if (v)
result = v.checkNestedReference(sc, Loc.initial);
}
override void visit(ThisExp e)
{
if (e.var)
result = e.var.checkNestedReference(sc, Loc.initial);
}
override void visit(DeclarationExp e)
{
VarDeclaration v = e.declaration.isVarDeclaration();
if (v)
{
result = v.checkNestedReference(sc, Loc.initial);
if (result)
return;
/* Some expressions cause the frontend to create a temporary.
* For example, structs with cpctors replace the original
* expression e with:
* __cpcttmp = __cpcttmp.cpctor(e);
*
* In this instance, we need to ensure that the original
* expression e does not have any nested references by
* checking the declaration initializer too.
*/
if (v._init && v._init.isExpInitializer())
{
Expression ie = v._init.initializerToExpression();
result = lambdaCheckForNestedRef(ie, sc);
}
}
}
}
scope LambdaCheckForNestedRef v = new LambdaCheckForNestedRef(sc);
walkPostorder(e, v);
return v.result;
}
/*****************************************
* See if context `s` is nested within context `p`, meaning
* it `p` is reachable at runtime by walking the static links.
* If any of the intervening contexts are function literals,
* make sure they are delegates.
* Params:
* s = inner context
* p = outer context
* Returns:
* true means it is accessible by walking the context pointers at runtime
* References:
* for static links see https://en.wikipedia.org/wiki/Call_stack#Functions_of_the_call_stack
*/
bool ensureStaticLinkTo(Dsymbol s, Dsymbol p)
{
while (s)
{
if (s == p) // hit!
return true;
if (auto fd = s.isFuncDeclaration())
{
if (!fd.isThis() && !fd.isNested())
break;
// https://issues.dlang.org/show_bug.cgi?id=15332
// change to delegate if fd is actually nested.
if (auto fld = fd.isFuncLiteralDeclaration())
fld.tok = TOK.delegate_;
}
if (auto ad = s.isAggregateDeclaration())
{
if (ad.storage_class & STC.static_)
break;
}
s = s.toParentP(p);
}
return false;
}

View file

@ -1,388 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/enum.c
*/
#include "root/dsystem.h"
#include "root/root.h"
#include "errors.h"
#include "enum.h"
#include "attrib.h"
#include "mtype.h"
#include "scope.h"
#include "id.h"
#include "expression.h"
#include "module.h"
#include "declaration.h"
#include "init.h"
bool isSpecialEnumIdent(const Identifier *ident)
{
return ident == Id::__c_long ||
ident == Id::__c_ulong ||
ident == Id::__c_longlong ||
ident == Id::__c_ulonglong ||
ident == Id::__c_long_double ||
ident == Id::__c_wchar_t ||
ident == Id::__c_complex_float ||
ident == Id::__c_complex_double ||
ident == Id::__c_complex_real;
}
/********************************* EnumDeclaration ****************************/
EnumDeclaration::EnumDeclaration(Loc loc, Identifier *id, Type *memtype)
: ScopeDsymbol(id)
{
//printf("EnumDeclaration() %s\n", toChars());
this->loc = loc;
type = new TypeEnum(this);
this->memtype = memtype;
maxval = NULL;
minval = NULL;
defaultval = NULL;
sinit = NULL;
isdeprecated = false;
protection = Prot(Prot::undefined);
parent = NULL;
added = false;
inuse = 0;
}
Dsymbol *EnumDeclaration::syntaxCopy(Dsymbol *s)
{
assert(!s);
EnumDeclaration *ed = new EnumDeclaration(loc, ident,
memtype ? memtype->syntaxCopy() : NULL);
return ScopeDsymbol::syntaxCopy(ed);
}
void EnumDeclaration::setScope(Scope *sc)
{
if (semanticRun > PASSinit)
return;
ScopeDsymbol::setScope(sc);
}
void EnumDeclaration::addMember(Scope *sc, ScopeDsymbol *sds)
{
/* Anonymous enum members get added to enclosing scope.
*/
ScopeDsymbol *scopesym = isAnonymous() ? sds : this;
if (!isAnonymous())
{
ScopeDsymbol::addMember(sc, sds);
if (!symtab)
symtab = new DsymbolTable();
}
if (members)
{
for (size_t i = 0; i < members->length; i++)
{
EnumMember *em = (*members)[i]->isEnumMember();
em->ed = this;
//printf("add %s to scope %s\n", em->toChars(), scopesym->toChars());
em->addMember(sc, isAnonymous() ? scopesym : this);
}
}
added = true;
}
/******************************
* Get the value of the .max/.min property as an Expression
* Input:
* id Id::max or Id::min
*/
Expression *EnumDeclaration::getMaxMinValue(Loc loc, Identifier *id)
{
//printf("EnumDeclaration::getMaxValue()\n");
bool first = true;
Expression **pval = (id == Id::max) ? &maxval : &minval;
if (inuse)
{
error(loc, "recursive definition of .%s property", id->toChars());
goto Lerrors;
}
if (*pval)
goto Ldone;
if (_scope)
dsymbolSemantic(this, _scope);
if (errors)
goto Lerrors;
if (!members)
{
if (isSpecial())
{
/* Allow these special enums to not need a member list
*/
return memtype->getProperty(loc, id, 0);
}
error(loc, "is opaque and has no `.%s`", id->toChars());
goto Lerrors;
}
if (!(memtype && memtype->isintegral()))
{
error(loc, "has no .%s property because base type %s is not an integral type",
id->toChars(),
memtype ? memtype->toChars() : "");
goto Lerrors;
}
for (size_t i = 0; i < members->length; i++)
{
EnumMember *em = (*members)[i]->isEnumMember();
if (!em)
continue;
if (em->errors)
{
errors = true;
continue;
}
if (em->semanticRun < PASSsemanticdone)
{
em->error("is forward referenced looking for `.%s`", id->toChars());
errors = true;
continue;
}
if (first)
{
*pval = em->value();
first = false;
}
else
{
/* In order to work successfully with UDTs,
* build expressions to do the comparisons,
* and let the semantic analyzer and constant
* folder give us the result.
*/
/* Compute:
* if (e > maxval)
* maxval = e;
*/
Expression *e = em->value();
Expression *ec = new CmpExp(id == Id::max ? TOKgt : TOKlt, em->loc, e, *pval);
inuse++;
ec = expressionSemantic(ec, em->_scope);
inuse--;
ec = ec->ctfeInterpret();
if (ec->op == TOKerror)
{
errors = true;
continue;
}
if (ec->toInteger())
*pval = e;
}
}
if (errors)
goto Lerrors;
Ldone:
{
Expression *e = *pval;
if (e->op != TOKerror)
{
e = e->copy();
e->loc = loc;
}
return e;
}
Lerrors:
*pval = new ErrorExp();
return *pval;
}
/****************
* Determine if enum is a 'special' one.
* Returns:
* true if special
*/
bool EnumDeclaration::isSpecial() const
{
return isSpecialEnumIdent(ident) && memtype;
}
Expression *EnumDeclaration::getDefaultValue(Loc loc)
{
//printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars());
if (defaultval)
return defaultval;
if (_scope)
dsymbolSemantic(this, _scope);
if (errors)
goto Lerrors;
if (!members)
{
if (isSpecial())
{
/* Allow these special enums to not need a member list
*/
defaultval = memtype->defaultInit(loc);
return defaultval;
}
error(loc, "is opaque and has no default initializer");
goto Lerrors;
}
for (size_t i = 0; i < members->length; i++)
{
EnumMember *em = (*members)[i]->isEnumMember();
if (em)
{
if (em->semanticRun < PASSsemanticdone)
{
error(loc, "forward reference of `%s.init`", toChars());
goto Lerrors;
}
defaultval = em->value();
return defaultval;
}
}
Lerrors:
defaultval = new ErrorExp();
return defaultval;
}
Type *EnumDeclaration::getMemtype(Loc loc)
{
if (loc.linnum == 0)
loc = this->loc;
if (_scope)
{
/* Enum is forward referenced. We don't need to resolve the whole thing,
* just the base type
*/
if (memtype)
memtype = typeSemantic(memtype, loc, _scope);
}
if (!memtype)
{
if (!isAnonymous() && (members || semanticRun >= PASSsemanticdone))
memtype = Type::tint32;
else
{
error(loc, "is forward referenced looking for base type");
return Type::terror;
}
}
return memtype;
}
bool EnumDeclaration::oneMember(Dsymbol **ps, Identifier *ident)
{
if (isAnonymous())
return Dsymbol::oneMembers(members, ps, ident);
return Dsymbol::oneMember(ps, ident);
}
Type *EnumDeclaration::getType()
{
return type;
}
const char *EnumDeclaration::kind() const
{
return "enum";
}
bool EnumDeclaration::isDeprecated()
{
return isdeprecated;
}
Prot EnumDeclaration::prot()
{
return protection;
}
Dsymbol *EnumDeclaration::search(const Loc &loc, Identifier *ident, int flags)
{
//printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident->toChars());
if (_scope)
{
// Try one last time to resolve this enum
dsymbolSemantic(this, _scope);
}
Dsymbol *s = ScopeDsymbol::search(loc, ident, flags);
return s;
}
/********************************* EnumMember ****************************/
EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *origType)
: VarDeclaration(loc, NULL, id ? id : Id::empty, new ExpInitializer(loc, value))
{
this->ed = NULL;
this->origValue = value;
this->origType = origType;
}
EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *memType,
StorageClass stc, UserAttributeDeclaration *uad, DeprecatedDeclaration *dd)
: VarDeclaration(loc, NULL, id ? id : Id::empty, new ExpInitializer(loc, value))
{
this->ed = NULL;
this->origValue = value;
this->origType = memType;
this->storage_class = stc;
this->userAttribDecl = uad;
this->depdecl = dd;
}
Expression *&EnumMember::value()
{
return ((ExpInitializer*)_init)->exp;
}
Dsymbol *EnumMember::syntaxCopy(Dsymbol *s)
{
assert(!s);
return new EnumMember(loc, ident,
value() ? value()->syntaxCopy() : NULL,
origType ? origType->syntaxCopy() : NULL);
}
const char *EnumMember::kind() const
{
return "enum member";
}
Expression *EnumMember::getVarExp(Loc loc, Scope *sc)
{
dsymbolSemantic(this, sc);
if (errors)
return new ErrorExp();
checkDisabled(loc, sc);
if (depdecl && !depdecl->_scope)
depdecl->_scope = sc;
checkDeprecated(loc, sc);
if (errors)
return new ErrorExp();
Expression *e = new VarExp(loc, this);
return expressionSemantic(e, sc);
}

333
gcc/d/dmd/denum.d Normal file
View file

@ -0,0 +1,333 @@
/**
* Define `enum` declarations and `enum` members.
*
* Specification: $(LINK2 https://dlang.org/spec/enum.html, Enums)
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
* References: https://dlang.org/spec/enum.html
*/
module dmd.denum;
import core.stdc.stdio;
import dmd.attrib;
import dmd.gluelayer;
import dmd.declaration;
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.expression;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.init;
import dmd.mtype;
import dmd.tokens;
import dmd.typesem;
import dmd.visitor;
/***********************************************************
* AST node for `EnumDeclaration`
* https://dlang.org/spec/enum.html#EnumDeclaration
*/
extern (C++) final class EnumDeclaration : ScopeDsymbol
{
/* The separate, and distinct, cases are:
* 1. enum { ... }
* 2. enum : memtype { ... }
* 3. enum id { ... }
* 4. enum id : memtype { ... }
* 5. enum id : memtype;
* 6. enum id;
*/
Type type; // the TypeEnum
Type memtype; // type of the members
Visibility visibility;
Expression maxval;
Expression minval;
Expression defaultval; // default initializer
bool isdeprecated;
bool added;
int inuse;
extern (D) this(const ref Loc loc, Identifier ident, Type memtype)
{
super(loc, ident);
//printf("EnumDeclaration() %s\n", toChars());
type = new TypeEnum(this);
this.memtype = memtype;
visibility = Visibility(Visibility.Kind.undefined);
}
override EnumDeclaration syntaxCopy(Dsymbol s)
{
assert(!s);
auto ed = new EnumDeclaration(loc, ident, memtype ? memtype.syntaxCopy() : null);
ScopeDsymbol.syntaxCopy(ed);
return ed;
}
override void addMember(Scope* sc, ScopeDsymbol sds)
{
version (none)
{
printf("EnumDeclaration::addMember() %s\n", toChars());
for (size_t i = 0; i < members.dim; i++)
{
EnumMember em = (*members)[i].isEnumMember();
printf(" member %s\n", em.toChars());
}
}
if (!isAnonymous())
{
ScopeDsymbol.addMember(sc, sds);
}
addEnumMembers(this, sc, sds);
}
override void setScope(Scope* sc)
{
if (semanticRun > PASS.init)
return;
ScopeDsymbol.setScope(sc);
}
override bool oneMember(Dsymbol* ps, Identifier ident)
{
if (isAnonymous())
return Dsymbol.oneMembers(members, ps, ident);
return Dsymbol.oneMember(ps, ident);
}
override Type getType()
{
return type;
}
override const(char)* kind() const
{
return "enum";
}
override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
{
//printf("%s.EnumDeclaration::search('%s')\n", toChars(), ident.toChars());
if (_scope)
{
// Try one last time to resolve this enum
dsymbolSemantic(this, _scope);
}
Dsymbol s = ScopeDsymbol.search(loc, ident, flags);
return s;
}
// is Dsymbol deprecated?
override bool isDeprecated() const
{
return isdeprecated;
}
override Visibility visible() pure nothrow @nogc @safe
{
return visibility;
}
/****************
* Determine if enum is a special one.
* Returns:
* `true` if special
*/
bool isSpecial() const nothrow @nogc
{
return isSpecialEnumIdent(ident) && memtype;
}
Expression getDefaultValue(const ref Loc loc)
{
Expression handleErrors(){
defaultval = ErrorExp.get();
return defaultval;
}
//printf("EnumDeclaration::getDefaultValue() %p %s\n", this, toChars());
if (defaultval)
return defaultval;
if (_scope)
dsymbolSemantic(this, _scope);
if (errors)
return handleErrors();
if (!members)
{
if (isSpecial())
{
/* Allow these special enums to not need a member list
*/
return defaultval = memtype.defaultInit(loc);
}
error(loc, "is opaque and has no default initializer");
return handleErrors();
}
foreach (const i; 0 .. members.dim)
{
EnumMember em = (*members)[i].isEnumMember();
if (em)
{
if (em.semanticRun < PASS.semanticdone)
{
error(loc, "forward reference of `%s.init`", toChars());
return handleErrors();
}
defaultval = em.value;
return defaultval;
}
}
return handleErrors();
}
Type getMemtype(const ref Loc loc)
{
if (_scope)
{
/* Enum is forward referenced. We don't need to resolve the whole thing,
* just the base type
*/
if (memtype)
{
Loc locx = loc.isValid() ? loc : this.loc;
memtype = memtype.typeSemantic(locx, _scope);
}
else
{
// Run semantic to get the type from a possible first member value
dsymbolSemantic(this, _scope);
}
}
if (!memtype)
{
if (!isAnonymous() && (members || semanticRun >= PASS.semanticdone))
memtype = Type.tint32;
else
{
Loc locx = loc.isValid() ? loc : this.loc;
error(locx, "is forward referenced looking for base type");
return Type.terror;
}
}
return memtype;
}
override inout(EnumDeclaration) isEnumDeclaration() inout
{
return this;
}
Symbol* sinit;
override void accept(Visitor v)
{
v.visit(this);
}
}
/***********************************************************
* AST node representing a member of an enum.
* https://dlang.org/spec/enum.html#EnumMember
* https://dlang.org/spec/enum.html#AnonymousEnumMember
*/
extern (C++) final class EnumMember : VarDeclaration
{
/* Can take the following forms:
* 1. id
* 2. id = value
* 3. type id = value
*/
@property ref value() { return (cast(ExpInitializer)_init).exp; }
// A cast() is injected to 'value' after dsymbolSemantic(),
// but 'origValue' will preserve the original value,
// or previous value + 1 if none was specified.
Expression origValue;
Type origType;
EnumDeclaration ed;
extern (D) this(const ref 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;
}
extern(D) this(Loc loc, Identifier id, Expression value, Type memtype,
StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd)
{
this(loc, id, value, memtype);
storage_class = stc;
userAttribDecl = uad;
depdecl = dd;
}
override EnumMember syntaxCopy(Dsymbol s)
{
assert(!s);
return new EnumMember(
loc, ident,
value ? value.syntaxCopy() : null,
origType ? origType.syntaxCopy() : null,
storage_class,
userAttribDecl ? userAttribDecl.syntaxCopy(s) : null,
depdecl ? depdecl.syntaxCopy(s) : null);
}
override const(char)* kind() const
{
return "enum member";
}
override inout(EnumMember) isEnumMember() inout
{
return this;
}
override void accept(Visitor v)
{
v.visit(this);
}
}
/******************************************
* Check for special enum names.
*
* Special enum names are used by the C++ name mangler to represent
* C++ types that are not basic D types.
* Params:
* ident = identifier to check for specialness
* Returns:
* `true` if it is special
*/
bool isSpecialEnumIdent(const Identifier ident) @nogc nothrow
{
return ident == Id.__c_long ||
ident == Id.__c_ulong ||
ident == Id.__c_longlong ||
ident == Id.__c_ulonglong ||
ident == Id.__c_long_double ||
ident == Id.__c_wchar_t ||
ident == Id.__c_complex_float ||
ident == Id.__c_complex_double ||
ident == Id.__c_complex_real;
}

View file

@ -1,320 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/import.c
*/
#include "root/dsystem.h"
#include "root/root.h"
#include "mars.h"
#include "dsymbol.h"
#include "import.h"
#include "identifier.h"
#include "module.h"
#include "scope.h"
#include "mtype.h"
#include "declaration.h"
#include "id.h"
#include "attrib.h"
#include "hdrgen.h"
/********************************* Import ****************************/
Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId,
int isstatic)
: Dsymbol(NULL)
{
assert(id);
this->loc = loc;
this->packages = packages;
this->id = id;
this->aliasId = aliasId;
this->isstatic = isstatic;
this->protection = Prot(Prot::private_); // default to private
this->pkg = NULL;
this->mod = NULL;
// Set symbol name (bracketed)
if (aliasId)
{
// import [cstdio] = std.stdio;
this->ident = aliasId;
}
else if (packages && packages->length)
{
// import [std].stdio;
this->ident = (*packages)[0];
}
else
{
// import [foo];
this->ident = id;
}
}
void Import::addAlias(Identifier *name, Identifier *alias)
{
if (isstatic)
error("cannot have an import bind list");
if (!aliasId)
this->ident = NULL; // make it an anonymous import
names.push(name);
aliases.push(alias);
}
const char *Import::kind() const
{
return isstatic ? "static import" : "import";
}
Prot Import::prot()
{
return protection;
}
Dsymbol *Import::syntaxCopy(Dsymbol *s)
{
assert(!s);
Import *si = new Import(loc, packages, id, aliasId, isstatic);
for (size_t i = 0; i < names.length; i++)
{
si->addAlias(names[i], aliases[i]);
}
return si;
}
void Import::load(Scope *sc)
{
//printf("Import::load('%s') %p\n", toPrettyChars(), this);
// See if existing module
DsymbolTable *dst = Package::resolve(packages, NULL, &pkg);
Dsymbol *s = dst->lookup(id);
if (s)
{
if (s->isModule())
mod = (Module *)s;
else
{
if (s->isAliasDeclaration())
{
::error(loc, "%s %s conflicts with %s", s->kind(), s->toPrettyChars(), id->toChars());
}
else if (Package *p = s->isPackage())
{
if (p->isPkgMod == PKGunknown)
{
mod = Module::load(loc, packages, id);
if (!mod)
p->isPkgMod = PKGpackage;
else
{
// mod is a package.d, or a normal module which conflicts with the package name.
assert(mod->isPackageFile == (p->isPkgMod == PKGmodule));
if (mod->isPackageFile)
mod->tag = p->tag; // reuse the same package tag
}
}
else
{
mod = p->isPackageMod();
}
if (!mod)
{
::error(loc, "can only import from a module, not from package %s.%s",
p->toPrettyChars(), id->toChars());
}
}
else if (pkg)
{
::error(loc, "can only import from a module, not from package %s.%s",
pkg->toPrettyChars(), id->toChars());
}
else
{
::error(loc, "can only import from a module, not from package %s",
id->toChars());
}
}
}
if (!mod)
{
// Load module
mod = Module::load(loc, packages, id);
if (mod)
{
dst->insert(id, mod); // id may be different from mod->ident,
// if so then insert alias
}
}
if (mod && !mod->importedFrom)
mod->importedFrom = sc ? sc->_module->importedFrom : Module::rootModule;
if (!pkg)
{
if (mod && mod->isPackageFile)
{
// one level depth package.d file (import pkg; ./pkg/package.d)
// it's necessary to use the wrapping Package already created
pkg = mod->pkg;
}
else
pkg = mod;
}
//printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg);
}
void Import::importAll(Scope *sc)
{
if (mod) return; // Already done
load(sc);
if (!mod) return; // Failed
if (sc->stc & STCstatic)
isstatic = true;
mod->importAll(NULL);
if (mod->md && mod->md->isdeprecated)
{
Expression *msg = mod->md->msg;
if (StringExp *se = msg ? msg->toStringExp() : NULL)
mod->deprecation(loc, "is deprecated - %s", se->string);
else
mod->deprecation(loc, "is deprecated");
}
if (sc->explicitProtection)
protection = sc->protection;
if (!isstatic && !aliasId && !names.length)
sc->scopesym->importScope(mod, protection);
// Enable access to pkgs/mod as soon as posible, because compiler
// can traverse them before the import gets semantic (Issue: 21501)
if (!aliasId && !names.length)
addPackageAccess(sc->scopesym);
}
/*******************************
* Mark the imported packages as accessible from the current
* scope. This access check is necessary when using FQN b/c
* we're using a single global package tree.
* https://issues.dlang.org/show_bug.cgi?id=313
*/
void Import::addPackageAccess(ScopeDsymbol *scopesym)
{
//printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this);
if (packages)
{
// import a.b.c.d;
Package *p = pkg; // a
scopesym->addAccessiblePackage(p, protection);
for (size_t i = 1; i < packages->length; i++) // [b, c]
{
Identifier *id = (*packages)[i];
p = (Package *) p->symtab->lookup(id);
// https://issues.dlang.org/show_bug.cgi?id=17991
// An import of truly empty file/package can happen
// https://issues.dlang.org/show_bug.cgi?id=20151
// Package in the path conflicts with a module name
if (p == NULL)
return;
scopesym->addAccessiblePackage(p, protection);
}
}
scopesym->addAccessiblePackage(mod, protection); // d
}
Dsymbol *Import::toAlias()
{
if (aliasId)
return mod;
return this;
}
/*****************************
* Add import to sd's symbol table.
*/
void Import::addMember(Scope *sc, ScopeDsymbol *sd)
{
//printf("Import::addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd->toChars(), sc);
if (names.length == 0)
return Dsymbol::addMember(sc, sd);
if (aliasId)
Dsymbol::addMember(sc, sd);
/* Instead of adding the import to sd's symbol table,
* add each of the alias=name pairs
*/
for (size_t i = 0; i < names.length; i++)
{
Identifier *name = names[i];
Identifier *alias = aliases[i];
if (!alias)
alias = name;
TypeIdentifier *tname = new TypeIdentifier(loc, name);
AliasDeclaration *ad = new AliasDeclaration(loc, alias, tname);
ad->_import = this;
ad->addMember(sc, sd);
aliasdecls.push(ad);
}
}
void Import::setScope(Scope *sc)
{
Dsymbol::setScope(sc);
if (aliasdecls.length)
{
if (!mod)
importAll(sc);
sc = sc->push(mod);
sc->protection = protection;
for (size_t i = 0; i < aliasdecls.length; i++)
{
AliasDeclaration *ad = aliasdecls[i];
ad->setScope(sc);
}
sc = sc->pop();
}
}
Dsymbol *Import::search(const Loc &loc, Identifier *ident, int flags)
{
//printf("%s.Import::search(ident = '%s', flags = x%x)\n", toChars(), ident->toChars(), flags);
if (!pkg)
{
load(NULL);
mod->importAll(NULL);
dsymbolSemantic(mod, NULL);
}
// Forward it to the package/module
return pkg->search(loc, ident, flags);
}
bool Import::overloadInsert(Dsymbol *s)
{
/* Allow multiple imports with the same package base, but disallow
* alias collisions (Bugzilla 5412).
*/
assert(ident && ident == s->ident);
Import *imp;
if (!aliasId && (imp = s->isImport()) != NULL && !imp->aliasId)
return true;
else
return false;
}

358
gcc/d/dmd/dimport.d Normal file
View file

@ -0,0 +1,358 @@
/**
* A `Dsymbol` representing a renamed import.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.dimport;
import dmd.arraytypes;
import dmd.astenums;
import dmd.declaration;
import dmd.dmodule;
import dmd.dscope;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.errors;
import dmd.expression;
import dmd.globals;
import dmd.identifier;
import dmd.mtype;
import dmd.visitor;
/***********************************************************
*/
extern (C++) final class Import : Dsymbol
{
/* static import aliasId = pkg1.pkg2.id : alias1 = name1, alias2 = name2;
*/
Identifier[] packages; // array of Identifier's representing packages
Identifier id; // module Identifier
Identifier aliasId;
int isstatic; // !=0 if static import
Visibility visibility;
// Pairs of alias=name to bind into current namespace
Identifiers names;
Identifiers aliases;
Module mod;
Package pkg; // leftmost package/module
// corresponding AliasDeclarations for alias=name pairs
AliasDeclarations aliasdecls;
extern (D) this(const ref Loc loc, Identifier[] packages, Identifier id, Identifier aliasId, int isstatic)
{
Identifier selectIdent()
{
// select Dsymbol identifier (bracketed)
if (aliasId)
{
// import [aliasId] = std.stdio;
return aliasId;
}
else if (packages.length > 0)
{
// import [std].stdio;
return packages[0];
}
else
{
// import [id];
return id;
}
}
super(loc, selectIdent());
assert(id);
version (none)
{
printf("Import::Import(");
foreach (id; packages)
{
printf("%s.", id.toChars());
}
printf("%s)\n", id.toChars());
}
this.packages = packages;
this.id = id;
this.aliasId = aliasId;
this.isstatic = isstatic;
this.visibility = Visibility.Kind.private_; // default to private
}
extern (D) void addAlias(Identifier name, Identifier _alias)
{
if (isstatic)
error("cannot have an import bind list");
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";
}
override Visibility visible() pure nothrow @nogc @safe
{
return visibility;
}
// copy only syntax trees
override Import syntaxCopy(Dsymbol s)
{
assert(!s);
auto si = new Import(loc, packages, id, aliasId, isstatic);
si.comment = comment;
for (size_t i = 0; i < names.dim; i++)
{
si.addAlias(names[i], aliases[i]);
}
return si;
}
/*******************************
* Load this module.
* Returns:
* true for errors, false for success
*/
bool load(Scope* sc)
{
//printf("Import::load('%s') %p\n", toPrettyChars(), this);
// See if existing module
const errors = global.errors;
DsymbolTable dst = Package.resolve(packages, null, &pkg);
version (none)
{
if (pkg && pkg.isModule())
{
.error(loc, "can only import from a module, not from a member of module `%s`. Did you mean `import %s : %s`?", pkg.toChars(), pkg.toPrettyChars(), id.toChars());
mod = pkg.isModule(); // Error recovery - treat as import of that module
return true;
}
}
Dsymbol s = dst.lookup(id);
if (s)
{
if (s.isModule())
mod = cast(Module)s;
else
{
if (s.isAliasDeclaration())
{
.error(loc, "%s `%s` conflicts with `%s`", s.kind(), s.toPrettyChars(), id.toChars());
}
else if (Package p = s.isPackage())
{
if (p.isPkgMod == PKG.unknown)
{
uint preverrors = global.errors;
mod = Module.load(loc, packages, id);
if (!mod)
p.isPkgMod = PKG.package_;
else
{
// mod is a package.d, or a normal module which conflicts with the package name.
if (mod.isPackageFile)
mod.tag = p.tag; // reuse the same package tag
else
{
// show error if Module.load does not
if (preverrors == global.errors)
.error(loc, "%s `%s` from file %s conflicts with %s `%s`", mod.kind(), mod.toPrettyChars(), mod.srcfile.toChars, p.kind(), p.toPrettyChars());
return true;
}
}
}
else
{
mod = p.isPackageMod();
}
if (!mod)
{
.error(loc, "can only import from a module, not from package `%s.%s`", p.toPrettyChars(), id.toChars());
}
}
else if (pkg)
{
.error(loc, "can only import from a module, not from package `%s.%s`", pkg.toPrettyChars(), id.toChars());
}
else
{
.error(loc, "can only import from a module, not from package `%s`", id.toChars());
}
}
}
if (!mod)
{
// Load module
mod = Module.load(loc, packages, id);
if (mod)
{
// id may be different from mod.ident, if so then insert alias
dst.insert(id, mod);
}
}
if (mod && !mod.importedFrom)
mod.importedFrom = sc ? sc._module.importedFrom : Module.rootModule;
if (!pkg)
{
if (mod && mod.isPackageFile)
{
// one level depth package.d file (import pkg; ./pkg/package.d)
// it's necessary to use the wrapping Package already created
pkg = mod.pkg;
}
else
pkg = mod;
}
//printf("-Import::load('%s'), pkg = %p\n", toChars(), pkg);
return global.errors != errors;
}
override void importAll(Scope* sc)
{
if (mod) return; // Already done
load(sc);
if (!mod) return; // Failed
if (sc.stc & STC.static_)
isstatic = true;
mod.importAll(null);
mod.checkImportDeprecation(loc, sc);
if (sc.explicitVisibility)
visibility = sc.visibility;
if (!isstatic && !aliasId && !names.dim)
sc.scopesym.importScope(mod, visibility);
// Enable access to pkgs/mod as soon as posible, because compiler
// can traverse them before the import gets semantic (Issue: 21501)
if (!aliasId && !names.dim)
addPackageAccess(sc.scopesym);
}
/*******************************
* Mark the imported packages as accessible from the current
* scope. This access check is necessary when using FQN b/c
* we're using a single global package tree.
* https://issues.dlang.org/show_bug.cgi?id=313
*/
extern (D) void addPackageAccess(ScopeDsymbol scopesym)
{
//printf("Import::addPackageAccess('%s') %p\n", toPrettyChars(), this);
if (packages.length > 0)
{
// import a.b.c.d;
auto p = pkg; // a
scopesym.addAccessiblePackage(p, visibility);
foreach (id; packages[1 .. $]) // [b, c]
{
p = cast(Package) p.symtab.lookup(id);
// https://issues.dlang.org/show_bug.cgi?id=17991
// An import of truly empty file/package can happen
// https://issues.dlang.org/show_bug.cgi?id=20151
// Package in the path conflicts with a module name
if (p is null)
break;
scopesym.addAccessiblePackage(p, visibility);
}
}
scopesym.addAccessiblePackage(mod, visibility); // d
}
override Dsymbol toAlias()
{
if (aliasId)
return mod;
return this;
}
/*****************************
* Add import to sd's symbol table.
*/
override void addMember(Scope* sc, ScopeDsymbol sd)
{
//printf("Import.addMember(this=%s, sd=%s, sc=%p)\n", toChars(), sd.toChars(), sc);
if (names.dim == 0)
return Dsymbol.addMember(sc, sd);
if (aliasId)
Dsymbol.addMember(sc, sd);
/* Instead of adding the import to sd's symbol table,
* add each of the alias=name pairs
*/
for (size_t i = 0; i < names.dim; i++)
{
Identifier name = names[i];
Identifier _alias = aliases[i];
if (!_alias)
_alias = name;
auto tname = new TypeIdentifier(loc, name);
auto ad = new AliasDeclaration(loc, _alias, tname);
ad._import = this;
ad.addMember(sc, sd);
aliasdecls.push(ad);
}
}
override void setScope(Scope* sc)
{
Dsymbol.setScope(sc);
if (aliasdecls.dim)
{
if (!mod)
importAll(sc);
sc = sc.push(mod);
sc.visibility = visibility;
foreach (ad; aliasdecls)
ad.setScope(sc);
sc = sc.pop();
}
}
override Dsymbol search(const ref Loc loc, Identifier ident, int flags = SearchLocalsOnly)
{
//printf("%s.Import.search(ident = '%s', flags = x%x)\n", toChars(), ident.toChars(), flags);
if (!pkg)
{
load(null);
mod.importAll(null);
mod.dsymbolSemantic(null);
}
// Forward it to the package/module
return pkg.search(loc, ident, flags);
}
override bool overloadInsert(Dsymbol s)
{
/* Allow multiple imports with the same package base, but disallow
* alias collisions
* 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
return false;
}
override inout(Import) isImport() inout
{
return this;
}
override void accept(Visitor v)
{
v.visit(this);
}
}

File diff suppressed because it is too large Load diff

7487
gcc/d/dmd/dinterpret.d Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,458 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/macro.c
*/
/* Simple macro text processor.
*/
#include "root/dsystem.h"
#include "mars.h"
#include "errors.h"
#include "root/rmem.h"
#include "root/root.h"
#include "macro.h"
bool isIdStart(const utf8_t *p);
bool isIdTail(const utf8_t *p);
int utfStride(const utf8_t *p);
utf8_t *memdup(const utf8_t *p, size_t len)
{
return (utf8_t *)memcpy(mem.xmalloc(len), p, len);
}
Macro::Macro(const utf8_t *name, size_t namelen, const utf8_t *text, size_t textlen)
{
next = NULL;
this->name = name;
this->namelen = namelen;
this->text = text;
this->textlen = textlen;
inuse = 0;
}
Macro *Macro::search(const utf8_t *name, size_t namelen)
{ Macro *table;
//printf("Macro::search(%.*s)\n", namelen, name);
for (table = this; table; table = table->next)
{
if (table->namelen == namelen &&
memcmp(table->name, name, namelen) == 0)
{
//printf("\tfound %d\n", table->textlen);
break;
}
}
return table;
}
Macro *Macro::define(Macro **ptable, const utf8_t *name, size_t namelen, const utf8_t *text, size_t textlen)
{
//printf("Macro::define('%.*s' = '%.*s')\n", namelen, name, textlen, text);
Macro *table;
//assert(ptable);
for (table = *ptable; table; table = table->next)
{
if (table->namelen == namelen &&
memcmp(table->name, name, namelen) == 0)
{
table->text = text;
table->textlen = textlen;
return table;
}
}
table = new Macro(name, namelen, text, textlen);
table->next = *ptable;
*ptable = table;
return table;
}
/**********************************************************
* Given buffer p[0..end], extract argument marg[0..marglen].
* Params:
* n 0: get entire argument
* 1..9: get nth argument
* -1: get 2nd through end
*/
size_t extractArgN(const utf8_t *p, size_t end, const utf8_t **pmarg, size_t *pmarglen, int n)
{
/* Scan forward for matching right parenthesis.
* Nest parentheses.
* Skip over "..." and '...' strings inside HTML tags.
* Skip over <!-- ... --> comments.
* Skip over previous macro insertions
* Set marglen.
*/
unsigned parens = 1;
unsigned char instring = 0;
unsigned incomment = 0;
unsigned intag = 0;
unsigned inexp = 0;
int argn = 0;
size_t v = 0;
Largstart:
// Skip first space, if any, to find the start of the macro argument
if (n != 1 && v < end && isspace(p[v]))
v++;
*pmarg = p + v;
for (; v < end; v++)
{ utf8_t c = p[v];
switch (c)
{
case ',':
if (!inexp && !instring && !incomment && parens == 1)
{
argn++;
if (argn == 1 && n == -1)
{ v++;
goto Largstart;
}
if (argn == n)
break;
if (argn + 1 == n)
{ v++;
goto Largstart;
}
}
continue;
case '(':
if (!inexp && !instring && !incomment)
parens++;
continue;
case ')':
if (!inexp && !instring && !incomment && --parens == 0)
{
break;
}
continue;
case '"':
case '\'':
if (!inexp && !incomment && intag)
{
if (c == instring)
instring = 0;
else if (!instring)
instring = c;
}
continue;
case '<':
if (!inexp && !instring && !incomment)
{
if (v + 6 < end &&
p[v + 1] == '!' &&
p[v + 2] == '-' &&
p[v + 3] == '-')
{
incomment = 1;
v += 3;
}
else if (v + 2 < end &&
isalpha(p[v + 1]))
intag = 1;
}
continue;
case '>':
if (!inexp)
intag = 0;
continue;
case '-':
if (!inexp &&
!instring &&
incomment &&
v + 2 < end &&
p[v + 1] == '-' &&
p[v + 2] == '>')
{
incomment = 0;
v += 2;
}
continue;
case 0xFF:
if (v + 1 < end)
{
if (p[v + 1] == '{')
inexp++;
else if (p[v + 1] == '}')
inexp--;
}
continue;
default:
continue;
}
break;
}
if (argn == 0 && n == -1)
*pmarg = p + v;
*pmarglen = p + v - *pmarg;
//printf("extractArg%d('%.*s') = '%.*s'\n", n, end, p, *pmarglen, *pmarg);
return v;
}
/*****************************************************
* Expand macro in place in buf.
* Only look at the text in buf from start to end.
*/
void Macro::expand(OutBuffer *buf, size_t start, size_t *pend,
const utf8_t *arg, size_t arglen)
{
// limit recursive expansion
static int nest;
if (nest > global.recursionLimit)
{
error(Loc(), "DDoc macro expansion limit exceeded; more than %d expansions.",
global.recursionLimit);
return;
}
nest++;
size_t end = *pend;
assert(start <= end);
assert(end <= buf->length());
/* First pass - replace $0
*/
arg = memdup(arg, arglen);
for (size_t u = start; u + 1 < end; )
{
utf8_t *p = (utf8_t *)buf->slice().ptr; // buf->slice().ptr is not loop invariant
/* Look for $0, but not $$0, and replace it with arg.
*/
if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+'))
{
if (u > start && p[u - 1] == '$')
{ // Don't expand $$0, but replace it with $0
buf->remove(u - 1, 1);
end--;
u += 1; // now u is one past the closing '1'
continue;
}
utf8_t c = p[u + 1];
int n = (c == '+') ? -1 : c - '0';
const utf8_t *marg;
size_t marglen;
if (n == 0)
{
marg = arg;
marglen = arglen;
}
else
extractArgN(arg, arglen, &marg, &marglen, n);
if (marglen == 0)
{ // Just remove macro invocation
//printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
buf->remove(u, 2);
end -= 2;
}
else if (c == '+')
{
// Replace '$+' with 'arg'
//printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg);
buf->remove(u, 2);
buf->insert(u, marg, marglen);
end += marglen - 2;
// Scan replaced text for further expansion
size_t mend = u + marglen;
expand(buf, u, &mend, NULL, 0);
end += mend - (u + marglen);
u = mend;
}
else
{
// Replace '$1' with '\xFF{arg\xFF}'
//printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], marglen, marg);
buf->slice().ptr[u] = 0xFF;
buf->slice().ptr[u + 1] = '{';
buf->insert(u + 2, marg, marglen);
buf->insert(u + 2 + marglen, (const char *)"\xFF}", 2);
end += -2 + 2 + marglen + 2;
// Scan replaced text for further expansion
size_t mend = u + 2 + marglen;
expand(buf, u + 2, &mend, NULL, 0);
end += mend - (u + 2 + marglen);
u = mend;
}
//printf("u = %d, end = %d\n", u, end);
//printf("#%.*s#\n", end, &buf->slice().ptr[0]);
continue;
}
u++;
}
/* Second pass - replace other macros
*/
for (size_t u = start; u + 4 < end; )
{
utf8_t *p = (utf8_t *)buf->slice().ptr; // buf->slice().ptr is not loop invariant
/* A valid start of macro expansion is $(c, where c is
* an id start character, and not $$(c.
*/
if (p[u] == '$' && p[u + 1] == '(' && isIdStart(p+u+2))
{
//printf("\tfound macro start '%c'\n", p[u + 2]);
utf8_t *name = p + u + 2;
size_t namelen = 0;
const utf8_t *marg;
size_t marglen;
size_t v;
/* Scan forward to find end of macro name and
* beginning of macro argument (marg).
*/
for (v = u + 2; v < end; v+=utfStride(p+v))
{
if (!isIdTail(p+v))
{ // We've gone past the end of the macro name.
namelen = v - (u + 2);
break;
}
}
v += extractArgN(p + v, end - v, &marg, &marglen, 0);
assert(v <= end);
if (v < end)
{ // v is on the closing ')'
if (u > start && p[u - 1] == '$')
{ // Don't expand $$(NAME), but replace it with $(NAME)
buf->remove(u - 1, 1);
end--;
u = v; // now u is one past the closing ')'
continue;
}
Macro *m = search(name, namelen);
if (!m)
{
static const char undef[] = "DDOC_UNDEFINED_MACRO";
m = search((const utf8_t *)undef, strlen(undef));
if (m)
{
// Macro was not defined, so this is an expansion of
// DDOC_UNDEFINED_MACRO. Prepend macro name to args.
// marg = name[ ] ~ "," ~ marg[ ];
if (marglen)
{
utf8_t *q = (utf8_t *)mem.xmalloc(namelen + 1 + marglen);
assert(q);
memcpy(q, name, namelen);
q[namelen] = ',';
memcpy(q + namelen + 1, marg, marglen);
marg = q;
marglen += namelen + 1;
}
else
{
marg = name;
marglen = namelen;
}
}
}
if (m)
{
if (m->inuse && marglen == 0)
{ // Remove macro invocation
buf->remove(u, v + 1 - u);
end -= v + 1 - u;
}
else if (m->inuse &&
((arglen == marglen && memcmp(arg, marg, arglen) == 0) ||
(arglen + 4 == marglen &&
marg[0] == 0xFF &&
marg[1] == '{' &&
memcmp(arg, marg + 2, arglen) == 0 &&
marg[marglen - 2] == 0xFF &&
marg[marglen - 1] == '}'
)
)
)
{
/* Recursive expansion:
* marg is same as arg (with blue paint added)
* Just leave in place.
*/
}
else
{
//printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", m->namelen, m->name, marglen, marg, m->textlen, m->text);
marg = memdup(marg, marglen);
// Insert replacement text
buf->spread(v + 1, 2 + m->textlen + 2);
buf->slice().ptr[v + 1] = 0xFF;
buf->slice().ptr[v + 2] = '{';
memcpy(buf->slice().ptr + v + 3, m->text, m->textlen);
buf->slice().ptr[v + 3 + m->textlen] = 0xFF;
buf->slice().ptr[v + 3 + m->textlen + 1] = '}';
end += 2 + m->textlen + 2;
// Scan replaced text for further expansion
m->inuse++;
size_t mend = v + 1 + 2+m->textlen+2;
expand(buf, v + 1, &mend, marg, marglen);
end += mend - (v + 1 + 2+m->textlen+2);
m->inuse--;
buf->remove(u, v + 1 - u);
end -= v + 1 - u;
u += mend - (v + 1);
mem.xfree(const_cast<utf8_t *>(marg));
//printf("u = %d, end = %d\n", u, end);
//printf("#%.*s#\n", end - u, &buf->slice().ptr[u]);
continue;
}
}
else
{
// Replace $(NAME) with nothing
buf->remove(u, v + 1 - u);
end -= (v + 1 - u);
continue;
}
}
}
u++;
}
mem.xfree(const_cast<utf8_t *>(arg));
*pend = end;
nest--;
}

435
gcc/d/dmd/dmacro.d Normal file
View file

@ -0,0 +1,435 @@
/**
* Text macro processor for Ddoc.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.dmacro;
import core.stdc.ctype;
import core.stdc.string;
import dmd.doc;
import dmd.errors;
import dmd.globals;
import dmd.root.outbuffer;
import dmd.root.rmem;
extern (C++) struct MacroTable
{
/**********************************
* Define name=text macro.
* If macro `name` already exists, replace the text for it.
* Params:
* name = name of macro
* text = text of macro
*/
extern (D) void define(const(char)[] name, const(char)[] text)
{
//printf("MacroTable::define('%.*s' = '%.*s')\n", cast(int)name.length, name.ptr, text.length, text.ptr);
Macro* table;
for (table = mactab; table; table = table.next)
{
if (table.name == name)
{
table.text = text;
return;
}
}
table = new Macro(name, text);
table.next = mactab;
mactab = table;
}
/*****************************************************
* Look for macros in buf and expand them in place.
* Only look at the text in buf from start to pend.
*/
extern (D) void expand(ref OutBuffer buf, size_t start, ref size_t pend, const(char)[] arg)
{
version (none)
{
printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, pend, cast(int)arg.length, arg.ptr);
printf("Buf is: '%.*s'\n", cast(int)(pend - start), buf.data + start);
}
// limit recursive expansion
__gshared int nest;
if (nest > global.recursionLimit)
{
error(Loc.initial, "DDoc macro expansion limit exceeded; more than %d expansions.",
global.recursionLimit);
return;
}
nest++;
size_t end = pend;
assert(start <= end);
assert(end <= buf.length);
/* First pass - replace $0
*/
arg = memdup(arg);
for (size_t u = start; u + 1 < end;)
{
char* p = cast(char*)buf[].ptr; // buf.data is not loop invariant
/* Look for $0, but not $$0, and replace it with arg.
*/
if (p[u] == '$' && (isdigit(p[u + 1]) || p[u + 1] == '+'))
{
if (u > start && p[u - 1] == '$')
{
// Don't expand $$0, but replace it with $0
buf.remove(u - 1, 1);
end--;
u += 1; // now u is one past the closing '1'
continue;
}
char c = p[u + 1];
int n = (c == '+') ? -1 : c - '0';
const(char)[] marg;
if (n == 0)
{
marg = arg;
}
else
extractArgN(arg, marg, n);
if (marg.length == 0)
{
// Just remove macro invocation
//printf("Replacing '$%c' with '%.*s'\n", p[u + 1], cast(int)marg.length, marg.ptr);
buf.remove(u, 2);
end -= 2;
}
else if (c == '+')
{
// Replace '$+' with 'arg'
//printf("Replacing '$%c' with '%.*s'\n", p[u + 1], cast(int)marg.length, marg.ptr);
buf.remove(u, 2);
buf.insert(u, marg);
end += marg.length - 2;
// Scan replaced text for further expansion
size_t mend = u + marg.length;
expand(buf, u, mend, null);
end += mend - (u + marg.length);
u = mend;
}
else
{
// Replace '$1' with '\xFF{arg\xFF}'
//printf("Replacing '$%c' with '\xFF{%.*s\xFF}'\n", p[u + 1], cast(int)marg.length, marg.ptr);
ubyte[] slice = cast(ubyte[])buf[];
slice[u] = 0xFF;
slice[u + 1] = '{';
buf.insert(u + 2, marg);
buf.insert(u + 2 + marg.length, "\xFF}");
end += -2 + 2 + marg.length + 2;
// Scan replaced text for further expansion
size_t mend = u + 2 + marg.length;
expand(buf, u + 2, mend, null);
end += mend - (u + 2 + marg.length);
u = mend;
}
//printf("u = %d, end = %d\n", u, end);
//printf("#%.*s#\n", cast(int)end, &buf.data[0]);
continue;
}
u++;
}
/* Second pass - replace other macros
*/
for (size_t u = start; u + 4 < end;)
{
char* p = cast(char*)buf[].ptr; // buf.data is not loop invariant
/* A valid start of macro expansion is $(c, where c is
* an id start character, and not $$(c.
*/
if (p[u] == '$' && p[u + 1] == '(' && isIdStart(p + u + 2))
{
//printf("\tfound macro start '%c'\n", p[u + 2]);
char* name = p + u + 2;
size_t namelen = 0;
const(char)[] marg;
size_t v;
/* Scan forward to find end of macro name and
* beginning of macro argument (marg).
*/
for (v = u + 2; v < end; v += utfStride(p + v))
{
if (!isIdTail(p + v))
{
// We've gone past the end of the macro name.
namelen = v - (u + 2);
break;
}
}
v += extractArgN(p[v .. end], marg, 0);
assert(v <= end);
if (v < end)
{
// v is on the closing ')'
if (u > start && p[u - 1] == '$')
{
// Don't expand $$(NAME), but replace it with $(NAME)
buf.remove(u - 1, 1);
end--;
u = v; // now u is one past the closing ')'
continue;
}
Macro* m = search(name[0 .. namelen]);
if (!m)
{
immutable undef = "DDOC_UNDEFINED_MACRO";
m = search(undef);
if (m)
{
// Macro was not defined, so this is an expansion of
// DDOC_UNDEFINED_MACRO. Prepend macro name to args.
// marg = name[ ] ~ "," ~ marg[ ];
if (marg.length)
{
char* q = cast(char*)mem.xmalloc(namelen + 1 + marg.length);
assert(q);
memcpy(q, name, namelen);
q[namelen] = ',';
memcpy(q + namelen + 1, marg.ptr, marg.length);
marg = q[0 .. marg.length + namelen + 1];
}
else
{
marg = name[0 .. namelen];
}
}
}
if (m)
{
if (m.inuse && marg.length == 0)
{
// Remove macro invocation
buf.remove(u, v + 1 - u);
end -= v + 1 - u;
}
else if (m.inuse && ((arg.length == marg.length && memcmp(arg.ptr, marg.ptr, arg.length) == 0) ||
(arg.length + 4 == marg.length && marg[0] == 0xFF && marg[1] == '{' && memcmp(arg.ptr, marg.ptr + 2, arg.length) == 0 && marg[marg.length - 2] == 0xFF && marg[marg.length - 1] == '}')))
{
/* Recursive expansion:
* marg is same as arg (with blue paint added)
* Just leave in place.
*/
}
else
{
//printf("\tmacro '%.*s'(%.*s) = '%.*s'\n", cast(int)m.namelen, m.name, cast(int)marg.length, marg.ptr, cast(int)m.textlen, m.text);
marg = memdup(marg);
// Insert replacement text
buf.spread(v + 1, 2 + m.text.length + 2);
ubyte[] slice = cast(ubyte[])buf[];
slice[v + 1] = 0xFF;
slice[v + 2] = '{';
slice[v + 3 .. v + 3 + m.text.length] = cast(ubyte[])m.text[];
slice[v + 3 + m.text.length] = 0xFF;
slice[v + 3 + m.text.length + 1] = '}';
end += 2 + m.text.length + 2;
// Scan replaced text for further expansion
m.inuse++;
size_t mend = v + 1 + 2 + m.text.length + 2;
expand(buf, v + 1, mend, marg);
end += mend - (v + 1 + 2 + m.text.length + 2);
m.inuse--;
buf.remove(u, v + 1 - u);
end -= v + 1 - u;
u += mend - (v + 1);
mem.xfree(cast(char*)marg.ptr);
//printf("u = %d, end = %d\n", u, end);
//printf("#%.*s#\n", cast(int)(end - u), &buf.data[u]);
continue;
}
}
else
{
// Replace $(NAME) with nothing
buf.remove(u, v + 1 - u);
end -= (v + 1 - u);
continue;
}
}
}
u++;
}
mem.xfree(cast(char*)arg);
pend = end;
nest--;
}
private:
extern (D) Macro* search(const(char)[] name)
{
Macro* table;
//printf("Macro::search(%.*s)\n", cast(int)name.length, name.ptr);
for (table = mactab; table; table = table.next)
{
if (table.name == name)
{
//printf("\tfound %d\n", table.textlen);
break;
}
}
return table;
}
Macro* mactab;
}
/* ************************************************************************ */
private:
struct Macro
{
Macro* next; // next in list
const(char)[] name; // macro name
const(char)[] text; // macro replacement text
int inuse; // macro is in use (don't expand)
this(const(char)[] name, const(char)[] text)
{
this.name = name;
this.text = text;
}
}
/************************
* Make mutable copy of slice p.
* Params:
* p = slice
* Returns:
* copy allocated with mem.xmalloc()
*/
char[] memdup(const(char)[] p)
{
size_t len = p.length;
return (cast(char*)memcpy(mem.xmalloc(len), p.ptr, len))[0 .. len];
}
/**********************************************************
* Given buffer buf[], extract argument marg[].
* Params:
* buf = source string
* marg = set to slice of buf[]
* n = 0: get entire argument
* 1..9: get nth argument
* -1: get 2nd through end
*/
size_t extractArgN(const(char)[] buf, out const(char)[] marg, int n)
{
/* Scan forward for matching right parenthesis.
* Nest parentheses.
* Skip over "..." and '...' strings inside HTML tags.
* Skip over <!-- ... --> comments.
* Skip over previous macro insertions
* Set marg.
*/
uint parens = 1;
ubyte instring = 0;
uint incomment = 0;
uint intag = 0;
uint inexp = 0;
uint argn = 0;
size_t v = 0;
const p = buf.ptr;
const end = buf.length;
Largstart:
// Skip first space, if any, to find the start of the macro argument
if (n != 1 && v < end && isspace(p[v]))
v++;
size_t vstart = v;
for (; v < end; v++)
{
char c = p[v];
switch (c)
{
case ',':
if (!inexp && !instring && !incomment && parens == 1)
{
argn++;
if (argn == 1 && n == -1)
{
v++;
goto Largstart;
}
if (argn == n)
break;
if (argn + 1 == n)
{
v++;
goto Largstart;
}
}
continue;
case '(':
if (!inexp && !instring && !incomment)
parens++;
continue;
case ')':
if (!inexp && !instring && !incomment && --parens == 0)
{
break;
}
continue;
case '"':
case '\'':
if (!inexp && !incomment && intag)
{
if (c == instring)
instring = 0;
else if (!instring)
instring = c;
}
continue;
case '<':
if (!inexp && !instring && !incomment)
{
if (v + 6 < end && p[v + 1] == '!' && p[v + 2] == '-' && p[v + 3] == '-')
{
incomment = 1;
v += 3;
}
else if (v + 2 < end && isalpha(p[v + 1]))
intag = 1;
}
continue;
case '>':
if (!inexp)
intag = 0;
continue;
case '-':
if (!inexp && !instring && incomment && v + 2 < end && p[v + 1] == '-' && p[v + 2] == '>')
{
incomment = 0;
v += 2;
}
continue;
case 0xFF:
if (v + 1 < end)
{
if (p[v + 1] == '{')
inexp++;
else if (p[v + 1] == '}')
inexp--;
}
continue;
default:
continue;
}
break;
}
if (argn == 0 && n == -1)
marg = p[v .. v];
else
marg = p[vstart .. v];
//printf("extractArg%d('%.*s') = '%.*s'\n", n, cast(int)end, p, cast(int)marg.length, marg.ptr);
return v;
}

File diff suppressed because it is too large Load diff

1297
gcc/d/dmd/dmangle.d Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1608
gcc/d/dmd/dmodule.d Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

5388
gcc/d/dmd/doc.d Normal file

File diff suppressed because it is too large Load diff

View file

@ -5,15 +5,11 @@
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/doc.h
* https://github.com/dlang/dmd/blob/master/src/dmd/doc.h
*/
#pragma once
#include "root/dsystem.h"
class Module;
struct OutBuffer;
void escapeDdocString(OutBuffer *buf, size_t start);
void gendocfile(Module *m);

View file

@ -1,646 +0,0 @@
/* Compiler implementation of the D programming language
* Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* written by Walter Bright
* http://www.digitalmars.com
* Distributed under the Boost Software License, Version 1.0.
* http://www.boost.org/LICENSE_1_0.txt
* https://github.com/D-Programming-Language/dmd/blob/master/src/scope.c
*/
#include "root/dsystem.h" // strlen()
#include "root/root.h"
#include "root/rmem.h"
#include "root/speller.h"
#include "mars.h"
#include "init.h"
#include "identifier.h"
#include "scope.h"
#include "attrib.h"
#include "dsymbol.h"
#include "declaration.h"
#include "statement.h"
#include "aggregate.h"
#include "module.h"
#include "id.h"
#include "target.h"
#include "template.h"
Scope *Scope::freelist = NULL;
void allocFieldinit(Scope *sc, size_t dim)
{
sc->fieldinit = (unsigned *)mem.xcalloc(sizeof(unsigned), dim);
sc->fieldinit_dim = dim;
}
void freeFieldinit(Scope *sc)
{
if (sc->fieldinit)
mem.xfree(sc->fieldinit);
sc->fieldinit = NULL;
sc->fieldinit_dim = 0;
}
Scope *Scope::alloc()
{
if (freelist)
{
Scope *s = freelist;
freelist = s->enclosing;
//printf("freelist %p\n", s);
assert(s->flags & SCOPEfree);
s->flags &= ~SCOPEfree;
return s;
}
return new Scope();
}
Scope::Scope()
{
// Create root scope
//printf("Scope::Scope() %p\n", this);
this->_module = NULL;
this->scopesym = NULL;
this->enclosing = NULL;
this->parent = NULL;
this->sw = NULL;
this->tf = NULL;
this->os = NULL;
this->tinst = NULL;
this->minst = NULL;
this->sbreak = NULL;
this->scontinue = NULL;
this->fes = NULL;
this->callsc = NULL;
this->aligndecl = NULL;
this->func = NULL;
this->slabel = NULL;
this->linkage = LINKd;
this->cppmangle = CPPMANGLEdefault;
this->inlining = PINLINEdefault;
this->protection = Prot(Prot::public_);
this->explicitProtection = 0;
this->stc = 0;
this->depdecl = NULL;
this->inunion = 0;
this->nofree = 0;
this->noctor = 0;
this->intypeof = 0;
this->lastVar = NULL;
this->callSuper = 0;
this->fieldinit = NULL;
this->fieldinit_dim = 0;
this->flags = 0;
this->lastdc = NULL;
this->anchorCounts = NULL;
this->prevAnchor = NULL;
this->userAttribDecl = NULL;
}
Scope *Scope::copy()
{
Scope *sc = Scope::alloc();
*sc = *this; // memcpy
/* Bugzilla 11777: The copied scope should not inherit fieldinit.
*/
sc->fieldinit = NULL;
return sc;
}
Scope *Scope::createGlobal(Module *_module)
{
Scope *sc = Scope::alloc();
*sc = Scope(); // memset
sc->aligndecl = NULL;
sc->linkage = LINKd;
sc->inlining = PINLINEdefault;
sc->protection = Prot(Prot::public_);
sc->_module = _module;
sc->tinst = NULL;
sc->minst = _module;
sc->scopesym = new ScopeDsymbol();
sc->scopesym->symtab = new DsymbolTable();
// Add top level package as member of this global scope
Dsymbol *m = _module;
while (m->parent)
m = m->parent;
m->addMember(NULL, sc->scopesym);
m->parent = NULL; // got changed by addMember()
// Create the module scope underneath the global scope
sc = sc->push(_module);
sc->parent = _module;
return sc;
}
Scope *Scope::push()
{
Scope *s = copy();
//printf("Scope::push(this = %p) new = %p\n", this, s);
assert(!(flags & SCOPEfree));
s->scopesym = NULL;
s->enclosing = this;
s->slabel = NULL;
s->nofree = 0;
s->fieldinit = saveFieldInit();
s->flags = (flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile | SCOPEconstraint |
SCOPEnoaccesscheck | SCOPEignoresymbolvisibility |
SCOPEprintf | SCOPEscanf));
s->lastdc = NULL;
assert(this != s);
return s;
}
Scope *Scope::push(ScopeDsymbol *ss)
{
//printf("Scope::push(%s)\n", ss->toChars());
Scope *s = push();
s->scopesym = ss;
return s;
}
Scope *Scope::pop()
{
//printf("Scope::pop() %p nofree = %d\n", this, nofree);
Scope *enc = enclosing;
if (enclosing)
{
enclosing->callSuper |= callSuper;
if (fieldinit)
{
if (enclosing->fieldinit)
{
assert(fieldinit != enclosing->fieldinit);
size_t dim = fieldinit_dim;
for (size_t i = 0; i < dim; i++)
enclosing->fieldinit[i] |= fieldinit[i];
}
freeFieldinit(this);
}
}
if (!nofree)
{
enclosing = freelist;
freelist = this;
flags |= SCOPEfree;
}
return enc;
}
Scope *Scope::startCTFE()
{
Scope *sc = this->push();
sc->flags = this->flags | SCOPEctfe;
return sc;
}
Scope *Scope::endCTFE()
{
assert(flags & SCOPEctfe);
return pop();
}
void Scope::mergeCallSuper(Loc loc, unsigned cs)
{
// This does a primitive flow analysis to support the restrictions
// regarding when and how constructors can appear.
// It merges the results of two paths.
// The two paths are callSuper and cs; the result is merged into callSuper.
if (cs != callSuper)
{
// Have ALL branches called a constructor?
int aAll = (cs & (CSXthis_ctor | CSXsuper_ctor)) != 0;
int bAll = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0;
// Have ANY branches called a constructor?
bool aAny = (cs & CSXany_ctor) != 0;
bool bAny = (callSuper & CSXany_ctor) != 0;
// Have any branches returned?
bool aRet = (cs & CSXreturn) != 0;
bool bRet = (callSuper & CSXreturn) != 0;
// Have any branches halted?
bool aHalt = (cs & CSXhalt) != 0;
bool bHalt = (callSuper & CSXhalt) != 0;
bool ok = true;
if (aHalt && bHalt)
{
callSuper = CSXhalt;
}
else if ((!aHalt && aRet && !aAny && bAny) ||
(!bHalt && bRet && !bAny && aAny))
{
// If one has returned without a constructor call, there must be never
// have been ctor calls in the other.
ok = false;
}
else if (aHalt || (aRet && aAll))
{
// If one branch has called a ctor and then exited, anything the
// other branch has done is OK (except returning without a
// ctor call, but we already checked that).
callSuper |= cs & (CSXany_ctor | CSXlabel);
}
else if (bHalt || (bRet && bAll))
{
callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel));
}
else
{
// Both branches must have called ctors, or both not.
ok = (aAll == bAll);
// If one returned without a ctor, we must remember that
// (Don't bother if we've already found an error)
if (ok && aRet && !aAny)
callSuper |= CSXreturn;
callSuper |= cs & (CSXany_ctor | CSXlabel);
}
if (!ok)
error(loc, "one path skips constructor");
}
}
unsigned *Scope::saveFieldInit()
{
unsigned *fi = NULL;
if (fieldinit) // copy
{
size_t dim = fieldinit_dim;
fi = (unsigned *)mem.xmalloc(sizeof(unsigned) * dim);
for (size_t i = 0; i < dim; i++)
fi[i] = fieldinit[i];
}
return fi;
}
/****************************************
* Merge `b` flow analysis results into `a`.
* Params:
* a = the path to merge fi into
* b = the other path
* Returns:
* false means either `a` or `b` skips initialization
*/
static bool mergeFieldInit(unsigned &a, const unsigned b)
{
if (b == a)
return true;
// Have any branches returned?
bool aRet = (a & CSXreturn) != 0;
bool bRet = (b & CSXreturn) != 0;
// Have any branches halted?
bool aHalt = (a & CSXhalt) != 0;
bool bHalt = (b & CSXhalt) != 0;
if (aHalt && bHalt)
{
a = CSXhalt;
return true;
}
// The logic here is to prefer the branch that neither halts nor returns.
bool ok;
if (!bHalt && bRet)
{
// Branch b returns, no merging required.
ok = (b & CSXthis_ctor);
}
else if (!aHalt && aRet)
{
// Branch a returns, but b doesn't, b takes precedence.
ok = (a & CSXthis_ctor);
a = b;
}
else if (bHalt)
{
// Branch b halts, no merging required.
ok = (a & CSXthis_ctor);
}
else if (aHalt)
{
// Branch a halts, but b doesn't, b takes precedence
ok = (b & CSXthis_ctor);
a = b;
}
else
{
// Neither branch returns nor halts, merge flags
ok = !((a ^ b) & CSXthis_ctor);
a |= b;
}
return ok;
}
void Scope::mergeFieldInit(Loc loc, unsigned *fies)
{
if (fieldinit && fies)
{
FuncDeclaration *f = func;
if (fes) f = fes->func;
AggregateDeclaration *ad = f->isMember2();
assert(ad);
for (size_t i = 0; i < ad->fields.length; i++)
{
VarDeclaration *v = ad->fields[i];
bool mustInit = (v->storage_class & STCnodefaultctor ||
v->type->needsNested());
if (!::mergeFieldInit(fieldinit[i], fies[i]) && mustInit)
{
::error(loc, "one path skips field %s", v->toChars());
}
}
}
}
Module *Scope::instantiatingModule()
{
// TODO: in speculative context, returning 'module' is correct?
return minst ? minst : _module;
}
static Dsymbol *searchScopes(Scope *scope, Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags)
{
for (Scope *sc = scope; sc; sc = sc->enclosing)
{
assert(sc != sc->enclosing);
if (!sc->scopesym)
continue;
//printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc->scopesym->toChars(), sc->scopesym->kind(), flags);
if (sc->scopesym->isModule())
flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
if (Dsymbol *s = sc->scopesym->search(loc, ident, flags))
{
if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
ident == Id::length && sc->scopesym->isArrayScopeSymbol() &&
sc->enclosing && sc->enclosing->search(loc, ident, NULL, flags))
{
warning(s->loc, "array `length` hides other `length` name in outer scope");
}
if (pscopesym)
*pscopesym = sc->scopesym;
return s;
}
// Stop when we hit a module, but keep going if that is not just under the global scope
if (sc->scopesym->isModule() && !(sc->enclosing && !sc->enclosing->enclosing))
break;
}
return NULL;
}
/************************************
* Perform unqualified name lookup by following the chain of scopes up
* until found.
*
* Params:
* loc = location to use for error messages
* ident = name to look up
* pscopesym = if supplied and name is found, set to scope that ident was found in
* flags = modify search based on flags
*
* Returns:
* symbol if found, null if not
*/
Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym, int flags)
{
// This function is called only for unqualified lookup
assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));
/* If ident is "start at module scope", only look at module scope
*/
if (ident == Id::empty)
{
// Look for module scope
for (Scope *sc = this; sc; sc = sc->enclosing)
{
assert(sc != sc->enclosing);
if (!sc->scopesym)
continue;
if (Dsymbol *s = sc->scopesym->isModule())
{
if (pscopesym)
*pscopesym = sc->scopesym;
return s;
}
}
return NULL;
}
if (this->flags & SCOPEignoresymbolvisibility)
flags |= IgnoreSymbolVisibility;
// First look in local scopes
Dsymbol *s = searchScopes(this, loc, ident, pscopesym, flags | SearchLocalsOnly);
if (!s)
{
// Second look in imported modules
s = searchScopes(this, loc, ident, pscopesym, flags | SearchImportsOnly);
}
return s;
}
Dsymbol *Scope::insert(Dsymbol *s)
{
if (VarDeclaration *vd = s->isVarDeclaration())
{
if (lastVar)
vd->lastVar = lastVar;
lastVar = vd;
}
else if (WithScopeSymbol *ss = s->isWithScopeSymbol())
{
if (VarDeclaration *wthis = ss->withstate->wthis)
{
if (lastVar)
wthis->lastVar = lastVar;
lastVar = wthis;
}
return NULL;
}
for (Scope *sc = this; sc; sc = sc->enclosing)
{
//printf("\tsc = %p\n", sc);
if (sc->scopesym)
{
//printf("\t\tsc->scopesym = %p\n", sc->scopesym);
if (!sc->scopesym->symtab)
sc->scopesym->symtab = new DsymbolTable();
return sc->scopesym->symtabInsert(s);
}
}
assert(0);
return NULL;
}
/********************************************
* Search enclosing scopes for ClassDeclaration.
*/
ClassDeclaration *Scope::getClassScope()
{
for (Scope *sc = this; sc; sc = sc->enclosing)
{
if (!sc->scopesym)
continue;
ClassDeclaration *cd = sc->scopesym->isClassDeclaration();
if (cd)
return cd;
}
return NULL;
}
/********************************************
* Search enclosing scopes for ClassDeclaration.
*/
AggregateDeclaration *Scope::getStructClassScope()
{
for (Scope *sc = this; sc; sc = sc->enclosing)
{
if (!sc->scopesym)
continue;
AggregateDeclaration *ad = sc->scopesym->isClassDeclaration();
if (ad)
return ad;
ad = sc->scopesym->isStructDeclaration();
if (ad)
return ad;
}
return NULL;
}
/*******************************************
* For TemplateDeclarations, we need to remember the Scope
* where it was declared. So mark the Scope as not
* to be free'd.
*/
void Scope::setNoFree()
{
//int i = 0;
//printf("Scope::setNoFree(this = %p)\n", this);
for (Scope *sc = this; sc; sc = sc->enclosing)
{
//printf("\tsc = %p\n", sc);
sc->nofree = 1;
assert(!(flags & SCOPEfree));
//assert(sc != sc->enclosing);
//assert(!sc->enclosing || sc != sc->enclosing->enclosing);
//if (++i == 10)
//assert(0);
}
}
structalign_t Scope::alignment()
{
if (aligndecl)
return aligndecl->getAlignment(this);
else
return STRUCTALIGN_DEFAULT;
}
/************************************************
* Given the failed search attempt, try to find
* one with a close spelling.
*/
static void *scope_search_fp(void *arg, const char *seed, int* cost)
{
//printf("scope_search_fp('%s')\n", seed);
/* If not in the lexer's string table, it certainly isn't in the symbol table.
* Doing this first is a lot faster.
*/
size_t len = strlen(seed);
if (!len)
return NULL;
Identifier *id = Identifier::lookup(seed, len);
if (!id)
return NULL;
Scope *sc = (Scope *)arg;
Module::clearCache();
Dsymbol *scopesym = NULL;
Dsymbol *s = sc->search(Loc(), id, &scopesym, IgnoreErrors);
if (s)
{
for (*cost = 0; sc; sc = sc->enclosing, (*cost)++)
if (sc->scopesym == scopesym)
break;
if (scopesym != s->parent)
{
(*cost)++; // got to the symbol through an import
if (s->prot().kind == Prot::private_)
return NULL;
}
}
return (void*)s;
}
Dsymbol *Scope::search_correct(Identifier *ident)
{
if (global.gag)
return NULL; // don't do it for speculative compiles; too time consuming
Dsymbol *scopesym = NULL;
// search for exact name first
if (Dsymbol *s = search(Loc(), ident, &scopesym, IgnoreErrors))
return s;
return (Dsymbol *)speller(ident->toChars(), &scope_search_fp, this, idchars);
}
/************************************
* Maybe `ident` was a C or C++ name. Check for that,
* and suggest the D equivalent.
* Params:
* ident = unknown identifier
* Returns:
* D identifier string if found, null if not
*/
const char *Scope::search_correct_C(Identifier *ident)
{
TOK tok;
if (ident == Id::C_NULL)
tok = TOKnull;
else if (ident == Id::C_TRUE)
tok = TOKtrue;
else if (ident == Id::C_FALSE)
tok = TOKfalse;
else if (ident == Id::C_unsigned)
tok = TOKuns32;
else if (ident == Id::C_wchar_t)
tok = target.c.twchar_t->ty == Twchar ? TOKwchar : TOKdchar;
else
return NULL;
return Token::toChars(tok);
}

768
gcc/d/dmd/dscope.d Normal file
View file

@ -0,0 +1,768 @@
/**
* A scope as defined by curly braces `{}`.
*
* Not to be confused with the `scope` storage class.
*
* Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
* Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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
*/
module dmd.dscope;
import core.stdc.stdio;
import core.stdc.string;
import dmd.aggregate;
import dmd.arraytypes;
import dmd.astenums;
import dmd.attrib;
import dmd.ctorflow;
import dmd.dclass;
import dmd.declaration;
import dmd.dmodule;
import dmd.doc;
import dmd.dsymbol;
import dmd.dsymbolsem;
import dmd.dtemplate;
import dmd.expression;
import dmd.errors;
import dmd.func;
import dmd.globals;
import dmd.id;
import dmd.identifier;
import dmd.root.outbuffer;
import dmd.root.rmem;
import dmd.root.speller;
import dmd.statement;
import dmd.target;
import dmd.tokens;
//version=LOGSEARCH;
// List of flags that can be applied to this `Scope`
enum SCOPE
{
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
onlysafeaccess = 0x0400, /// unsafe access is not allowed for @safe code
Cfile = 0x0800, /// C semantics apply
free = 0x8000, /// is on free list
fullinst = 0x10000, /// fully instantiate templates
alias_ = 0x20000, /// inside alias declaration.
// The following are mutually exclusive
printf = 0x4_0000, /// printf-style function
scanf = 0x8_0000, /// scanf-style function
}
/// 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.onlysafeaccess | SCOPE.ignoresymbolvisibility |
SCOPE.printf | SCOPE.scanf | SCOPE.Cfile;
struct Scope
{
Scope* enclosing; /// enclosing Scope
Module _module; /// Root module
ScopeDsymbol scopesym; /// current symbol
FuncDeclaration func; /// function we are in
Dsymbol parent; /// parent to use
LabelStatement slabel; /// enclosing labelled statement
SwitchStatement sw; /// enclosing switch statement
Statement tryBody; /// enclosing _body of TryCatchStatement or TryFinallyStatement
TryFinallyStatement tf; /// enclosing try finally statement
ScopeGuardStatement os; /// 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
Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
Dsymbol inunion; /// != null if processing members of a union
bool nofree; /// true if shouldn't free it
bool inLoop; /// true if inside a loop (where constructor calls aren't allowed)
int intypeof; /// in typeof(exp)
VarDeclaration lastVar; /// Previous symbol used to prevent goto-skips-init
/* If minst && !tinst, it's in definitely non-speculative scope (eg. module member scope).
* If !minst && !tinst, it's in definitely speculative scope (eg. template constraint).
* If minst && tinst, it's in instantiated code scope without speculation.
* If !minst && tinst, it's in instantiated code scope with speculation.
*/
Module minst; /// root module where the instantiated templates should belong to
TemplateInstance tinst; /// enclosing template instance
CtorFlow ctorflow; /// flow analysis for constructors
/// alignment for struct members
AlignDeclaration aligndecl;
/// C++ namespace this symbol is in
CPPNamespaceDeclaration namespace;
/// linkage for external functions
LINK linkage = LINK.d;
/// mangle type
CPPMANGLE cppmangle = CPPMANGLE.def;
/// inlining strategy for functions
PragmaDeclaration inlining;
/// visibility for class members
Visibility visibility = Visibility(Visibility.Kind.public_);
int explicitVisibility; /// set if in an explicit visibility attribute
StorageClass stc; /// storage class
DeprecatedDeclaration depdecl; /// customized deprecation message
uint flags;
// user defined attributes
UserAttributeDeclaration userAttribDecl;
DocComment* lastdc; /// documentation comment for last symbol at this scope
uint[void*] anchorCounts; /// lookup duplicate anchor name count
Identifier prevAnchor; /// qualified symbol name of last doc anchor
AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value,
/// do not set wasRead for it
extern (D) __gshared Scope* freelist;
extern (D) static Scope* alloc()
{
if (freelist)
{
Scope* s = freelist;
freelist = s.enclosing;
//printf("freelist %p\n", s);
assert(s.flags & SCOPE.free);
s.flags &= ~SCOPE.free;
return s;
}
return new Scope();
}
extern (D) static Scope* createGlobal(Module _module)
{
Scope* sc = Scope.alloc();
*sc = Scope.init;
sc._module = _module;
sc.minst = _module;
sc.scopesym = new ScopeDsymbol();
sc.scopesym.symtab = new DsymbolTable();
// Add top level package as member of this global scope
Dsymbol m = _module;
while (m.parent)
m = m.parent;
m.addMember(null, sc.scopesym);
m.parent = null; // got changed by addMember()
if (_module.isCFile)
sc.flags |= SCOPE.Cfile;
// Create the module scope underneath the global scope
sc = sc.push(_module);
sc.parent = _module;
return sc;
}
extern (C++) Scope* copy()
{
Scope* sc = Scope.alloc();
*sc = this;
/* https://issues.dlang.org/show_bug.cgi?id=11777
* The copied scope should not inherit fieldinit.
*/
sc.ctorflow.fieldinit = null;
return sc;
}
extern (C++) Scope* push()
{
Scope* s = copy();
//printf("Scope::push(this = %p) new = %p\n", this, s);
assert(!(flags & SCOPE.free));
s.scopesym = null;
s.enclosing = &this;
debug
{
if (enclosing)
assert(!(enclosing.flags & SCOPE.free));
if (s == enclosing)
{
printf("this = %p, enclosing = %p, enclosing.enclosing = %p\n", s, &this, enclosing);
}
assert(s != enclosing);
}
s.slabel = null;
s.nofree = false;
s.ctorflow.fieldinit = ctorflow.fieldinit.arraydup;
s.flags = (flags & PersistentFlags);
s.lastdc = null;
assert(&this != s);
return s;
}
extern (C++) Scope* push(ScopeDsymbol ss)
{
//printf("Scope::push(%s)\n", ss.toChars());
Scope* s = push();
s.scopesym = ss;
return s;
}
extern (C++) Scope* pop()
{
//printf("Scope::pop() %p nofree = %d\n", this, nofree);
if (enclosing)
enclosing.ctorflow.OR(ctorflow);
ctorflow.freeFieldinit();
Scope* enc = enclosing;
if (!nofree)
{
if (mem.isGCEnabled)
this = this.init;
enclosing = freelist;
freelist = &this;
flags |= SCOPE.free;
}
return enc;
}
/*************************
* Similar to pop(), but the results in `this` are not folded
* into `enclosing`.
*/
extern (D) void detach()
{
ctorflow.freeFieldinit();
enclosing = null;
pop();
}
extern (C++) Scope* startCTFE()
{
Scope* sc = this.push();
sc.flags = this.flags | SCOPE.ctfe;
version (none)
{
/* TODO: Currently this is not possible, because we need to
* unspeculative some types and symbols if they are necessary for the
* final executable. Consider:
*
* struct S(T) {
* string toString() const { return "instantiated"; }
* }
* enum x = S!int();
* void main() {
* // To call x.toString in runtime, compiler should unspeculative S!int.
* assert(x.toString() == "instantiated");
* }
*/
// If a template is instantiated from CT evaluated expression,
// compiler can elide its code generation.
sc.tinst = null;
sc.minst = null;
}
return sc;
}
extern (C++) Scope* endCTFE()
{
assert(flags & SCOPE.ctfe);
return pop();
}
/*******************************
* Merge results of `ctorflow` into `this`.
* Params:
* loc = for error messages
* ctorflow = flow results to merge in
*/
extern (D) void merge(const ref 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)
{
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)
{
error(loc, "one path skips field `%s`", v.toChars());
}
}
}
}
/************************************
* Perform unqualified name lookup by following the chain of scopes up
* until found.
*
* Params:
* loc = location to use for error messages
* ident = name to look up
* pscopesym = if supplied and name is found, set to scope that ident was found in
* flags = modify search based on flags
*
* Returns:
* symbol if found, null if not
*/
extern (C++) Dsymbol search(const ref Loc loc, Identifier ident, Dsymbol* pscopesym, int flags = IgnoreNone)
{
version (LOGSEARCH)
{
printf("Scope.search(%p, '%s' flags=x%x)\n", &this, ident.toChars(), flags);
// Print scope chain
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
if (!sc.scopesym)
continue;
printf("\tscope %s\n", sc.scopesym.toChars());
}
static void printMsg(string txt, Dsymbol s)
{
printf("%.*s %s.%s, kind = '%s'\n", cast(int)txt.length, txt.ptr,
s.parent ? s.parent.toChars() : "", s.toChars(), s.kind());
}
}
// This function is called only for unqualified lookup
assert(!(flags & (SearchLocalsOnly | SearchImportsOnly)));
/* If ident is "start at module scope", only look at module scope
*/
if (ident == Id.empty)
{
// Look for module scope
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
assert(sc != sc.enclosing);
if (!sc.scopesym)
continue;
if (Dsymbol s = sc.scopesym.isModule())
{
//printMsg("\tfound", s);
if (pscopesym)
*pscopesym = sc.scopesym;
return s;
}
}
return null;
}
Dsymbol checkAliasThis(AggregateDeclaration ad, Identifier ident, int flags, Expression* exp)
{
import dmd.mtype;
if (!ad || !ad.aliasthis)
return null;
Declaration decl = ad.aliasthis.sym.isDeclaration();
if (!decl)
return null;
Type t = decl.type;
ScopeDsymbol sds;
TypeClass tc;
TypeStruct ts;
switch(t.ty)
{
case Tstruct:
ts = cast(TypeStruct)t;
sds = ts.sym;
break;
case Tclass:
tc = cast(TypeClass)t;
sds = tc.sym;
break;
case Tinstance:
sds = (cast(TypeInstance)t).tempinst;
break;
case Tenum:
sds = (cast(TypeEnum)t).sym;
break;
default: break;
}
if (!sds)
return null;
Dsymbol ret = sds.search(loc, ident, flags);
if (ret)
{
*exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
*exp = new DotIdExp(loc, *exp, ident);
return ret;
}
if (!ts && !tc)
return null;
Dsymbol s;
*exp = new DotIdExp(loc, *exp, ad.aliasthis.ident);
if (ts && !(ts.att & AliasThisRec.tracing))
{
ts.att = cast(AliasThisRec)(ts.att | AliasThisRec.tracing);
s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
ts.att = cast(AliasThisRec)(ts.att & ~AliasThisRec.tracing);
}
else if(tc && !(tc.att & AliasThisRec.tracing))
{
tc.att = cast(AliasThisRec)(tc.att | AliasThisRec.tracing);
s = checkAliasThis(sds.isAggregateDeclaration(), ident, flags, exp);
tc.att = cast(AliasThisRec)(tc.att & ~AliasThisRec.tracing);
}
return s;
}
Dsymbol searchScopes(int flags)
{
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
assert(sc != sc.enclosing);
if (!sc.scopesym)
continue;
//printf("\tlooking in scopesym '%s', kind = '%s', flags = x%x\n", sc.scopesym.toChars(), sc.scopesym.kind(), flags);
if (sc.scopesym.isModule())
flags |= SearchUnqualifiedModule; // tell Module.search() that SearchLocalsOnly is to be obeyed
if (Dsymbol s = sc.scopesym.search(loc, ident, flags))
{
if (flags & TagNameSpace)
{
// ImportC: if symbol is not a tag, look for it in tag table
if (!s.isScopeDsymbol())
{
auto ps = cast(void*)s in sc._module.tagSymTab;
if (!ps)
goto NotFound;
s = *ps;
}
}
if (!(flags & (SearchImportsOnly | IgnoreErrors)) &&
ident == Id.length && sc.scopesym.isArrayScopeSymbol() &&
sc.enclosing && sc.enclosing.search(loc, ident, null, flags))
{
warning(s.loc, "array `length` hides other `length` name in outer scope");
}
//printMsg("\tfound local", s);
if (pscopesym)
*pscopesym = sc.scopesym;
return s;
}
NotFound:
if (global.params.fixAliasThis)
{
Expression exp = new ThisExp(loc);
Dsymbol aliasSym = checkAliasThis(sc.scopesym.isAggregateDeclaration(), ident, flags, &exp);
if (aliasSym)
{
//printf("found aliassym: %s\n", aliasSym.toChars());
if (pscopesym)
*pscopesym = new ExpressionDsymbol(exp);
return aliasSym;
}
}
// Stop when we hit a module, but keep going if that is not just under the global scope
if (sc.scopesym.isModule() && !(sc.enclosing && !sc.enclosing.enclosing))
break;
}
return null;
}
if (this.flags & SCOPE.ignoresymbolvisibility)
flags |= IgnoreSymbolVisibility;
// First look in local scopes
Dsymbol s = searchScopes(flags | SearchLocalsOnly);
version (LOGSEARCH) if (s) printMsg("-Scope.search() found local", s);
if (!s)
{
// Second look in imported modules
s = searchScopes(flags | SearchImportsOnly);
version (LOGSEARCH) if (s) printMsg("-Scope.search() found import", s);
}
return s;
}
extern (D) Dsymbol search_correct(Identifier ident)
{
if (global.gag)
return null; // don't do it for speculative compiles; too time consuming
/************************************************
* Given the failed search attempt, try to find
* one with a close spelling.
* Params:
* seed = identifier to search for
* cost = set to the cost, which rises with each outer scope
* Returns:
* Dsymbol if found, null if not
*/
extern (D) Dsymbol scope_search_fp(const(char)[] seed, out int cost)
{
//printf("scope_search_fp('%s')\n", seed);
/* If not in the lexer's string table, it certainly isn't in the symbol table.
* Doing this first is a lot faster.
*/
if (!seed.length)
return null;
Identifier id = Identifier.lookup(seed);
if (!id)
return null;
Scope* sc = &this;
Module.clearCache();
Dsymbol scopesym = null;
Dsymbol s = sc.search(Loc.initial, id, &scopesym, IgnoreErrors);
if (!s)
return null;
// Do not show `@disable`d declarations
if (auto decl = s.isDeclaration())
if (decl.storage_class & STC.disable)
return null;
// Or `deprecated` ones if we're not in a deprecated scope
if (s.isDeprecated() && !sc.isDeprecated())
return null;
for (cost = 0; sc; sc = sc.enclosing, ++cost)
if (sc.scopesym == scopesym)
break;
if (scopesym != s.parent)
{
++cost; // got to the symbol through an import
if (s.visible().kind == Visibility.Kind.private_)
return null;
}
return s;
}
Dsymbol scopesym = null;
// search for exact name first
if (auto s = search(Loc.initial, ident, &scopesym, IgnoreErrors))
return s;
return speller!scope_search_fp(ident.toString());
}
/************************************
* Maybe `ident` was a C or C++ name. Check for that,
* and suggest the D equivalent.
* Params:
* ident = unknown identifier
* Returns:
* D identifier string if found, null if not
*/
extern (D) static const(char)* search_correct_C(Identifier ident)
{
import dmd.astenums : Twchar;
TOK tok;
if (ident == Id.NULL)
tok = TOK.null_;
else if (ident == Id.TRUE)
tok = TOK.true_;
else if (ident == Id.FALSE)
tok = TOK.false_;
else if (ident == Id.unsigned)
tok = TOK.uns32;
else if (ident == Id.wchar_t)
tok = target.c.wchar_tsize == 2 ? TOK.wchar_ : TOK.dchar_;
else
return null;
return Token.toChars(tok);
}
/***************************
* Find the innermost scope with a symbol table.
* Returns:
* innermost scope, null if none
*/
extern (D) Scope* inner() return
{
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
if (sc.scopesym)
return sc;
}
return null;
}
/******************************
* Add symbol s to innermost symbol table.
* Params:
* s = symbol to insert
* Returns:
* null if already in table, `s` if not
*/
extern (D) Dsymbol insert(Dsymbol s)
{
//printf("insert() %s\n", s.toChars());
if (VarDeclaration vd = s.isVarDeclaration())
{
if (lastVar)
vd.lastVar = lastVar;
lastVar = vd;
}
else if (WithScopeSymbol ss = s.isWithScopeSymbol())
{
if (VarDeclaration vd = ss.withstate.wthis)
{
if (lastVar)
vd.lastVar = lastVar;
lastVar = vd;
}
return null;
}
auto scopesym = inner().scopesym;
//printf("\t\tscopesym = %p\n", scopesym);
if (!scopesym.symtab)
scopesym.symtab = new DsymbolTable();
if (!(flags & SCOPE.Cfile))
return scopesym.symtabInsert(s);
// ImportC insert
if (!scopesym.symtabInsert(s)) // if already in table
{
Dsymbol s2 = scopesym.symtabLookup(s, s.ident); // s2 is existing entry
return handleTagSymbols(this, s, s2, scopesym);
}
return s; // inserted
}
/********************************************
* Search enclosing scopes for ScopeDsymbol.
*/
ScopeDsymbol getScopesym()
{
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
if (sc.scopesym)
return sc.scopesym;
}
return null; // not found
}
/********************************************
* Search enclosing scopes for ClassDeclaration.
*/
extern (C++) ClassDeclaration getClassScope()
{
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
if (!sc.scopesym)
continue;
if (ClassDeclaration cd = sc.scopesym.isClassDeclaration())
return cd;
}
return null;
}
/********************************************
* Search enclosing scopes for ClassDeclaration.
*/
extern (C++) AggregateDeclaration getStructClassScope()
{
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
if (!sc.scopesym)
continue;
if (AggregateDeclaration ad = sc.scopesym.isClassDeclaration())
return ad;
if (AggregateDeclaration ad = sc.scopesym.isStructDeclaration())
return ad;
}
return null;
}
/*******************************************
* For TemplateDeclarations, we need to remember the Scope
* where it was declared. So mark the Scope as not
* to be free'd.
*/
extern (D) void setNoFree()
{
//int i = 0;
//printf("Scope::setNoFree(this = %p)\n", this);
for (Scope* sc = &this; sc; sc = sc.enclosing)
{
//printf("\tsc = %p\n", sc);
sc.nofree = true;
assert(!(flags & SCOPE.free));
//assert(sc != sc.enclosing);
//assert(!sc.enclosing || sc != sc.enclosing.enclosing);
//if (++i == 10)
// assert(0);
}
}
structalign_t alignment()
{
if (aligndecl)
return aligndecl.getAlignment(&this);
else
return STRUCTALIGN_DEFAULT;
}
/**********************************
* Checks whether the current scope (or any of its parents) is deprecated.
*
* Returns: `true` if this or any parent scope is deprecated, `false` otherwise`
*/
extern(C++) bool isDeprecated() @safe @nogc pure nothrow const
{
for (const(Dsymbol)* sp = &(this.parent); *sp; sp = &(sp.parent))
{
if (sp.isDeprecated())
return true;
}
for (const(Scope)* sc2 = &this; sc2; sc2 = sc2.enclosing)
{
if (sc2.scopesym && sc2.scopesym.isDeprecated())
return true;
// If inside a StorageClassDeclaration that is deprecated
if (sc2.stc & STC.deprecated_)
return true;
}
if (_module.md && _module.md.isdeprecated)
{
return true;
}
return false;
}
}

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more