diff --git a/AUTHORS.txt b/AUTHORS.txt index c07ef5b37a0b..33233231fec8 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -26,6 +26,7 @@ Jeffrey Yasskin Jesse Ruderman Josh Matthews Joshua Wise +Jyun-Yan You Kelly Wilson Lennart Kudling Lindsey Kuper diff --git a/configure b/configure index c634805759c8..927fc62c4af7 100755 --- a/configure +++ b/configure @@ -212,7 +212,7 @@ case $CFG_CPUTYPE in CFG_CPUTYPE=arm ;; - x86_64 | x86-64 | x64) + x86_64 | x86-64 | x64 | amd64) CFG_CPUTYPE=x86_64 ;; diff --git a/doc/tutorial/args.md b/doc/tutorial/args.md index 1797f5a23f59..d388a1411204 100644 --- a/doc/tutorial/args.md +++ b/doc/tutorial/args.md @@ -105,7 +105,7 @@ de-initialized on the caller side, and give ownership of it to the called function. This is written `-`. Finally, the default passing styles (by-value for non-structural -types, by-reference for structural ones) are written `+` for by-value +types, by-reference for structural ones) are written `++` for by-value and `&&` for by(-immutable)-reference. It is sometimes necessary to override the defaults. We'll talk more about this when discussing [generics][gens]. diff --git a/mk/libuv/x86_64/freebsd/Makefile b/mk/libuv/x86_64/freebsd/Makefile new file mode 100644 index 000000000000..98a175dd3f02 --- /dev/null +++ b/mk/libuv/x86_64/freebsd/Makefile @@ -0,0 +1,353 @@ +# We borrow heavily from the kernel build setup, though we are simpler since +# we don't have Kconfig tweaking settings on us. + +# The implicit make rules have it looking for RCS files, among other things. +# We instead explicitly write all the rules we care about. +# It's even quicker (saves ~200ms) to pass -r on the command line. +MAKEFLAGS=-r + +# The source directory tree. +srcdir := ../../../.. + +# The name of the builddir. +builddir_name ?= out + +# The V=1 flag on command line makes us verbosely print command lines. +ifdef V + quiet= +else + quiet=quiet_ +endif + +# Specify BUILDTYPE=Release on the command line for a release build. +BUILDTYPE ?= Default + +# Directory all our build output goes into. +# Note that this must be two directories beneath src/ for unit tests to pass, +# as they reach into the src/ directory for data with relative paths. +builddir ?= $(builddir_name)/$(BUILDTYPE) +abs_builddir := $(abspath $(builddir)) +depsdir := $(builddir)/.deps + +# Object output directory. +obj := $(builddir)/obj +abs_obj := $(abspath $(obj)) + +# We build up a list of every single one of the targets so we can slurp in the +# generated dependency rule Makefiles in one pass. +all_deps := + + + +# C++ apps need to be linked with g++. +# +# Note: flock is used to seralize linking. Linking is a memory-intensive +# process so running parallel links can often lead to thrashing. To disable +# the serialization, override LINK via an envrionment variable as follows: +# +# export LINK=g++ +# +# This will allow make to invoke N linker processes as specified in -jN. +LINK ?= lockf $(builddir)/linker.lock $(CXX) + +CC.target ?= $(CC) +CFLAGS.target ?= $(CFLAGS) +CXX.target ?= $(CXX) +CXXFLAGS.target ?= $(CXXFLAGS) +LINK.target ?= $(LINK) +LDFLAGS.target ?= $(LDFLAGS) +AR.target ?= $(AR) +ARFLAGS.target ?= crs + +# N.B.: the logic of which commands to run should match the computation done +# in gyp's make.py where ARFLAGS.host etc. is computed. +# TODO(evan): move all cross-compilation logic to gyp-time so we don't need +# to replicate this environment fallback in make as well. +CC.host ?= gcc +CFLAGS.host ?= +CXX.host ?= g++ +CXXFLAGS.host ?= +LINK.host ?= g++ +LDFLAGS.host ?= +AR.host ?= ar +ARFLAGS.host := crs + +# Define a dir function that can handle spaces. +# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions +# "leading spaces cannot appear in the text of the first argument as written. +# These characters can be put into the argument value by variable substitution." +empty := +space := $(empty) $(empty) + +# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces +replace_spaces = $(subst $(space),?,$1) +unreplace_spaces = $(subst ?,$(space),$1) +dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) + +# Flags to make gcc output dependency info. Note that you need to be +# careful here to use the flags that ccache and distcc can understand. +# We write to a dep file on the side first and then rename at the end +# so we can't end up with a broken dep file. +depfile = $(depsdir)/$(call replace_spaces,$@).d +DEPFLAGS = -MMD -MF $(depfile).raw + +# We have to fixup the deps output in a few ways. +# (1) the file output should mention the proper .o file. +# ccache or distcc lose the path to the target, so we convert a rule of +# the form: +# foobar.o: DEP1 DEP2 +# into +# path/to/foobar.o: DEP1 DEP2 +# (2) we want missing files not to cause us to fail to build. +# We want to rewrite +# foobar.o: DEP1 DEP2 \ +# DEP3 +# to +# DEP1: +# DEP2: +# DEP3: +# so if the files are missing, they're just considered phony rules. +# We have to do some pretty insane escaping to get those backslashes +# and dollar signs past make, the shell, and sed at the same time. +# Doesn't work with spaces, but that's fine: .d files have spaces in +# their names replaced with other characters. +define fixup_dep +# The depfile may not exist if the input file didn't have any #includes. +touch $(depfile).raw +# Fixup path as in (1). +sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) +# Add extra rules as in (2). +# We remove slashes and replace spaces with new lines; +# remove blank lines; +# delete the first line and append a colon to the remaining lines. +sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ + grep -v '^$$' |\ + sed -e 1d -e 's|$$|:|' \ + >> $(depfile) +rm $(depfile).raw +endef + +# Command definitions: +# - cmd_foo is the actual command to run; +# - quiet_cmd_foo is the brief-output summary of the command. + +quiet_cmd_cc = CC($(TOOLSET)) $@ +cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_cxx = CXX($(TOOLSET)) $@ +cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< + +quiet_cmd_touch = TOUCH $@ +cmd_touch = touch $@ + +quiet_cmd_copy = COPY $@ +# send stderr to /dev/null to ignore messages when linking directories. +cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@") + +quiet_cmd_alink = AR($(TOOLSET)) $@ +cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^) + +# Due to circular dependencies between libraries :(, we wrap the +# special "figure out circular dependencies" flags around the entire +# input list during linking. +quiet_cmd_link = LINK($(TOOLSET)) $@ +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) + +# We support two kinds of shared objects (.so): +# 1) shared_library, which is just bundling together many dependent libraries +# into a link line. +# 2) loadable_module, which is generating a module intended for dlopen(). +# +# They differ only slightly: +# In the former case, we want to package all dependent code into the .so. +# In the latter case, we want to package just the API exposed by the +# outermost module. +# This means shared_library uses --whole-archive, while loadable_module doesn't. +# (Note that --whole-archive is incompatible with the --start-group used in +# normal linking.) + +# Other shared-object link notes: +# - Set SONAME to the library filename so our binaries don't reference +# the local, absolute paths used on the link command-line. +quiet_cmd_solink = SOLINK($(TOOLSET)) $@ +cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) + +quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ +cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS) + + +# Define an escape_quotes function to escape single quotes. +# This allows us to handle quotes properly as long as we always use +# use single quotes and escape_quotes. +escape_quotes = $(subst ','\'',$(1)) +# This comment is here just to include a ' to unconfuse syntax highlighting. +# Define an escape_vars function to escape '$' variable syntax. +# This allows us to read/write command lines with shell variables (e.g. +# $LD_LIBRARY_PATH), without triggering make substitution. +escape_vars = $(subst $$,$$$$,$(1)) +# Helper that expands to a shell command to echo a string exactly as it is in +# make. This uses printf instead of echo because printf's behaviour with respect +# to escape sequences is more portable than echo's across different shells +# (e.g., dash, bash). +exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' + +# Helper to compare the command we're about to run against the command +# we logged the last time we ran the command. Produces an empty +# string (false) when the commands match. +# Tricky point: Make has no string-equality test function. +# The kernel uses the following, but it seems like it would have false +# positives, where one string reordered its arguments. +# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ +# $(filter-out $(cmd_$@), $(cmd_$(1)))) +# We instead substitute each for the empty string into the other, and +# say they're equal if both substitutions produce the empty string. +# .d files contain ? instead of spaces, take that into account. +command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ + $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) + +# Helper that is non-empty when a prerequisite changes. +# Normally make does this implicitly, but we force rules to always run +# so we can check their command lines. +# $? -- new prerequisites +# $| -- order-only dependencies +prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) + +# Helper that executes all postbuilds, and deletes the output file when done +# if any of the postbuilds failed. +define do_postbuilds + @E=0;\ + for p in $(POSTBUILDS); do\ + eval $$p;\ + F=$$?;\ + if [ $$F -ne 0 ]; then\ + E=$$F;\ + fi;\ + done;\ + if [ $$E -ne 0 ]; then\ + rm -rf "$@";\ + exit $$E;\ + fi +endef + +# do_cmd: run a command via the above cmd_foo names, if necessary. +# Should always run for a given target to handle command-line changes. +# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. +# Third argument, if non-zero, makes it do POSTBUILDS processing. +# Note: We intentionally do NOT call dirx for depfile, since it contains ? for +# spaces already and dirx strips the ? characters. +define do_cmd +$(if $(or $(command_changed),$(prereq_changed)), + @$(call exact_echo, $($(quiet)cmd_$(1))) + @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" + $(if $(findstring flock,$(word 1,$(cmd_$1))), + @$(cmd_$(1)) + @echo " $(quiet_cmd_$(1)): Finished", + @$(cmd_$(1)) + ) + @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) + @$(if $(2),$(fixup_dep)) + $(if $(and $(3), $(POSTBUILDS)), + $(call do_postbuilds) + ) +) +endef + +# Declare the "all" target first so it is the default, +# even though we don't have the deps yet. +.PHONY: all +all: + +# Use FORCE_DO_CMD to force a target to run. Should be coupled with +# do_cmd. +.PHONY: FORCE_DO_CMD +FORCE_DO_CMD: + +TOOLSET := target +# Suffix rules, putting all outputs into $(obj). +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD + @$(call do_cmd,cxx,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD + @$(call do_cmd,cc,1) +$(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD + @$(call do_cmd,cc,1) + + +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/libuv/run-benchmarks.target.mk)))),) + include src/libuv/run-benchmarks.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/libuv/run-tests.target.mk)))),) + include src/libuv/run-tests.target.mk +endif +ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ + $(findstring $(join ^,$(prefix)),\ + $(join ^,src/libuv/uv.target.mk)))),) + include src/libuv/uv.target.mk +endif + +quiet_cmd_regen_makefile = ACTION Regenerating $@ +cmd_regen_makefile = ./src/libuv/build/gyp/gyp -fmake --ignore-environment "--toplevel-dir=." "--depth=." "--generator-output=mk/libuv/x86_64/unix" "-Dlibrary=static_library" "-Dtarget_arch=x86_64" "-DOS=freebsd" src/libuv/uv.gyp +Makefile: $(srcdir)/src/libuv/uv.gyp +# $(call do_cmd,regen_makefile) + +# "all" is a concatenation of the "all" targets from all the included +# sub-makefiles. This is just here to clarify. +all: + +# Add in dependency-tracking rules. $(all_deps) is the list of every single +# target in our tree. Only consider the ones with .d (dependency) info: +d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) +ifneq ($(d_files),) + # Rather than include each individual .d file, concatenate them into a + # single file which make is able to load faster. We split this into + # commands that take 1000 files at a time to avoid overflowing the + # command line. + $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps) + + ifneq ($(word 1001,$(d_files)),) + $(error Found unprocessed dependency files (gyp didn't generate enough rules!)) + endif + + # make looks for ways to re-generate included makefiles, but in our case, we + # don't have a direct way. Explicitly telling make that it has nothing to do + # for them makes it go faster. + $(depsdir)/all.deps: ; + + include $(depsdir)/all.deps +endif diff --git a/mk/libuv/x86_64/freebsd/src/libuv/run-benchmarks.target.mk b/mk/libuv/x86_64/freebsd/src/libuv/run-benchmarks.target.mk new file mode 100644 index 000000000000..333d5e04a231 --- /dev/null +++ b/mk/libuv/x86_64/freebsd/src/libuv/run-benchmarks.target.mk @@ -0,0 +1,83 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-benchmarks +DEFS_Default := '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' + +# Flags passed to all source files. +CFLAGS_Default := -pthread + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/libuv/test/benchmark-ares.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-ping-pongs.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-pound.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-pump.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-sizes.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-spawn.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-tcp-write-batch.o \ + $(obj).target/$(TARGET)/src/libuv/test/benchmark-udp-packet-storm.o \ + $(obj).target/$(TARGET)/src/libuv/test/dns-server.o \ + $(obj).target/$(TARGET)/src/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/libuv/test/blackhole-server.o \ + $(obj).target/$(TARGET)/src/libuv/test/run-benchmarks.o \ + $(obj).target/$(TARGET)/src/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := + +$(builddir)/run-benchmarks: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-benchmarks: LIBS := $(LIBS) +$(builddir)/run-benchmarks: LD_INPUTS := $(OBJS) $(obj).target/src/libuv/libuv.a +$(builddir)/run-benchmarks: TOOLSET := $(TOOLSET) +$(builddir)/run-benchmarks: $(OBJS) $(obj).target/src/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-benchmarks +# Add target alias +.PHONY: run-benchmarks +run-benchmarks: $(builddir)/run-benchmarks + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-benchmarks + diff --git a/mk/libuv/x86_64/freebsd/src/libuv/run-tests.target.mk b/mk/libuv/x86_64/freebsd/src/libuv/run-tests.target.mk new file mode 100644 index 000000000000..7eb08d192d1b --- /dev/null +++ b/mk/libuv/x86_64/freebsd/src/libuv/run-tests.target.mk @@ -0,0 +1,117 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := run-tests +DEFS_Default := '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' + +# Flags passed to all source files. +CFLAGS_Default := -pthread + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/libuv/include + +OBJS := $(obj).target/$(TARGET)/src/libuv/test/blackhole-server.o \ + $(obj).target/$(TARGET)/src/libuv/test/echo-server.o \ + $(obj).target/$(TARGET)/src/libuv/test/run-tests.o \ + $(obj).target/$(TARGET)/src/libuv/test/runner.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-get-loadavg.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-async.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-callback-stack.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-connection-fail.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-delayed-accept.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-fail-always.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-fs.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-fs-event.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-get-currentexe.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-get-memory.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-getaddrinfo.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-gethostbyname.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-getsockname.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-hrtime.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-idle.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-ipc.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-loop-handles.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-multiple-listen.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-pass-always.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-ping-pong.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-pipe-bind-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-pipe-connect-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-ref.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-shutdown-eof.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-spawn.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-stdio-over-pipes.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-bind-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-bind6-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-close.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-flags.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-connect-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-connect6-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-write-error.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-write-to-half-open-connection.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tcp-writealot.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-threadpool.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-timer-again.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-timer.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-tty.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-udp-dgram-too-big.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-udp-ipv6.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-udp-send-and-recv.o \ + $(obj).target/$(TARGET)/src/libuv/test/test-udp-multicast-join.o \ + $(obj).target/$(TARGET)/src/libuv/test/runner-unix.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# Make sure our dependencies are built before any of us. +$(OBJS): | $(obj).target/src/libuv/libuv.a + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := + +$(builddir)/run-tests: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(builddir)/run-tests: LIBS := $(LIBS) +$(builddir)/run-tests: LD_INPUTS := $(OBJS) $(obj).target/src/libuv/libuv.a +$(builddir)/run-tests: TOOLSET := $(TOOLSET) +$(builddir)/run-tests: $(OBJS) $(obj).target/src/libuv/libuv.a FORCE_DO_CMD + $(call do_cmd,link) + +all_deps += $(builddir)/run-tests +# Add target alias +.PHONY: run-tests +run-tests: $(builddir)/run-tests + +# Add executable to "all" target. +.PHONY: all +all: $(builddir)/run-tests + diff --git a/mk/libuv/x86_64/freebsd/src/libuv/uv.Makefile b/mk/libuv/x86_64/freebsd/src/libuv/uv.Makefile new file mode 100644 index 000000000000..3842bae54b95 --- /dev/null +++ b/mk/libuv/x86_64/freebsd/src/libuv/uv.Makefile @@ -0,0 +1,6 @@ +# This file is generated by gyp; do not edit. + +export builddir_name ?= mk/libuv/x86_64/unix/./src/libuv/out +.PHONY: all +all: + $(MAKE) -C ../.. uv run-benchmarks run-tests diff --git a/mk/libuv/x86_64/freebsd/src/libuv/uv.target.mk b/mk/libuv/x86_64/freebsd/src/libuv/uv.target.mk new file mode 100644 index 000000000000..f1b02252a037 --- /dev/null +++ b/mk/libuv/x86_64/freebsd/src/libuv/uv.target.mk @@ -0,0 +1,138 @@ +# This file is generated by gyp; do not edit. + +TOOLSET := target +TARGET := uv +DEFS_Default := '-D_LARGEFILE_SOURCE' \ + '-D_FILE_OFFSET_BITS=64' \ + '-D_GNU_SOURCE' \ + '-DEIO_STACKSIZE=262144' \ + '-DHAVE_CONFIG_H' \ + '-DEV_CONFIG_H="config_freebsd.h"' \ + '-DEIO_CONFIG_H="config_freebsd.h"' + +# Flags passed to all source files. +CFLAGS_Default := -pthread \ + -g \ + --std=gnu89 \ + -pedantic \ + -Wall \ + -Wextra \ + -Wno-unused-parameter + +# Flags passed to only C files. +CFLAGS_C_Default := + +# Flags passed to only C++ files. +CFLAGS_CC_Default := + +INCS_Default := -I$(srcdir)/src/libuv/include \ + -I$(srcdir)/src/libuv/include/uv-private \ + -I$(srcdir)/src/libuv/src \ + -I$(srcdir)/src/libuv/src/unix/ev \ + -I$(srcdir)/src/libuv/src/ares/config_freebsd + +OBJS := $(obj).target/$(TARGET)/src/libuv/src/uv-common.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_cancel.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares__close_sockets.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_data.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_destroy.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_expand_name.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_expand_string.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_fds.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_free_hostent.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_free_string.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_gethostbyaddr.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_gethostbyname.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares__get_hostent.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_getnameinfo.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_getopt.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_getsock.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_init.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_library_init.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_llist.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_mkquery.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_nowarn.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_options.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_aaaa_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_a_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_mx_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_ns_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_ptr_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_srv_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_parse_txt_reply.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_process.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_query.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares__read_line.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_search.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_send.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_strcasecmp.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_strdup.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_strerror.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_timeout.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares__timeval.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_version.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/ares_writev.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/bitncmp.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/inet_net_pton.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/inet_ntop.o \ + $(obj).target/$(TARGET)/src/libuv/src/ares/windows_port.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/core.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/uv-eio.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/fs.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/udp.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/tcp.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/pipe.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/tty.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/stream.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/cares.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/dl.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/error.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/process.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/eio/eio.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/ev/ev.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/freebsd.o \ + $(obj).target/$(TARGET)/src/libuv/src/unix/kqueue.o + +# Add to the list of files we specially track dependencies for. +all_deps += $(OBJS) + +# CFLAGS et al overrides must be target-local. +# See "Target-specific Variable Values" in the GNU Make manual. +$(OBJS): TOOLSET := $(TOOLSET) +$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) +$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) + +# Suffix rules, putting all outputs into $(obj). + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# Try building from generated source, too. + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.c FORCE_DO_CMD + @$(call do_cmd,cc,1) + +# End of this set of suffix rules +### Rules for final target. +LDFLAGS_Default := + +LIBS := -lm + +$(obj).target/src/libuv/libuv.a: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) +$(obj).target/src/libuv/libuv.a: LIBS := $(LIBS) +$(obj).target/src/libuv/libuv.a: TOOLSET := $(TOOLSET) +$(obj).target/src/libuv/libuv.a: $(OBJS) FORCE_DO_CMD + $(call do_cmd,alink) + +all_deps += $(obj).target/src/libuv/libuv.a +# Add target alias +.PHONY: uv +uv: $(obj).target/src/libuv/libuv.a + +# Add target alias to "all" target. +.PHONY: all +all: uv + diff --git a/mk/platform.mk b/mk/platform.mk index da3727c67eb5..40a3dd8ebc6b 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -24,14 +24,15 @@ endif ifneq ($(findstring freebsd,$(CFG_OSTYPE)),) CFG_LIB_NAME=lib$(1).so CFG_LIB_GLOB=lib$(1)-*.so - CFG_GCCISH_CFLAGS += -fPIC -march=i686 -I/usr/local/include + CFG_GCCISH_CFLAGS += -fPIC -I/usr/local/include CFG_GCCISH_LINK_FLAGS += -shared -fPIC -lpthread -lrt - ifeq ($(CFG_CPUTYPE), x86_64) - CFG_GCCISH_CFLAGS_i386 += -m32 - CFG_GCCISH_LINK_FLAGS_i386 += -m32 - CFG_GCCISH_CFLAGS_x86_64 += -m32 - CFG_GCCISH_LINK_FLAGS_x86_64 += -m32 - endif + CFG_GCCISH_DEF_FLAG := -Wl,--export-dynamic,--dynamic-list= + CFG_GCCISH_PRE_LIB_FLAGS := -Wl,-whole-archive + CFG_GCCISH_POST_LIB_FLAGS := -Wl,-no-whole-archive + CFG_GCCISH_CFLAGS_i386 += -m32 + CFG_GCCISH_LINK_FLAGS_i386 += -m32 + CFG_GCCISH_CFLAGS_x86_64 += -m64 + CFG_GCCISH_LINK_FLAGS_x86_64 += -m64 CFG_UNIXY := 1 CFG_LDENV := LD_LIBRARY_PATH CFG_DEF_SUFFIX := .bsd.def @@ -246,4 +247,4 @@ define CFG_MAKE_ASSEMBLER endef $(foreach target,$(CFG_TARGET_TRIPLES),\ - $(eval $(call CFG_MAKE_ASSEMBLER,$(target)))) \ No newline at end of file + $(eval $(call CFG_MAKE_ASSEMBLER,$(target)))) diff --git a/mk/rt.mk b/mk/rt.mk index a9c35572dc35..2eae42087290 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -119,6 +119,9 @@ ifeq ($$(CFG_WINDOWSY), 1) else ifeq ($(CFG_OSTYPE), apple-darwin) LIBUV_OSTYPE_$(1) := mac LIBUV_LIB_$(1) := rt/$(1)/libuv/Default/libuv.a +else ifeq ($(CFG_OSTYPE), unknown-freebsd) + LIBUV_OSTYPE_$(1) := freebsd + LIBUV_LIB_$(1) := rt/$(1)/libuv/Default/obj.target/src/libuv/libuv.a else LIBUV_OSTYPE_$(1) := unix LIBUV_LIB_$(1) := rt/$(1)/libuv/Default/obj.target/src/libuv/libuv.a @@ -174,6 +177,12 @@ $$(LIBUV_LIB_$(1)): $$(wildcard \ # These could go in rt.mk or rustllvm.mk, they're needed for both. # This regexp has a single $, escaped twice +%.bsd.def: %.def.in $$(MKFILE_DEPS) + @$$(call E, def: $$@) + $$(Q)echo "{" > $$@ + $$(Q)sed 's/.$$$$/&;/' $$< >> $$@ + $$(Q)echo "};" >> $$@ + %.linux.def: %.def.in $$(MKFILE_DEPS) @$$(call E, def: $$@) $$(Q)echo "{" > $$@ diff --git a/src/comp/back/link.rs b/src/comp/back/link.rs index 98917342c2d6..8950618c2e6e 100644 --- a/src/comp/back/link.rs +++ b/src/comp/back/link.rs @@ -34,7 +34,7 @@ fn llvm_err(sess: session::session, msg: str) unsafe { let buf = llvm::LLVMRustGetLastError(); if buf == ptr::null() { sess.fatal(msg); - } else { sess.fatal(msg + ": " + str::str_from_cstr(buf)); } + } else { sess.fatal(msg + ": " + str::from_cstr(buf)); } } fn load_intrinsics_bc(sess: session::session) -> option::t { @@ -566,7 +566,8 @@ fn link_binary(sess: session::session, let rmlib = bind fn (config: @session::config, filename: str) -> str { if config.os == session::os_macos || - config.os == session::os_linux && + (config.os == session::os_linux || + config.os == session::os_freebsd) && str::find(filename, "lib") == 0 { ret str::slice(filename, 3u, str::byte_len(filename)); @@ -580,6 +581,7 @@ fn link_binary(sess: session::session, ret alt config.os { session::os_macos. { rmext(rmlib(filename)) } session::os_linux. { rmext(rmlib(filename)) } + session::os_freebsd. { rmext(rmlib(filename)) } _ { rmext(filename) } }; } @@ -657,6 +659,15 @@ fn link_binary(sess: session::session, gcc_args += ["-lrt", "-ldl"]; } + if sess.get_targ_cfg().os == session::os_freebsd { + gcc_args += ["-lrt", "-L/usr/local/lib", "-lexecinfo", + "-L/usr/local/lib/gcc46", + "-L/usr/local/lib/gcc44", "-lstdc++", + "-Wl,-z,origin", + "-Wl,-rpath,/usr/local/lib/gcc46", + "-Wl,-rpath,/usr/local/lib/gcc44"]; + } + // OS X 10.6 introduced 'compact unwind info', which is produced by the // linker from the dwarf unwind info. Unfortunately, it does not seem to // understand how to unwind our __morestack frame, so we have to turn it diff --git a/src/comp/back/rpath.rs b/src/comp/back/rpath.rs index d7eaa083bfbd..fccacdcba801 100644 --- a/src/comp/back/rpath.rs +++ b/src/comp/back/rpath.rs @@ -105,6 +105,7 @@ fn get_rpath_relative_to_output(os: session::os, // Mac doesn't appear to support $ORIGIN let prefix = alt os { session::os_linux. { "$ORIGIN" + fs::path_sep() } + session::os_freebsd. { "$ORIGIN" + fs::path_sep() } session::os_macos. { "@executable_path" + fs::path_sep() } }; @@ -191,6 +192,7 @@ fn minimize_rpaths(rpaths: [str]) -> [str] { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] #[cfg(test)] mod test { #[test] @@ -315,6 +317,14 @@ mod test { assert res == "$ORIGIN/../lib"; } + #[test] + #[cfg(target_os = "freebsd")] + fn test_rpath_relative() { + let res = get_rpath_relative_to_output(session::os_freebsd, + "/usr", "bin/rustc", "lib/libstd.so"); + assert res == "$ORIGIN/../lib"; + } + #[test] #[cfg(target_os = "macos")] fn test_rpath_relative() { diff --git a/src/comp/back/x86.rs b/src/comp/back/x86.rs index 1e5b61739d4d..6e668b4c967c 100644 --- a/src/comp/back/x86.rs +++ b/src/comp/back/x86.rs @@ -8,6 +8,7 @@ fn get_target_strs(target_os: session::os) -> target_strs::t { session::os_macos. { "__DATA,__note.rustc" } session::os_win32. { ".note.rustc" } session::os_linux. { ".note.rustc" } + session::os_freebsd. { ".note.rustc" } }, data_layout: alt target_os { @@ -24,12 +25,17 @@ fn get_target_strs(target_os: session::os) -> target_strs::t { session::os_linux. { "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32" } + + session::os_freebsd. { + "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32" + } }, target_triple: alt target_os { session::os_macos. { "i686-apple-darwin" } session::os_win32. { "i686-pc-mingw32" } session::os_linux. { "i686-unknown-linux-gnu" } + session::os_freebsd. { "i686-unknown-freebsd" } }, gcc_args: ["-m32"] diff --git a/src/comp/back/x86_64.rs b/src/comp/back/x86_64.rs index 98d33d0a8b73..b02910be489d 100644 --- a/src/comp/back/x86_64.rs +++ b/src/comp/back/x86_64.rs @@ -8,6 +8,7 @@ fn get_target_strs(target_os: session::os) -> target_strs::t { session::os_macos. { "__DATA,__note.rustc" } session::os_win32. { ".note.rustc" } session::os_linux. { ".note.rustc" } + session::os_freebsd. { ".note.rustc" } }, data_layout: alt target_os { @@ -29,12 +30,19 @@ fn get_target_strs(target_os: session::os) -> target_strs::t { "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ "s0:64:64-f80:128:128-n8:16:32:64-S128" } + + session::os_freebsd. { + "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + "s0:64:64-f80:128:128-n8:16:32:64-S128" + } }, target_triple: alt target_os { session::os_macos. { "x86_64-apple-darwin" } session::os_win32. { "x86_64-pc-mingw32" } session::os_linux. { "x86_64-unknown-linux-gnu" } + session::os_freebsd. { "x86_64-unknown-freebsd" } }, gcc_args: ["-m64"] diff --git a/src/comp/driver/driver.rs b/src/comp/driver/driver.rs index d67b7bd7cf30..b8a6ba287293 100644 --- a/src/comp/driver/driver.rs +++ b/src/comp/driver/driver.rs @@ -26,6 +26,7 @@ fn default_configuration(sess: session::session, argv0: str, input: str) -> session::os_win32. { "msvcrt.dll" } session::os_macos. { "libc.dylib" } session::os_linux. { "libc.so.6" } + session::os_freebsd. { "libc.so.7" } _ { "libc.so" } }; @@ -172,8 +173,9 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str, time(time_passes, "const checking", bind middle::check_const::check_crate(sess, crate)); let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars); - let method_map = time(time_passes, "typechecking", - bind typeck::check_crate(ty_cx, impl_map, crate)); + let (method_map, dict_map) = + time(time_passes, "typechecking", + bind typeck::check_crate(ty_cx, impl_map, crate)); time(time_passes, "block-use checking", bind middle::block_use::check_crate(ty_cx, crate)); time(time_passes, "function usage", @@ -201,7 +203,7 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str, bind trans::trans_crate(sess, crate, ty_cx, outputs.obj_filename, exp_map, ast_map, mut_map, copy_map, last_uses, - method_map)); + method_map, dict_map)); time(time_passes, "LLVM passes", bind link::write::run_passes(sess, llmod, outputs.obj_filename)); @@ -294,6 +296,8 @@ fn get_os(triple: str) -> session::os { session::os_macos } else if str::find(triple, "linux") >= 0 { session::os_linux + } else if str::find(triple, "freebsd") >= 0 { + session::os_freebsd } else { early_error("Unknown operating system!") }; } diff --git a/src/comp/driver/session.rs b/src/comp/driver/session.rs index 1bedbf87f7ea..55f25a14f1d3 100644 --- a/src/comp/driver/session.rs +++ b/src/comp/driver/session.rs @@ -9,7 +9,7 @@ import syntax::parse::parser::parse_sess; import util::filesearch; import back::target_strs; -tag os { os_win32; os_macos; os_linux; } +tag os { os_win32; os_macos; os_linux; os_freebsd; } tag arch { arch_x86; arch_x86_64; arch_arm; } diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs index 256170665153..927e98796571 100644 --- a/src/comp/lib/llvm.rs +++ b/src/comp/lib/llvm.rs @@ -993,7 +993,8 @@ fn type_to_str_inner(names: type_names, outer0: [TypeRef], ty: TypeRef) -> } 10 { let el_ty = llvm::LLVMGetElementType(ty); - ret "[" + type_to_str_inner(names, outer, el_ty) + "]"; + ret "[" + type_to_str_inner(names, outer, el_ty) + " x " + + uint::str(llvm::LLVMGetArrayLength(ty)) + "]"; } 11 { let i: uint = 0u; diff --git a/src/comp/metadata/creader.rs b/src/comp/metadata/creader.rs index 618de096773c..21ead604d31d 100644 --- a/src/comp/metadata/creader.rs +++ b/src/comp/metadata/creader.rs @@ -129,6 +129,7 @@ fn default_native_lib_naming(sess: session::session, static: bool) -> session::os_win32. { ret {prefix: "", suffix: ".dll"}; } session::os_macos. { ret {prefix: "lib", suffix: ".dylib"}; } session::os_linux. { ret {prefix: "lib", suffix: ".so"}; } + session::os_freebsd. { ret {prefix: "lib", suffix: ".so"}; } } } @@ -215,7 +216,7 @@ fn get_metadata_section(sess: session::session, let si = mk_section_iter(of.llof); while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { let name_buf = llvm::LLVMGetSectionName(si.llsi); - let name = unsafe { str::str_from_cstr(name_buf) }; + let name = unsafe { str::from_cstr(name_buf) }; if str::eq(name, sess.get_targ_cfg().target_strs.meta_sect_name) { let cbuf = llvm::LLVMGetSectionContents(si.llsi); let csz = llvm::LLVMGetSectionSize(si.llsi); diff --git a/src/comp/metadata/csearch.rs b/src/comp/metadata/csearch.rs index 2603903cf656..3da705f41efc 100644 --- a/src/comp/metadata/csearch.rs +++ b/src/comp/metadata/csearch.rs @@ -72,6 +72,7 @@ fn get_impls_for_mod(cstore: cstore::cstore, def: ast::def_id, let nm = decoder::lookup_item_name(cdata, did.node); if alt name { some(n) { n == nm } none. { true } } { result += [@{did: did, + iface_did: none::, // FIXME[impl] ident: nm, methods: decoder::lookup_impl_methods( cdata, did.node, did.crate)}]; diff --git a/src/comp/metadata/decoder.rs b/src/comp/metadata/decoder.rs index adb5e581ae57..49536b6e1c9e 100644 --- a/src/comp/metadata/decoder.rs +++ b/src/comp/metadata/decoder.rs @@ -117,23 +117,20 @@ fn item_type(item: ebml::doc, this_cnum: ast::crate_num, tcx: ty::ctxt, fn item_ty_param_bounds(item: ebml::doc, this_cnum: ast::crate_num, tcx: ty::ctxt, extres: external_resolver) - -> [@[ty::param_bound]] { + -> @[ty::param_bounds] { let bounds = []; let def_parser = bind parse_external_def_id(this_cnum, extres, _); ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p| bounds += [tydecode::parse_bounds_data(@ebml::doc_data(p), this_cnum, def_parser, tcx)]; } - bounds + @bounds } fn item_ty_param_count(item: ebml::doc) -> uint { let n = 0u; - ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p| - for byte in ebml::doc_data(p) { - if byte as char == '.' { n += 1u; } - } - } + ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds, + {|_p| n += 1u; }); n } @@ -212,8 +209,8 @@ fn get_type(data: @[u8], def: ast::def_id, tcx: ty::ctxt, let t = item_type(item, this_cnum, tcx, extres); let tp_bounds = if family_has_type_params(item_family(item)) { item_ty_param_bounds(item, this_cnum, tcx, extres) - } else { [] }; - ret @{bounds: tp_bounds, ty: t}; + } else { @[] }; + ret {bounds: tp_bounds, ty: t}; } fn get_type_param_count(data: @[u8], id: ast::node_id) -> uint { diff --git a/src/comp/metadata/encoder.rs b/src/comp/metadata/encoder.rs index 60994d3b17ce..4776b8af34d3 100644 --- a/src/comp/metadata/encoder.rs +++ b/src/comp/metadata/encoder.rs @@ -190,7 +190,7 @@ fn encode_type_param_bounds(ebml_w: ebml::writer, ecx: @encode_ctxt, abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)}; for param in params { ebml::start_tag(ebml_w, tag_items_data_item_ty_param_bounds); - let bs = ecx.ccx.tcx.ty_param_bounds.get(local_def(param.id)); + let bs = ecx.ccx.tcx.ty_param_bounds.get(param.id); tyencode::enc_bounds(io::new_writer(ebml_w.writer), ty_str_ctxt, bs); ebml::end_tag(ebml_w); } diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs index 4f4c7f41c5e1..1a083bf8fb63 100644 --- a/src/comp/metadata/tydecode.rs +++ b/src/comp/metadata/tydecode.rs @@ -202,9 +202,17 @@ fn parse_ty(st: @pstate, sd: str_def) -> ty::t { st.pos = st.pos + 1u; ret ty::mk_tag(st.tcx, def, params); } + 'x' { + assert (next(st) as char == '['); + let def = parse_def(st, sd); + let params: [ty::t] = []; + while peek(st) as char != ']' { params += [parse_ty(st, sd)]; } + st.pos = st.pos + 1u; + ret ty::mk_iface(st.tcx, def, params); + } 'p' { - let bounds = parse_bounds(st, sd); - ret ty::mk_param(st.tcx, parse_int(st) as uint, bounds); + let did = parse_def(st, sd); + ret ty::mk_param(st.tcx, parse_int(st) as uint, did); } '@' { ret ty::mk_box(st.tcx, parse_mt(st, sd)); } '~' { ret ty::mk_uniq(st.tcx, parse_mt(st, sd)); } @@ -259,7 +267,7 @@ fn parse_ty(st: @pstate, sd: str_def) -> ty::t { while peek(st) as char != '[' { name += str::unsafe_from_byte(next(st)); } - methods += [{ident: name, tps: [], + methods += [{ident: name, tps: @[], fty: {proto: proto with parse_ty_fn(st, sd)}}]; } st.pos += 1u; @@ -401,8 +409,7 @@ fn parse_bounds_data(data: @[u8], crate_num: int, sd: str_def, tcx: ty::ctxt) fn parse_bounds(st: @pstate, sd: str_def) -> @[ty::param_bound] { let bounds = []; - while peek(st) as char == '.' { - next(st); + while peek(st) != 0u8 { bounds += [alt next(st) as char { 'S' { ty::bound_send } 'C' { ty::bound_copy } diff --git a/src/comp/metadata/tyencode.rs b/src/comp/metadata/tyencode.rs index 88885153e77a..73d444727234 100644 --- a/src/comp/metadata/tyencode.rs +++ b/src/comp/metadata/tyencode.rs @@ -126,6 +126,13 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) { for t: ty::t in tys { enc_ty(w, cx, t); } w.write_char(']'); } + ty::ty_iface(def, tys) { + w.write_str("x["); + w.write_str(cx.ds(def)); + w.write_char('|'); + for t: ty::t in tys { enc_ty(w, cx, t); } + w.write_char(']'); + } ty::ty_tup(ts) { w.write_str("T["); for t in ts { enc_ty(w, cx, t); } @@ -176,9 +183,10 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) { w.write_str(cx.ds(def)); w.write_char('|'); } - ty::ty_param(id, bounds) { + ty::ty_param(id, did) { w.write_char('p'); - enc_bounds(w, cx, bounds); + w.write_str(cx.ds(did)); + w.write_char('|'); w.write_str(uint::str(id)); } ty::ty_type. { w.write_char('Y'); } @@ -265,7 +273,6 @@ fn enc_ty_constr(w: io::writer, cx: @ctxt, c: @ty::type_constr) { fn enc_bounds(w: io::writer, cx: @ctxt, bs: @[ty::param_bound]) { for bound in *bs { - w.write_char('.'); alt bound { ty::bound_send. { w.write_char('S'); } ty::bound_copy. { w.write_char('C'); } diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs index f71112002c47..00d5534bfecb 100644 --- a/src/comp/middle/kind.rs +++ b/src/comp/middle/kind.rs @@ -44,6 +44,7 @@ fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map, let visit = visit::mk_vt(@{ visit_expr: check_expr, visit_stmt: check_stmt, + visit_block: check_block, visit_fn: check_fn with *visit::default_visitor() }); @@ -117,12 +118,18 @@ fn check_fn_cap_clause(cx: ctx, } } -fn check_expr(e: @expr, cx: ctx, v: visit::vt) { +fn check_block(b: blk, cx: ctx, v: visit::vt) { + alt b.node.expr { + some(ex) { maybe_copy(cx, ex); } + _ {} + } + visit::visit_block(b, cx, v); +} +fn check_expr(e: @expr, cx: ctx, v: visit::vt) { alt e.node { expr_assign(_, ex) | expr_assign_op(_, _, ex) | - expr_block({node: {expr: some(ex), _}, _}) | - expr_unary(box(_), ex) | expr_unary(uniq(_), ex) { maybe_copy(cx, ex); } + expr_unary(box(_), ex) | expr_unary(uniq(_), ex) | expr_ret(some(ex)) { maybe_copy(cx, ex); } expr_copy(expr) { check_copy_ex(cx, expr, false); } // Vector add copies. @@ -163,14 +170,14 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt) { alt substs.substs { some(ts) { let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id)); - let kinds = vec::map(ty::lookup_item_type(cx.tcx, did).bounds, - {|bs| ty::param_bounds_to_kind(bs)}); + let bounds = ty::lookup_item_type(cx.tcx, did).bounds; let i = 0u; for ty in ts { let kind = ty::type_kind(cx.tcx, ty); - if !ty::kind_lteq(kinds[i], kind) { + let p_kind = ty::param_bounds_to_kind(bounds[i]); + if !ty::kind_lteq(p_kind, kind) { cx.tcx.sess.span_err(e.span, "instantiating a " + - kind_to_str(kinds[i]) + + kind_to_str(p_kind) + " type parameter with a " + kind_to_str(kind) + " type"); } diff --git a/src/comp/middle/last_use.rs b/src/comp/middle/last_use.rs index 113e9a9c2997..78301c812023 100644 --- a/src/comp/middle/last_use.rs +++ b/src/comp/middle/last_use.rs @@ -1,7 +1,7 @@ import syntax::{visit, ast_util}; import syntax::ast::*; import syntax::codemap::span; -import std::list::{list, nil, cons, tail}; +import std::list::{is_not_empty, list, nil, cons, tail}; import core::{vec, option}; import std::list; @@ -63,6 +63,13 @@ fn find_last_uses(c: @crate, def_map: resolve::def_map, ret mini_table; } +fn is_block(cx: ctx, id: node_id) -> bool { + alt ty::struct(cx.tcx, ty::node_id_to_monotype(cx.tcx, id)) { + ty::ty_fn({proto: proto_block., _}) { true } + _ { false } + } +} + fn visit_expr(ex: @expr, cx: ctx, v: visit::vt) { alt ex.node { expr_ret(oexpr) { @@ -135,9 +142,8 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt) { let arg_ts = ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)); for arg in args { alt arg.node { - //NDM--register captured as uses - expr_fn(_, _, _, captured) { fns += [arg]; } - expr_fn_block(_, _) { fns += [arg]; } + expr_fn(proto_block., _, _, _) { fns += [arg]; } + expr_fn_block(_, _) when is_block(cx, arg.id) { fns += [arg]; } _ { alt arg_ts[i].mode { by_mut_ref. { clear_if_path(cx, arg, v, false); } @@ -163,6 +169,15 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, visit::visit_fn(fk, decl, body, sp, id, cx, v); }); } else { + alt cx.tcx.freevars.find(id) { + some(vars) { + for v in *vars { + clear_in_current(cx, ast_util::def_id_of_def(v.def).node, + false); + } + } + _ {} + } let old = nil; cx.blocks <-> old; visit::visit_fn(fk, decl, body, sp, id, cx, v); @@ -177,7 +192,9 @@ fn visit_block(tp: block_type, cx: ctx, visit: block()) { visit(); local.second = true; visit(); - cx.blocks = tail(cx.blocks); + let cx_blocks = cx.blocks; + check is_not_empty(cx_blocks); + cx.blocks = tail(cx_blocks); cx.current = join_branches(local.exits); } diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index ca9887534cb7..29d29a32fba3 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -37,7 +37,7 @@ tag scope { scope_loop(@ast::local); // there's only 1 decl per loop. scope_block(ast::blk, @mutable uint, @mutable uint); scope_arm(ast::arm); - scope_self(ast::node_id); + scope_method(ast::node_id, [ast::ty_param]); } type scopes = list; @@ -142,6 +142,7 @@ type env = mutable data: [ast::node_id]}, mutable reported: [{ident: str, sc: scope}], mutable ignored_imports: [node_id], + mutable current_tp: option::t, sess: session}; @@ -168,6 +169,7 @@ fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) -> used_imports: {mutable track: false, mutable data: []}, mutable reported: [], mutable ignored_imports: [], + mutable current_tp: none, sess: sess}; map_crate(e, crate); resolve_imports(*e); @@ -266,6 +268,7 @@ fn map_crate(e: @env, c: @ast::crate) { let imp = follow_import(*e, sc, *path, vi.span); if option::is_some(imp) { let glob = {def: option::get(imp), item: vi}; + check list::is_not_empty(sc); alt list::head(sc) { scope_item(i) { e.mod_map.get(i.id).glob_imports += [glob]; @@ -335,6 +338,7 @@ fn resolve_names(e: @env, c: @ast::crate) { visit_pat: bind walk_pat(e, _, _, _), visit_expr: bind walk_expr(e, _, _, _), visit_ty: bind walk_ty(e, _, _, _), + visit_ty_params: bind walk_tps(e, _, _, _), visit_constr: bind walk_constr(e, _, _, _, _, _), visit_fn: bind visit_fn_with_scope(e, _, _, _, _, _, _, _) with *visit::default_visitor()}; @@ -368,6 +372,20 @@ fn resolve_names(e: @env, c: @ast::crate) { _ { } } } + fn walk_tps(e: @env, tps: [ast::ty_param], sc: scopes, v: vt) { + let outer_current_tp = e.current_tp, current = 0u; + for tp in tps { + e.current_tp = some(current); + for bound in *tp.bounds { + alt bound { + bound_iface(t) { v.visit_ty(t, sc, v); } + _ {} + } + } + current += 1u; + } + e.current_tp = outer_current_tp; + } fn walk_constr(e: @env, p: @ast::path, sp: span, id: node_id, sc: scopes, _v: vt) { maybe_insert(e, id, lookup_path_strict(*e, sc, sp, p.node, ns_value)); @@ -403,9 +421,17 @@ fn visit_item_with_scope(i: @ast::item, sc: scopes, v: vt) { alt ifce { some(ty) { v.visit_ty(ty, sc, v); } _ {} } v.visit_ty(sty, sc, v); for m in methods { - v.visit_fn(visit::fk_method(m.ident, tps + m.tps), - m.decl, m.body, m.span, - m.id, sc, v); + let msc = cons(scope_method(i.id, tps + m.tps), @sc); + v.visit_fn(visit::fk_method(m.ident, []), + m.decl, m.body, m.span, m.id, msc, v); + } + } + ast::item_iface(tps, methods) { + visit::visit_ty_params(tps, sc, v); + for m in methods { + let msc = cons(scope_method(i.id, tps + m.tps), @sc); + for a in m.decl.inputs { v.visit_ty(a.ty, msc, v); } + v.visit_ty(m.decl.output, msc, v); } } _ { visit::visit_item(i, sc, v); } @@ -436,8 +462,8 @@ fn visit_fn_with_scope(e: @env, fk: visit::fn_kind, decl: ast::fn_decl, // for f's constrs in the table. for c: @ast::constr in decl.constraints { resolve_constr(e, c, sc, v); } let scope = alt fk { - visit::fk_item_fn(_, tps) | visit::fk_method(_, tps) | - visit::fk_res(_, tps) { + visit::fk_item_fn(_, tps) | visit::fk_res(_, tps) | + visit::fk_method(_, tps) { scope_bare_fn(decl, id, tps) } visit::fk_anon(_) | visit::fk_fn_block. { @@ -461,6 +487,7 @@ fn visit_block_with_scope(b: ast::blk, sc: scopes, v: vt) { } fn visit_decl_with_scope(d: @decl, sc: scopes, v: vt) { + check list::is_not_empty(sc); let loc_pos = alt list::head(sc) { scope_block(_, _, pos) { pos } _ { @mutable 0u } @@ -489,7 +516,7 @@ fn visit_expr_with_scope(x: @ast::expr, sc: scopes, v: vt) { v.visit_block(blk, new_sc, v); } ast::expr_anon_obj(_) { - visit::visit_expr(x, cons(scope_self(x.id), @sc), v); + visit::visit_expr(x, cons(scope_method(x.id, []), @sc), v); } _ { visit::visit_expr(x, sc, v); } } @@ -796,17 +823,14 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) scope_item(it) { alt it.node { ast::item_obj(ob, ty_params, _) { - ret lookup_in_obj(name, ob, ty_params, ns, it.id); + ret lookup_in_obj(e, name, ob, ty_params, ns, it.id); } - ast::item_impl(ty_params, _, _, _) { - if (name == "self" && ns == ns_value) { - ret some(ast::def_self(local_def(it.id))); - } - if ns == ns_type { ret lookup_in_ty_params(name, ty_params); } + ast::item_impl(tps, _, _, _) { + if ns == ns_type { ret lookup_in_ty_params(e, name, tps); } } ast::item_iface(tps, _) | ast::item_tag(_, tps) | ast::item_ty(_, tps) { - if ns == ns_type { ret lookup_in_ty_params(name, tps); } + if ns == ns_type { ret lookup_in_ty_params(e, name, tps); } } ast::item_mod(_) { ret lookup_in_local_mod(e, it.id, sp, name, ns, inside); @@ -817,21 +841,23 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) _ { } } } - scope_self(id) { + scope_method(id, tps) { if (name == "self" && ns == ns_value) { ret some(ast::def_self(local_def(id))); + } else if ns == ns_type { + ret lookup_in_ty_params(e, name, tps); } } scope_native_item(it) { alt it.node { ast::native_item_fn(decl, ty_params) { - ret lookup_in_fn(name, decl, ty_params, ns); + ret lookup_in_fn(e, name, decl, ty_params, ns); } } } scope_bare_fn(decl, _, ty_params) | scope_fn_expr(decl, _, ty_params) { - ret lookup_in_fn(name, decl, ty_params, ns); + ret lookup_in_fn(e, name, decl, ty_params, ns); } scope_loop(local) { if ns == ns_value { @@ -906,13 +932,13 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) e.sess.bug("reached unreachable code in lookup_in_scope"); // sigh } -fn lookup_in_ty_params(name: ident, ty_params: [ast::ty_param]) -> - option::t { +fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param]) + -> option::t { let n = 0u; for tp: ast::ty_param in ty_params { - if str::eq(tp.ident, name) { - ret some(ast::def_ty_param(local_def(tp.id), n)); - } + if str::eq(tp.ident, name) && alt e.current_tp { + some(cur) { n < cur } none. { true } + } { ret some(ast::def_ty_param(local_def(tp.id), n)); } n += 1u; } ret none::; @@ -927,7 +953,8 @@ fn lookup_in_pat(name: ident, pat: @ast::pat) -> option::t { ret found; } -fn lookup_in_fn(name: ident, decl: ast::fn_decl, ty_params: [ast::ty_param], +fn lookup_in_fn(e: env, name: ident, decl: ast::fn_decl, + ty_params: [ast::ty_param], ns: namespace) -> option::t { alt ns { ns_value. { @@ -938,12 +965,13 @@ fn lookup_in_fn(name: ident, decl: ast::fn_decl, ty_params: [ast::ty_param], } ret none::; } - ns_type. { ret lookup_in_ty_params(name, ty_params); } + ns_type. { ret lookup_in_ty_params(e, name, ty_params); } _ { ret none::; } } } -fn lookup_in_obj(name: ident, ob: ast::_obj, ty_params: [ast::ty_param], +fn lookup_in_obj(e: env, name: ident, ob: ast::_obj, + ty_params: [ast::ty_param], ns: namespace, id: node_id) -> option::t { alt ns { ns_value. { @@ -955,7 +983,7 @@ fn lookup_in_obj(name: ident, ob: ast::_obj, ty_params: [ast::ty_param], } ret none::; } - ns_type. { ret lookup_in_ty_params(name, ty_params); } + ns_type. { ret lookup_in_ty_params(e, name, ty_params); } _ { ret none::; } } } @@ -1701,7 +1729,8 @@ fn check_exports(e: @env) { // Impl resolution type method_info = {did: def_id, n_tps: uint, ident: ast::ident}; -type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]}; +type _impl = {did: def_id, iface_did: option::t, + ident: ast::ident, methods: [@method_info]}; type iscopes = list<@[@_impl]>; fn resolve_impls(e: @env, c: @ast::crate) { @@ -1757,14 +1786,20 @@ fn find_impls_in_view_item(e: env, vi: @ast::view_item, } } -fn find_impls_in_item(i: @ast::item, &impls: [@_impl], +fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl], name: option::t, ck_exports: option::t) { alt i.node { - ast::item_impl(_, _, _, mthds) { + ast::item_impl(_, ifce, _, mthds) { if alt name { some(n) { n == i.ident } _ { true } } && alt ck_exports { some(m) { is_exported(i.ident, m) } _ { true } } { impls += [@{did: local_def(i.id), + iface_did: alt ifce { + some(@{node: ast::ty_path(_, id), _}) { + some(def_id_of_def(e.def_map.get(id))) + } + _ { none } + }, ident: i.ident, methods: vec::map(mthds, {|m| @{did: local_def(m.id), @@ -1788,7 +1823,7 @@ fn find_impls_in_mod(e: env, m: def, &impls: [@_impl], cached = if defid.crate == ast::local_crate { let tmp = []; for i in option::get(e.mod_map.get(defid.node).m).items { - find_impls_in_item(i, tmp, name, none); + find_impls_in_item(e, i, tmp, name, none); } @tmp } else { @@ -1816,7 +1851,7 @@ fn visit_block_with_impl_scope(e: @env, b: ast::blk, sc: iscopes, for st in b.node.stmts { alt st.node { ast::stmt_decl(@{node: ast::decl_item(i), _}, _) { - find_impls_in_item(i, impls, none, none); + find_impls_in_item(*e, i, impls, none, none); } _ {} } @@ -1829,13 +1864,15 @@ fn visit_mod_with_impl_scope(e: @env, m: ast::_mod, s: span, sc: iscopes, v: vt) { let impls = []; for vi in m.view_items { find_impls_in_view_item(*e, vi, impls, sc); } - for i in m.items { find_impls_in_item(i, impls, none, none); } + for i in m.items { find_impls_in_item(*e, i, impls, none, none); } visit::visit_mod(m, s, vec::len(impls) > 0u ? cons(@impls, @sc) : sc, v); } fn resolve_impl_in_expr(e: @env, x: @ast::expr, sc: iscopes, v: vt) { alt x.node { - ast::expr_field(_, _, _) { e.impl_map.insert(x.id, sc); } + ast::expr_field(_, _, _) | ast::expr_path(_) { + e.impl_map.insert(x.id, sc); + } _ {} } visit::visit_expr(x, sc, v); diff --git a/src/comp/middle/shape.rs b/src/comp/middle/shape.rs index 94529b8be603..f78e0ffef85b 100644 --- a/src/comp/middle/shape.rs +++ b/src/comp/middle/shape.rs @@ -450,7 +450,7 @@ fn gen_tag_shapes(ccx: @crate_ctxt) -> ValueRef { let did = ccx.shape_cx.tag_order[i]; let variants = ty::tag_variants(ccx.tcx, did); let item_tyt = ty::lookup_item_type(ccx.tcx, did); - let ty_param_count = vec::len(item_tyt.bounds); + let ty_param_count = vec::len(*item_tyt.bounds); for v: ty::variant_info in *variants { offsets += [vec::len(data) as u16]; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 26a9a29b6857..c279c0a9b49d 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -13,7 +13,6 @@ // but many TypeRefs correspond to one ty::t; for instance, tup(int, int, // int) and rec(x=int, y=int, z=int) will have the same TypeRef. -import core::{either, str, int, uint, option, vec}; import std::{map, time}; import std::map::hashmap; import std::map::{new_int_hash, new_str_hash}; @@ -81,13 +80,12 @@ fn type_of_explicit_args(cx: @crate_ctxt, sp: span, inputs: [ty::arg]) -> // - create_llargs_for_fn_args. // - new_fn_ctxt // - trans_args -fn type_of_fn(cx: @crate_ctxt, sp: span, - is_method: bool, inputs: [ty::arg], - output: ty::t, ty_param_count: uint) - : non_ty_var(cx, output) -> TypeRef { +fn type_of_fn(cx: @crate_ctxt, sp: span, is_method: bool, inputs: [ty::arg], + output: ty::t, params: [ty::param_bounds]) -> TypeRef { let atys: [TypeRef] = []; // Arg 0: Output pointer. + check non_ty_var(cx, output); let out_ty = T_ptr(type_of_inner(cx, sp, output)); atys += [out_ty]; @@ -100,8 +98,15 @@ fn type_of_fn(cx: @crate_ctxt, sp: span, // Args >2: ty params, if not acquired via capture... if !is_method { - let i = 0u; - while i < ty_param_count { atys += [T_ptr(cx.tydesc_type)]; i += 1u; } + for bounds in params { + atys += [T_ptr(cx.tydesc_type)]; + for bound in *bounds { + alt bound { + ty::bound_iface(_) { atys += [T_ptr(T_dict())]; } + _ {} + } + } + } } // ... then explicit args. atys += type_of_explicit_args(cx, sp, inputs); @@ -110,15 +115,13 @@ fn type_of_fn(cx: @crate_ctxt, sp: span, // Given a function type and a count of ty params, construct an llvm type fn type_of_fn_from_ty(cx: @crate_ctxt, sp: span, fty: ty::t, - ty_param_count: uint) - : returns_non_ty_var(cx, fty) -> TypeRef { + param_bounds: [ty::param_bounds]) -> TypeRef { // FIXME: Check should be unnecessary, b/c it's implied // by returns_non_ty_var(t). Make that a postcondition // (see Issue #586) let ret_ty = ty::ty_fn_ret(cx.tcx, fty); - check non_ty_var(cx, ret_ty); ret type_of_fn(cx, sp, false, ty::ty_fn_args(cx.tcx, fty), - ret_ty, ty_param_count); + ret_ty, param_bounds); } fn type_of_inner(cx: @crate_ctxt, sp: span, t: ty::t) @@ -169,12 +172,10 @@ fn type_of_inner(cx: @crate_ctxt, sp: span, t: ty::t) T_struct(tys) } ty::ty_fn(_) { - // FIXME: could be a constraint on ty_fn - check returns_non_ty_var(cx, t); - T_fn_pair(cx, type_of_fn_from_ty(cx, sp, t, 0u)) + T_fn_pair(cx, type_of_fn_from_ty(cx, sp, t, [])) } ty::ty_native_fn(args, out) { - let nft = native_fn_wrapper_type(cx, sp, 0u, t); + let nft = native_fn_wrapper_type(cx, sp, [], t); T_fn_pair(cx, nft) } ty::ty_obj(meths) { cx.rust_object_type } @@ -204,6 +205,11 @@ fn type_of_inner(cx: @crate_ctxt, sp: span, t: ty::t) ty::ty_opaque_closure. { T_opaque_closure(cx) } + ty::ty_constr(subt,_) { + // FIXME: could be a constraint on ty_fn + check non_ty_var(cx, subt); + type_of_inner(cx, sp, subt) + } _ { fail "type_of_inner not implemented for this kind of type"; } @@ -233,8 +239,7 @@ fn type_of_ty_param_bounds_and_ty(lcx: @local_ctxt, sp: span, let t = tpt.ty; alt ty::struct(cx.tcx, t) { ty::ty_fn(_) | ty::ty_native_fn(_, _) { - check returns_non_ty_var(cx, t); - ret type_of_fn_from_ty(cx, sp, t, vec::len(tpt.bounds)); + ret type_of_fn_from_ty(cx, sp, t, *tpt.bounds); } _ { // fall through @@ -906,7 +911,10 @@ fn linearize_ty_params(cx: @block_ctxt, t: ty::t) -> ty::ty_param(pid, _) { let seen: bool = false; for d: uint in r.defs { if d == pid { seen = true; } } - if !seen { r.vals += [r.cx.fcx.lltydescs[pid]]; r.defs += [pid]; } + if !seen { + r.vals += [r.cx.fcx.lltyparams[pid].desc]; + r.defs += [pid]; + } } _ { } } @@ -1042,8 +1050,9 @@ fn get_tydesc(cx: @block_ctxt, t: ty::t, escapes: bool, // Is the supplied type a type param? If so, return the passed-in tydesc. alt ty::type_param(bcx_tcx(cx), t) { some(id) { - if id < vec::len(cx.fcx.lltydescs) { - ret {kind: tk_param, result: rslt(cx, cx.fcx.lltydescs[id])}; + if id < vec::len(cx.fcx.lltyparams) { + ret {kind: tk_param, + result: rslt(cx, cx.fcx.lltyparams[id].desc)}; } else { bcx_tcx(cx).sess.span_bug(cx.sp, "Unbound typaram in get_tydesc: " + @@ -1206,10 +1215,7 @@ fn make_generic_glue_inner(cx: @local_ctxt, sp: span, t: ty::t, p += 1u; } - // FIXME: Implement some kind of freeze operation in the standard library. - let lltydescs_frozen = []; - for lltydesc: ValueRef in lltydescs { lltydescs_frozen += [lltydesc]; } - fcx.lltydescs = lltydescs_frozen; + fcx.lltyparams = vec::map_mut(lltydescs, {|d| {desc: d, dicts: none}}); let bcx = new_top_block_ctxt(fcx); let lltop = bcx.llbb; @@ -2559,10 +2565,13 @@ fn trans_do_while(cx: @block_ctxt, body: ast::blk, cond: @ast::expr) -> ret next_cx; } -type generic_info = - {item_type: ty::t, - static_tis: [option::t<@tydesc_info>], - tydescs: [ValueRef]}; +type generic_info = { + item_type: ty::t, + static_tis: [option::t<@tydesc_info>], + tydescs: [ValueRef], + param_bounds: @[ty::param_bounds], + origins: option::t +}; tag lval_kind { temporary; //< Temporary value passed by value if of immediate type @@ -2571,7 +2580,12 @@ tag lval_kind { } type local_var_result = {val: ValueRef, kind: lval_kind}; type lval_result = {bcx: @block_ctxt, val: ValueRef, kind: lval_kind}; -tag callee_env { obj_env(ValueRef); null_env; is_closure; } +tag callee_env { + null_env; + is_closure; + obj_env(ValueRef); + dict_env(ValueRef, ValueRef); +} type lval_maybe_callee = {bcx: @block_ctxt, val: ValueRef, kind: lval_kind, @@ -2608,18 +2622,19 @@ fn trans_external_path(cx: @block_ctxt, did: ast::def_id, fn lval_static_fn(bcx: @block_ctxt, fn_id: ast::def_id, id: ast::node_id) -> lval_maybe_callee { - let tpt = ty::lookup_item_type(bcx_tcx(bcx), fn_id); + let ccx = bcx_ccx(bcx); + let tpt = ty::lookup_item_type(ccx.tcx, fn_id); let val = if fn_id.crate == ast::local_crate { // Internal reference. - assert (bcx_ccx(bcx).item_ids.contains_key(fn_id.node)); - bcx_ccx(bcx).item_ids.get(fn_id.node) + assert (ccx.item_ids.contains_key(fn_id.node)); + ccx.item_ids.get(fn_id.node) } else { // External reference. trans_external_path(bcx, fn_id, tpt) }; - let tys = ty::node_id_to_type_params(bcx_tcx(bcx), id); + let tys = ty::node_id_to_type_params(ccx.tcx, id); let gen = none, bcx = bcx; - if vec::len::(tys) != 0u { + if vec::len(tys) != 0u { let tydescs = [], tis = []; for t in tys { // TODO: Doesn't always escape. @@ -2629,7 +2644,11 @@ fn lval_static_fn(bcx: @block_ctxt, fn_id: ast::def_id, id: ast::node_id) bcx = td.bcx; tydescs += [td.val]; } - gen = some({item_type: tpt.ty, static_tis: tis, tydescs: tydescs}); + gen = some({item_type: tpt.ty, + static_tis: tis, + tydescs: tydescs, + param_bounds: tpt.bounds, + origins: ccx.dict_map.find(id)}); } ret {bcx: bcx, val: val, kind: owned, env: null_env, generic: gen}; } @@ -2731,7 +2750,7 @@ fn trans_var(cx: @block_ctxt, sp: span, def: ast::def, id: ast::node_id) ret lval_no_env(cx, ccx.consts.get(did.node), owned); } else { let tp = ty::node_id_to_monotype(ccx.tcx, id); - let val = trans_external_path(cx, did, @{bounds: [], ty: tp}); + let val = trans_external_path(cx, did, {bounds: @[], ty: tp}); ret lval_no_env(cx, load_if_immediate(cx, val, tp), owned_imm); } } @@ -2764,10 +2783,8 @@ fn trans_object_field_inner(bcx: @block_ctxt, o: ValueRef, let fn_ty: ty::t = ty::mk_fn(tcx, mths[ix].fty); let ret_ty = ty::ty_fn_ret(tcx, fn_ty); // FIXME: constrain ty_obj? - check non_ty_var(ccx, ret_ty); - let ll_fn_ty = type_of_fn(ccx, bcx.sp, true, - ty::ty_fn_args(tcx, fn_ty), ret_ty, 0u); + ty::ty_fn_args(tcx, fn_ty), ret_ty, []); v = Load(bcx, PointerCast(bcx, v, T_ptr(T_ptr(ll_fn_ty)))); ret {bcx: bcx, mthptr: v, objptr: o}; } @@ -2840,17 +2857,6 @@ fn expr_is_lval(bcx: @block_ctxt, e: @ast::expr) -> bool { ty::expr_is_lval(ccx.method_map, ccx.tcx, e) } -// This is for impl methods, not obj methods. -fn trans_method_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr, - did: ast::def_id) -> lval_maybe_callee { - let tz = [], tr = []; - let basety = ty::expr_ty(bcx_tcx(bcx), base); - let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety}, - type_of_or_i8(bcx, basety), tz, tr, base); - let val = PointerCast(bcx, val, T_opaque_boxed_closure_ptr(bcx_ccx(bcx))); - {env: obj_env(val) with lval_static_fn(bcx, did, e.id)} -} - fn trans_callee(bcx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee { alt e.node { ast::expr_path(p) { ret trans_path(bcx, p, e.id); } @@ -2858,8 +2864,12 @@ fn trans_callee(bcx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee { // Lval means this is a record field, so not a method if !expr_is_lval(bcx, e) { alt bcx_ccx(bcx).method_map.find(e.id) { - some(did) { // An impl method - ret trans_method_callee(bcx, e, base, did); + some(typeck::method_static(did)) { // An impl method + ret trans_impl::trans_static_callee(bcx, e, base, did); + } + some(typeck::method_param(iid, off, p, b)) { + ret trans_impl::trans_dict_callee( + bcx, e, base, iid, off, p, b); } none. { // An object method let of = trans_object_field(bcx, base, ident); @@ -2930,7 +2940,7 @@ fn maybe_add_env(bcx: @block_ctxt, c: lval_maybe_callee) -> (lval_kind, ValueRef) { alt c.env { is_closure. { (c.kind, c.val) } - obj_env(_) { + obj_env(_) | dict_env(_, _) { fail "Taking the value of a method does not work yet (issue #435)"; } null_env. { @@ -3143,7 +3153,23 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef, alt gen { some(g) { lazily_emit_all_generic_info_tydesc_glues(cx, g); - lltydescs = g.tydescs; + let i = 0u, n_orig = 0u; + for param in *g.param_bounds { + lltydescs += [g.tydescs[i]]; + for bound in *param { + alt bound { + ty::bound_iface(_) { + let res = trans_impl::get_dict( + bcx, option::get(g.origins)[n_orig]); + lltydescs += [res.val]; + bcx = res.bcx; + n_orig += 1u; + } + _ {} + } + } + i += 1u; + } args = ty::ty_fn_args(tcx, g.item_type); retty = ty::ty_fn_ret(tcx, g.item_type); } @@ -3214,12 +3240,13 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr, let bcx = f_res.bcx; let faddr = f_res.val; - let llenv; + let llenv, dict_param = none; alt f_res.env { null_env. { llenv = llvm::LLVMGetUndef(T_opaque_boxed_closure_ptr(bcx_ccx(cx))); } obj_env(e) { llenv = e; } + dict_env(dict, e) { llenv = e; dict_param = some(dict); } is_closure. { // It's a closure. Have to fetch the elements if f_res.kind == owned { @@ -3238,6 +3265,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr, trans_args(bcx, llenv, f_res.generic, args, fn_expr_ty, dest); bcx = args_res.bcx; let llargs = args_res.args; + option::may(dict_param) {|dict| llargs = [dict] + llargs} let llretslot = args_res.retslot; /* If the block is terminated, @@ -3300,8 +3328,7 @@ fn invoke_(bcx: @block_ctxt, llfn: ValueRef, llargs: [ValueRef], // cleanups to run if bcx.unreachable { ret bcx; } let normal_bcx = new_sub_block_ctxt(bcx, "normal return"); - invoker(bcx, llfn, llargs, - normal_bcx.llbb, + invoker(bcx, llfn, llargs, normal_bcx.llbb, get_landing_pad(bcx, to_zero, to_revoke)); ret normal_bcx; } @@ -4345,7 +4372,7 @@ fn new_fn_ctxt_w_id(cx: @local_ctxt, sp: span, llfndecl: ValueRef, llobjfields: new_int_hash::(), lllocals: new_int_hash::(), llupvars: new_int_hash::(), - mutable lltydescs: [], + mutable lltyparams: [], derived_tydescs: ty::new_ty_hash(), id: id, ret_style: rstyle, @@ -4387,10 +4414,22 @@ fn create_llargs_for_fn_args(cx: @fn_ctxt, ty_self: self_arg, obj_self(_) {} _ { for tp in ty_params { - let llarg = llvm::LLVMGetParam(cx.llfn, arg_n); - assert (llarg as int != 0); - cx.lltydescs += [llarg]; + let lltydesc = llvm::LLVMGetParam(cx.llfn, arg_n), dicts = none; arg_n += 1u; + for bound in *fcx_tcx(cx).ty_param_bounds.get(tp.id) { + alt bound { + ty::bound_iface(_) { + let dict = llvm::LLVMGetParam(cx.llfn, arg_n); + arg_n += 1u; + dicts = some(alt dicts { + none. { [dict] } + some(ds) { ds + [dict] } + }); + } + _ {} + } + } + cx.lltyparams += [{desc: lltydesc, dicts: dicts}]; } } } @@ -4479,7 +4518,7 @@ fn populate_fn_ctxt_from_llself(fcx: @fn_ctxt, llself: val_self_pair) { let lltyparam: ValueRef = GEPi(bcx, obj_typarams, [0, i]); lltyparam = Load(bcx, lltyparam); - fcx.lltydescs += [lltyparam]; + fcx.lltyparams += [{desc: lltyparam, dicts: none}]; i += 1; } i = 0; @@ -4658,7 +4697,8 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id, let ty_param_substs: [ty::t] = []; i = 0u; for tp: ast::ty_param in ty_params { - ty_param_substs += [ty::mk_param(ccx.tcx, i, @[])]; + ty_param_substs += [ty::mk_param(ccx.tcx, i, + ast_util::local_def(tp.id))]; i += 1u; } let arg_tys = arg_tys_of_fn(ccx, variant.node.id); @@ -4700,20 +4740,6 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id, finish_fn(fcx, lltop); } -fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method], - id: ast::node_id, tps: [ast::ty_param]) { - let sub_cx = extend_path(cx, name); - for m in methods { - alt cx.ccx.item_ids.find(m.id) { - some(llfn) { - trans_fn(extend_path(sub_cx, m.ident), m.span, m.decl, m.body, - llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)), - tps + m.tps, m.id); - } - } - } -} - // FIXME: this should do some structural hash-consing to avoid // duplicate constants. I think. Maybe LLVM has a magical mode @@ -5013,8 +5039,8 @@ fn trans_item(cx: @local_ctxt, item: ast::item) { with *extend_path(cx, item.ident)}; trans_obj(sub_cx, item.span, ob, ctor_id, tps); } - ast::item_impl(tps, _, _, ms) { - trans_impl(cx, item.ident, ms, item.id, tps); + ast::item_impl(tps, ifce, _, ms) { + trans_impl::trans_impl(cx, item.ident, ms, item.id, tps, ifce); } ast::item_res(decl, tps, body, dtor_id, ctor_id) { trans_res_ctor(cx, item.span, decl, ctor_id, tps); @@ -5080,13 +5106,17 @@ fn register_fn(ccx: @crate_ctxt, sp: span, path: [str], flav: str, register_fn_full(ccx, sp, path, flav, ty_params, node_id, t); } +fn param_bounds(ccx: @crate_ctxt, tp: ast::ty_param) -> ty::param_bounds { + ccx.tcx.ty_param_bounds.get(tp.id) +} + fn register_fn_full(ccx: @crate_ctxt, sp: span, path: [str], _flav: str, - ty_params: [ast::ty_param], node_id: ast::node_id, + tps: [ast::ty_param], node_id: ast::node_id, node_type: ty::t) : returns_non_ty_var(ccx, node_type) { let path = path; - let llfty = - type_of_fn_from_ty(ccx, sp, node_type, vec::len(ty_params)); + let llfty = type_of_fn_from_ty(ccx, sp, node_type, + vec::map(tps, {|p| param_bounds(ccx, p)})); let ps: str = mangle_exported_name(ccx, path, node_type); let llfn: ValueRef = decl_cdecl_fn(ccx.llmod, ps, llfty); ccx.item_ids.insert(node_id, llfn); @@ -5122,9 +5152,7 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef, ty: ty::mk_vec(ccx.tcx, {ty: unit_ty, mut: ast::imm})}; // FIXME: mk_nil should have a postcondition let nt = ty::mk_nil(ccx.tcx); - check non_ty_var(ccx, nt); - - let llfty = type_of_fn(ccx, sp, false, [vecarg_ty], nt, 0u); + let llfty = type_of_fn(ccx, sp, false, [vecarg_ty], nt, []); let llfdecl = decl_fn(ccx.llmod, "_rust_main", lib::llvm::LLVMCCallConv, llfty); @@ -5152,6 +5180,8 @@ fn create_main_wrapper(ccx: @crate_ctxt, sp: span, main_llfn: ValueRef, fn main_name() -> str { ret "main"; } #[cfg(target_os = "linux")] fn main_name() -> str { ret "main"; } + #[cfg(target_os = "freebsd")] + fn main_name() -> str { ret "main"; } let llfty = T_fn([ccx.int_type, ccx.int_type], ccx.int_type); let llfn = decl_cdecl_fn(ccx.llmod, main_name(), llfty); let llbb = str::as_buf("top", {|buf| @@ -5215,12 +5245,12 @@ fn native_fn_ty_param_count(cx: @crate_ctxt, id: ast::node_id) -> uint { ret count; } -fn native_fn_wrapper_type(cx: @crate_ctxt, sp: span, ty_param_count: uint, +fn native_fn_wrapper_type(cx: @crate_ctxt, sp: span, + param_bounds: [ty::param_bounds], x: ty::t) -> TypeRef { alt ty::struct(cx.tcx, x) { ty::ty_native_fn(args, out) { - check non_ty_var(cx, out); - ret type_of_fn(cx, sp, false, args, out, ty_param_count); + ret type_of_fn(cx, sp, false, args, out, param_bounds); } } } @@ -5245,56 +5275,54 @@ fn collect_native_item(ccx: @crate_ctxt, _v: vt<[str]>) { alt i.node { ast::native_item_fn(_, tps) { - if !ccx.obj_methods.contains_key(i.id) { - let sp = i.span; - let id = i.id; - let node_type = node_id_type(ccx, id); - let fn_abi = - alt attr::get_meta_item_value_str_by_name(i.attrs, "abi") { - option::none. { + let sp = i.span; + let id = i.id; + let node_type = node_id_type(ccx, id); + let fn_abi = + alt attr::get_meta_item_value_str_by_name(i.attrs, "abi") { + option::none. { // if abi isn't specified for this function, inherit from - // its enclosing native module - option::get(*abi) + // its enclosing native module + option::get(*abi) } - _ { - alt attr::native_abi(i.attrs) { - either::right(abi_) { abi_ } - either::left(msg) { ccx.sess.span_fatal(i.span, msg) } + _ { + alt attr::native_abi(i.attrs) { + either::right(abi_) { abi_ } + either::left(msg) { ccx.sess.span_fatal(i.span, msg) } + } } - } }; - alt fn_abi { - ast::native_abi_rust_intrinsic. { - // For intrinsics: link the function directly to the intrinsic - // function itself. - let num_ty_param = vec::len(tps); - check returns_non_ty_var(ccx, node_type); - let fn_type = type_of_fn_from_ty(ccx, sp, node_type, - num_ty_param); - let ri_name = "rust_intrinsic_" + link_name(i); - let llnativefn = get_extern_fn( - ccx.externs, ccx.llmod, ri_name, - lib::llvm::LLVMCCallConv, fn_type); - ccx.item_ids.insert(id, llnativefn); - ccx.item_symbols.insert(id, ri_name); - } + alt fn_abi { + ast::native_abi_rust_intrinsic. { + // For intrinsics: link the function directly to the intrinsic + // function itself. + let fn_type = type_of_fn_from_ty( + ccx, sp, node_type, + vec::map(tps, {|p| param_bounds(ccx, p)})); + let ri_name = "rust_intrinsic_" + link_name(i); + let llnativefn = get_extern_fn( + ccx.externs, ccx.llmod, ri_name, + lib::llvm::LLVMCCallConv, fn_type); + ccx.item_ids.insert(id, llnativefn); + ccx.item_symbols.insert(id, ri_name); + } - ast::native_abi_cdecl. | ast::native_abi_stdcall. { - // For true external functions: create a rust wrapper - // and link to that. The rust wrapper will handle - // switching to the C stack. - let new_pt = pt + [i.ident]; - register_fn(ccx, i.span, new_pt, "native fn", tps, i.id); - } - } + ast::native_abi_cdecl. | ast::native_abi_stdcall. { + // For true external functions: create a rust wrapper + // and link to that. The rust wrapper will handle + // switching to the C stack. + let new_pt = pt + [i.ident]; + register_fn(ccx, i.span, new_pt, "native fn", tps, i.id); + } } } _ { } } } -fn collect_item_1(ccx: @crate_ctxt, abi: @mutable option::t, - i: @ast::item, &&pt: [str], v: vt<[str]>) { +fn collect_item(ccx: @crate_ctxt, abi: @mutable option::t, + i: @ast::item, &&pt: [str], v: vt<[str]>) { + let new_pt = pt + [i.ident]; alt i.node { ast::item_const(_, _) { let typ = node_id_type(ccx, i.id); @@ -5319,26 +5347,11 @@ fn collect_item_1(ccx: @crate_ctxt, abi: @mutable option::t, } } } - _ { } - } - visit::visit_item(i, pt + [i.ident], v); -} - -fn collect_item_2(ccx: @crate_ctxt, i: @ast::item, &&pt: [str], - v: vt<[str]>) { - let new_pt = pt + [i.ident]; - visit::visit_item(i, new_pt, v); - alt i.node { ast::item_fn(_, tps, _) { - if !ccx.obj_methods.contains_key(i.id) { - register_fn(ccx, i.span, new_pt, "fn", tps, i.id); - } + register_fn(ccx, i.span, new_pt, "fn", tps, i.id); } ast::item_obj(ob, tps, ctor_id) { register_fn(ccx, i.span, new_pt, "obj_ctor", tps, ctor_id); - for m: @ast::method in ob.methods { - ccx.obj_methods.insert(m.id, ()); - } } ast::item_impl(tps, _, _, methods) { let name = ccx.names.next(i.ident); @@ -5358,47 +5371,28 @@ fn collect_item_2(ccx: @crate_ctxt, i: @ast::item, &&pt: [str], check returns_non_ty_var(ccx, t); register_fn_full(ccx, i.span, new_pt, "res_dtor", tps, i.id, t); } - _ { } - } -} - -fn collect_items(ccx: @crate_ctxt, crate: @ast::crate) { - let abi = @mutable none::; - let visitor0 = visit::default_visitor(); - let visitor1 = - @{visit_native_item: bind collect_native_item(ccx, abi, _, _, _), - visit_item: bind collect_item_1(ccx, abi, _, _, _) with *visitor0}; - let visitor2 = - @{visit_item: bind collect_item_2(ccx, _, _, _) with *visitor0}; - visit::visit_crate(*crate, [], visit::mk_vt(visitor1)); - visit::visit_crate(*crate, [], visit::mk_vt(visitor2)); -} - -fn collect_tag_ctor(ccx: @crate_ctxt, i: @ast::item, &&pt: [str], - v: vt<[str]>) { - let new_pt = pt + [i.ident]; - visit::visit_item(i, new_pt, v); - alt i.node { ast::item_tag(variants, tps) { - for variant: ast::variant in variants { + for variant in variants { if vec::len(variant.node.args) != 0u { register_fn(ccx, i.span, new_pt + [variant.node.name], "tag", tps, variant.node.id); } } } - _ {/* fall through */ } + _ { } } + visit::visit_item(i, new_pt, v); } -fn collect_tag_ctors(ccx: @crate_ctxt, crate: @ast::crate) { - let visitor = - @{visit_item: bind collect_tag_ctor(ccx, _, _, _) - with *visit::default_visitor()}; - visit::visit_crate(*crate, [], visit::mk_vt(visitor)); +fn collect_items(ccx: @crate_ctxt, crate: @ast::crate) { + let abi = @mutable none::; + visit::visit_crate(*crate, [], visit::mk_vt(@{ + visit_native_item: bind collect_native_item(ccx, abi, _, _, _), + visit_item: bind collect_item(ccx, abi, _, _, _) + with *visit::default_visitor() + })); } - // The constant translation pass. fn trans_constant(ccx: @crate_ctxt, it: @ast::item, &&pt: [str], v: vt<[str]>) { @@ -5407,15 +5401,12 @@ fn trans_constant(ccx: @crate_ctxt, it: @ast::item, &&pt: [str], alt it.node { ast::item_tag(variants, _) { let i = 0u; - let n_variants = vec::len::(variants); - while i < n_variants { - let variant = variants[i]; - let p = new_pt + [it.ident, variant.node.name, "discrim"]; + for variant in variants { + let p = new_pt + [variant.node.name, "discrim"]; let s = mangle_exported_name(ccx, p, ty::mk_int(ccx.tcx)); - let discrim_gvar = - str::as_buf(s, {|buf| - llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) - }); + let discrim_gvar = str::as_buf(s, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) + }); llvm::LLVMSetInitializer(discrim_gvar, C_int(ccx, i as int)); llvm::LLVMSetGlobalConstant(discrim_gvar, True); ccx.discrims.insert( @@ -5424,6 +5415,28 @@ fn trans_constant(ccx: @crate_ctxt, it: @ast::item, &&pt: [str], i += 1u; } } + ast::item_impl(tps, some(@{node: ast::ty_path(_, id), _}), _, ms) { + let i_did = ast_util::def_id_of_def(ccx.tcx.def_map.get(id)); + let ty = ty::lookup_item_type(ccx.tcx, i_did).ty; + // FIXME[impl] use the same name as used in collect_items, for + // slightly more consistent symbol names? + let new_pt = pt + [ccx.names.next(it.ident)]; + let extra_tps = vec::map(tps, {|p| param_bounds(ccx, p)}); + let tbl = C_struct(vec::map(*ty::iface_methods(ccx.tcx, i_did), {|im| + alt vec::find(ms, {|m| m.ident == im.ident}) { + some(m) { + trans_impl::trans_wrapper(ccx, new_pt, extra_tps, m) + } + } + })); + let s = mangle_exported_name(ccx, new_pt + ["!vtable"], ty); + let vt_gvar = str::as_buf(s, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl), buf) + }); + llvm::LLVMSetInitializer(vt_gvar, tbl); + llvm::LLVMSetGlobalConstant(vt_gvar, True); + ccx.item_ids.insert(it.id, vt_gvar); + } _ { } } } @@ -5601,7 +5614,8 @@ fn write_abi_version(ccx: @crate_ctxt) { fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, output: str, emap: resolve::exp_map, amap: ast_map::map, mut_map: mut::mut_map, copy_map: alias::copy_map, - last_uses: last_use::last_uses, method_map: typeck::method_map) + last_uses: last_use::last_uses, method_map: typeck::method_map, + dict_map: typeck::dict_map) -> (ModuleRef, link::link_meta) { let sha = std::sha1::mk_sha1(); let link_meta = link::build_link_meta(sess, *crate, output, sha); @@ -5666,7 +5680,6 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, discrims: ast_util::new_def_id_hash::(), discrim_symbols: new_int_hash::(), consts: new_int_hash::(), - obj_methods: new_int_hash::<()>(), tydescs: ty::new_ty_hash(), module_data: new_str_hash::(), lltypes: ty::new_ty_hash(), @@ -5679,6 +5692,7 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, copy_map: copy_map, last_uses: last_uses, method_map: method_map, + dict_map: dict_map, stats: {mutable n_static_tydescs: 0u, mutable n_derived_tydescs: 0u, @@ -5702,7 +5716,6 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, dbg_cx: dbg_cx}; let cx = new_local_ctxt(ccx); collect_items(ccx, crate); - collect_tag_ctors(ccx, crate); trans_constants(ccx, crate); trans_mod(cx, crate.node.module); fill_crate_map(ccx, crate_map); @@ -5720,7 +5733,6 @@ fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, #error("n_null_glues: %u", ccx.stats.n_null_glues); #error("n_real_glues: %u", ccx.stats.n_real_glues); - for timing: {ident: str, time: int} in *ccx.stats.fn_times { #error("time: %s took %d ms", timing.ident, timing.time); } diff --git a/src/comp/middle/trans_closure.rs b/src/comp/middle/trans_closure.rs index 7205dd27c1c4..7759072f0288 100644 --- a/src/comp/middle/trans_closure.rs +++ b/src/comp/middle/trans_closure.rs @@ -82,17 +82,21 @@ tag environment_value { // Given a closure ty, emits a corresponding tuple ty fn mk_closure_ty(tcx: ty::ctxt, ck: ty::closure_kind, - n_bound_tds: uint, + ty_params: [fn_ty_param], bound_data_ty: ty::t) -> ty::t { let tydesc_ty = alt ck { ty::closure_block. | ty::closure_shared. { ty::mk_type(tcx) } ty::closure_send. { ty::mk_send_type(tcx) } }; - ret ty::mk_tup(tcx, [ - tydesc_ty, - ty::mk_tup(tcx, vec::init_elt(tydesc_ty, n_bound_tds)), - bound_data_ty]); + let param_ptrs = []; + for tp in ty_params { + param_ptrs += [tydesc_ty]; + option::may(tp.dicts) {|dicts| + for dict in dicts { param_ptrs += [tydesc_ty]; } + } + } + ty::mk_tup(tcx, [tydesc_ty, ty::mk_tup(tcx, param_ptrs), bound_data_ty]) } fn shared_opaque_closure_box_ty(tcx: ty::ctxt) -> ty::t { @@ -117,7 +121,7 @@ type closure_result = { // heap allocated closure that copies the upvars into environment. // Otherwise, it is stack allocated and copies pointers to the upvars. fn store_environment( - bcx: @block_ctxt, lltydescs: [ValueRef], + bcx: @block_ctxt, lltyparams: [fn_ty_param], bound_values: [environment_value], ck: ty::closure_kind) -> closure_result { @@ -162,7 +166,7 @@ fn store_environment( } let bound_data_ty = ty::mk_tup(tcx, bound_tys); let closure_ty = - mk_closure_ty(tcx, ck, vec::len(lltydescs), bound_data_ty); + mk_closure_ty(tcx, ck, lltyparams, bound_data_ty); let temp_cleanups = []; @@ -210,7 +214,7 @@ fn store_environment( // in the shape code. Therefore, I am using // tps_normal, which is what we used before. // - // let tps = tps_fn(vec::len(lltydescs)); + // let tps = tps_fn(vec::len(lltyparams)); let tps = tps_normal; let {result:closure_td, _} = @@ -232,10 +236,19 @@ fn store_environment( let {bcx:bcx, val:ty_params_slot} = GEP_tup_like_1(bcx, closure_ty, closure, [0, abi::closure_elt_ty_params]); - vec::iteri(lltydescs) { |i, td| - let ty_param_slot = GEPi(bcx, ty_params_slot, [0, i as int]); - let cloned_td = maybe_clone_tydesc(bcx, ck, td); - Store(bcx, cloned_td, ty_param_slot); + let off = 0; + + for tp in lltyparams { + let cloned_td = maybe_clone_tydesc(bcx, ck, tp.desc); + Store(bcx, cloned_td, GEPi(bcx, ty_params_slot, [0, off])); + off += 1; + option::may(tp.dicts, {|dicts| + for dict in dicts { + let cast = PointerCast(bcx, dict, val_ty(cloned_td)); + Store(bcx, cast, GEPi(bcx, ty_params_slot, [0, off])); + off += 1; + } + }); } // Copy expr values into boxed bindings. @@ -316,7 +329,7 @@ fn build_closure(bcx0: @block_ctxt, } } } - ret store_environment(bcx, copy bcx.fcx.lltydescs, env_vals, ck); + ret store_environment(bcx, copy bcx.fcx.lltyparams, env_vals, ck); } // Given an enclosing block context, a new function context, a closure type, @@ -338,13 +351,23 @@ fn load_environment(enclosing_cx: @block_ctxt, // Populate the type parameters from the environment. We need to // do this first because the tydescs are needed to index into // the bindings if they are dynamically sized. - let tydesc_count = vec::len(enclosing_cx.fcx.lltydescs); let lltydescs = GEPi(bcx, llclosure, [0, abi::box_rc_field_body, abi::closure_elt_ty_params]); - uint::range(0u, tydesc_count) { |i| - let lltydescptr = GEPi(bcx, lltydescs, [0, i as int]); - fcx.lltydescs += [Load(bcx, lltydescptr)]; + let off = 0; + for tp in copy enclosing_cx.fcx.lltyparams { + let tydesc = Load(bcx, GEPi(bcx, lltydescs, [0, off])); + off += 1; + let dicts = option::map(tp.dicts, {|dicts| + let rslt = []; + for dict in dicts { + let dict = Load(bcx, GEPi(bcx, lltydescs, [0, off])); + rslt += [PointerCast(bcx, dict, T_ptr(T_dict()))]; + off += 1; + } + rslt + }); + fcx.lltyparams += [{desc: tydesc, dicts: dicts}]; } // Populate the upvars from the environment. @@ -382,8 +405,7 @@ fn trans_expr_fn(bcx: @block_ctxt, if dest == ignore { ret bcx; } let ccx = bcx_ccx(bcx), bcx = bcx; let fty = node_id_type(ccx, id); - check returns_non_ty_var(ccx, fty); - let llfnty = type_of_fn_from_ty(ccx, sp, fty, 0u); + let llfnty = type_of_fn_from_ty(ccx, sp, fty, []); let sub_cx = extend_path(bcx.fcx.lcx, ccx.names.next("anon")); let s = mangle_internal_name_by_path(ccx, sub_cx.path); let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty); @@ -436,19 +458,25 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t, } // Figure out which tydescs we need to pass, if any. - let outgoing_fty_real; // the type with typarams still in it - let lltydescs: [ValueRef]; - alt f_res.generic { - none. { outgoing_fty_real = outgoing_fty; lltydescs = []; } + let (outgoing_fty_real, lltydescs, param_bounds) = alt f_res.generic { + none. { (outgoing_fty, [], @[]) } some(ginfo) { + for bounds in *ginfo.param_bounds { + for bound in *bounds { + alt bound { + ty::bound_iface(_) { + fail "FIXME[impl] binding bounded types not implemented"; + } + _ {} + } + } + } lazily_emit_all_generic_info_tydesc_glues(cx, ginfo); - outgoing_fty_real = ginfo.item_type; - lltydescs = ginfo.tydescs; + (ginfo.item_type, ginfo.tydescs, ginfo.param_bounds) } - } + }; - let ty_param_count = vec::len(lltydescs); - if vec::len(bound) == 0u && ty_param_count == 0u { + if vec::len(bound) == 0u && vec::len(lltydescs) == 0u { // Trivial 'binding': just return the closure let lv = lval_maybe_callee_to_lval(f_res, pair_ty); bcx = lv.bcx; @@ -480,14 +508,14 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t, // Actually construct the closure let {llbox, box_ty, bcx} = store_environment( - bcx, lltydescs, + bcx, vec::map(lltydescs, {|d| {desc: d, dicts: none}}), env_vals + vec::map(bound, {|x| env_expr(x)}), ty::closure_shared); // Make thunk let llthunk = trans_bind_thunk(cx.fcx.lcx, cx.sp, pair_ty, outgoing_fty_real, args, - box_ty, ty_param_count, target_res); + box_ty, *param_bounds, target_res); // Fill the function pair fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox); @@ -558,7 +586,7 @@ fn trans_bind_thunk(cx: @local_ctxt, outgoing_fty: ty::t, args: [option::t<@ast::expr>], boxed_closure_ty: ty::t, - ty_param_count: uint, + param_bounds: [ty::param_bounds], target_fn: option::t) -> {val: ValueRef, ty: TypeRef} { // If we supported constraints on record fields, we could make the @@ -606,7 +634,7 @@ fn trans_bind_thunk(cx: @local_ctxt, // our bound tydescs, we need to load tydescs out of the environment // before derived tydescs are constructed. To do this, we load them // in the load_env block. - let load_env_bcx = new_raw_block_ctxt(fcx, fcx.llloadenv); + let l_bcx = new_raw_block_ctxt(fcx, fcx.llloadenv); // The 'llenv' that will arrive in the thunk we're creating is an // environment that will contain the values of its arguments and a pointer @@ -616,7 +644,7 @@ fn trans_bind_thunk(cx: @local_ctxt, // 'boxed_closure_ty', which was determined by trans_bind. check (type_has_static_size(ccx, boxed_closure_ty)); let llclosure_ptr_ty = type_of(ccx, sp, boxed_closure_ty); - let llclosure = PointerCast(load_env_bcx, fcx.llenv, llclosure_ptr_ty); + let llclosure = PointerCast(l_bcx, fcx.llenv, llclosure_ptr_ty); // "target", in this context, means the function that's having some of its // arguments bound and that will be called inside the thunk we're @@ -667,19 +695,32 @@ fn trans_bind_thunk(cx: @local_ctxt, let llargs: [ValueRef] = [llretptr, lltargetenv]; // Copy in the type parameters. - let i: uint = 0u; - while i < ty_param_count { - // Silly check - check type_is_tup_like(load_env_bcx, boxed_closure_ty); - let lltyparam_ptr = - GEP_tup_like(load_env_bcx, boxed_closure_ty, llclosure, - [0, abi::box_rc_field_body, - abi::closure_elt_ty_params, i as int]); - load_env_bcx = lltyparam_ptr.bcx; - let td = Load(load_env_bcx, lltyparam_ptr.val); - llargs += [td]; - fcx.lltydescs += [td]; - i += 1u; + check type_is_tup_like(l_bcx, boxed_closure_ty); + let {bcx: l_bcx, val: param_record} = + GEP_tup_like(l_bcx, boxed_closure_ty, llclosure, + [0, abi::box_rc_field_body, abi::closure_elt_ty_params]); + let off = 0; + for param in param_bounds { + let dsc = Load(l_bcx, GEPi(l_bcx, param_record, [0, off])), + dicts = none; + llargs += [dsc]; + off += 1; + for bound in *param { + alt bound { + ty::bound_iface(_) { + let dict = Load(l_bcx, GEPi(l_bcx, param_record, [0, off])); + dict = PointerCast(l_bcx, dict, T_ptr(T_dict())); + llargs += [dict]; + off += 1; + dicts = some(alt dicts { + none. { [dict] } + some(ds) { ds + [dict] } + }); + } + _ {} + } + } + fcx.lltyparams += [{desc: dsc, dicts: dicts}]; } let a: uint = 2u; // retptr, env come first @@ -737,13 +778,11 @@ fn trans_bind_thunk(cx: @local_ctxt, // needs to take. let ccx = bcx_ccx(bcx); - check returns_non_ty_var(ccx, outgoing_fty); let lltargetty = - type_of_fn_from_ty(ccx, sp, outgoing_fty, ty_param_count); + type_of_fn_from_ty(ccx, sp, outgoing_fty, param_bounds); lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(lltargetty)); Call(bcx, lltargetfn, llargs); build_return(bcx); finish_fn(fcx, lltop); ret {val: llthunk, ty: llthunk_ty}; } - diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index 9d0f14e6bd1c..fa0810fcf912 100644 --- a/src/comp/middle/trans_common.rs +++ b/src/comp/middle/trans_common.rs @@ -92,7 +92,6 @@ type crate_ctxt = discrims: hashmap, discrim_symbols: hashmap, consts: hashmap, - obj_methods: hashmap, tydescs: hashmap, module_data: hashmap, lltypes: hashmap, @@ -105,6 +104,7 @@ type crate_ctxt = copy_map: alias::copy_map, last_uses: last_use::last_uses, method_map: typeck::method_map, + dict_map: typeck::dict_map, stats: stats, upcalls: @upcall::upcalls, rust_object_type: TypeRef, @@ -131,6 +131,8 @@ type val_self_pair = {v: ValueRef, t: ty::t}; tag local_val { local_mem(ValueRef); local_imm(ValueRef); } +type fn_ty_param = {desc: ValueRef, dicts: option::t<[ValueRef]>}; + // Function context. Every LLVM function we create will have one of // these. type fn_ctxt = @@ -236,7 +238,7 @@ type fn_ctxt = llobjfields: hashmap, lllocals: hashmap, llupvars: hashmap, - mutable lltydescs: [ValueRef], + mutable lltyparams: [fn_ty_param], derived_tydescs: hashmap, id: ast::node_id, ret_style: ast::ret_style, @@ -320,13 +322,13 @@ fn get_res_dtor(ccx: @crate_ctxt, sp: span, did: ast::def_id, inner_t: ty::t) } } - let params = csearch::get_type_param_count(ccx.sess.get_cstore(), did); + let param_bounds = ty::lookup_item_type(ccx.tcx, did).bounds; let nil_res = ty::mk_nil(ccx.tcx); // FIXME: Silly check -- mk_nil should have a postcondition check non_ty_var(ccx, nil_res); let f_t = type_of_fn(ccx, sp, false, [{mode: ast::by_ref, ty: inner_t}], - nil_res, params); + nil_res, *param_bounds); ret trans::get_extern_const(ccx.externs, ccx.llmod, csearch::get_symbol(ccx.sess.get_cstore(), did), f_t); @@ -406,7 +408,7 @@ fn ty_str(tn: type_names, t: TypeRef) -> str { ret lib::llvm::type_to_str(tn, t); } -fn val_ty(v: ValueRef) -> TypeRef { ret llvm::LLVMTypeOf(v); } +fn val_ty(&&v: ValueRef) -> TypeRef { ret llvm::LLVMTypeOf(v); } fn val_str(tn: type_names, v: ValueRef) -> str { ret ty_str(tn, val_ty(v)); } @@ -533,11 +535,9 @@ fn T_size_t(targ_cfg: @session::config) -> TypeRef { ret T_int(targ_cfg); } -fn T_fn(inputs: [TypeRef], output: TypeRef) -> TypeRef { - unsafe { - ret llvm::LLVMFunctionType(output, to_ptr(inputs), - vec::len::(inputs), False); - } +fn T_fn(inputs: [TypeRef], output: TypeRef) -> TypeRef unsafe { + ret llvm::LLVMFunctionType(output, to_ptr(inputs), + vec::len::(inputs), False); } fn T_fn_pair(cx: @crate_ctxt, tfn: TypeRef) -> TypeRef { @@ -546,10 +546,8 @@ fn T_fn_pair(cx: @crate_ctxt, tfn: TypeRef) -> TypeRef { fn T_ptr(t: TypeRef) -> TypeRef { ret llvm::LLVMPointerType(t, 0u); } -fn T_struct(elts: [TypeRef]) -> TypeRef { - unsafe { - ret llvm::LLVMStructType(to_ptr(elts), vec::len(elts), False); - } +fn T_struct(elts: [TypeRef]) -> TypeRef unsafe { + ret llvm::LLVMStructType(to_ptr(elts), vec::len(elts), False); } fn T_named_struct(name: str) -> TypeRef { @@ -574,6 +572,12 @@ fn T_rust_object() -> TypeRef { ret t; } +// A dict is, in reality, a vtable pointer followed by zero or more pointers +// to tydescs and other dicts that it closes over. But the types and number of +// those are rarely known to the code that needs to manipulate them, so they +// are described by this opaque type. +fn T_dict() -> TypeRef { T_array(T_ptr(T_i8()), 1u) } + fn T_task(targ_cfg: @session::config) -> TypeRef { let t = T_named_struct("task"); diff --git a/src/comp/middle/trans_impl.rs b/src/comp/middle/trans_impl.rs new file mode 100644 index 000000000000..04bff49877f7 --- /dev/null +++ b/src/comp/middle/trans_impl.rs @@ -0,0 +1,172 @@ +import trans::*; +import trans_common::*; +import trans_build::*; +import option::{some, none}; +import syntax::{ast, ast_util}; +import back::link; +import lib::llvm; +import llvm::llvm::{ValueRef, TypeRef, LLVMGetParam}; + +fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method], + id: ast::node_id, tps: [ast::ty_param], + _ifce: option::t<@ast::ty>) { + let sub_cx = extend_path(cx, name); + for m in methods { + alt cx.ccx.item_ids.find(m.id) { + some(llfn) { + trans_fn(extend_path(sub_cx, m.ident), m.span, m.decl, m.body, + llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)), + tps + m.tps, m.id); + } + } + } +} + +fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result { + let tz = [], tr = []; + let basety = ty::expr_ty(bcx_tcx(bcx), base); + let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety}, + T_ptr(type_of_or_i8(bcx, basety)), tz, + tr, base); + rslt(bcx, PointerCast(bcx, val, T_opaque_boxed_closure_ptr(bcx_ccx(bcx)))) +} + +fn trans_static_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr, + did: ast::def_id) -> lval_maybe_callee { + let {bcx, val} = trans_self_arg(bcx, base); + {env: obj_env(val) with lval_static_fn(bcx, did, e.id)} +} + +fn trans_dict_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr, + iface_id: ast::def_id, n_method: uint, + n_param: uint, n_bound: uint) -> lval_maybe_callee { + let tcx = bcx_tcx(bcx); + let {bcx, val} = trans_self_arg(bcx, base); + let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound]; + let method = ty::iface_methods(tcx, iface_id)[n_method]; + let fty = ty::expr_ty(tcx, e); + let bare_fn_ty = type_of_fn_from_ty(bcx_ccx(bcx), ast_util::dummy_sp(), + fty, *method.tps); + let {inputs: bare_inputs, output} = llfn_arg_tys(bare_fn_ty); + let fn_ty = T_fn([val_ty(dict)] + bare_inputs, output); + let vtable = PointerCast(bcx, Load(bcx, GEPi(bcx, dict, [0, 0])), + T_ptr(T_array(T_ptr(fn_ty), n_method + 1u))); + let mptr = Load(bcx, GEPi(bcx, vtable, [0, n_method as int])); + let generic = none; + if vec::len(*method.tps) > 0u { + let tydescs = [], tis = []; + for t in ty::node_id_to_type_params(tcx, e.id) { + // TODO: Doesn't always escape. + let ti = none; + let td = get_tydesc(bcx, t, true, tps_normal, ti).result; + tis += [ti]; + tydescs += [td.val]; + bcx = td.bcx; + } + generic = some({item_type: fty, + static_tis: tis, + tydescs: tydescs, + param_bounds: method.tps, + origins: bcx_ccx(bcx).dict_map.find(e.id)}); + } + {bcx: bcx, val: mptr, kind: owned, + env: dict_env(dict, val), + generic: generic} +} + +fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} { + let out_ty = llvm::llvm::LLVMGetReturnType(ft); + let n_args = llvm::llvm::LLVMCountParamTypes(ft); + let args = vec::init_elt(0 as TypeRef, n_args); + unsafe { llvm::llvm::LLVMGetParamTypes(ft, vec::to_ptr(args)); } + {inputs: args, output: out_ty} +} + +fn trans_wrapper(ccx: @crate_ctxt, pt: [ast::ident], + extra_tps: [ty::param_bounds], m: @ast::method) -> ValueRef { + let real_fn = ccx.item_ids.get(m.id); + let {inputs: real_args, output: real_ret} = + llfn_arg_tys(llvm::llvm::LLVMGetElementType(val_ty(real_fn))); + let extra_ptrs = []; + for tp in extra_tps { + extra_ptrs += [T_ptr(ccx.tydesc_type)]; + for bound in *tp { + alt bound { + ty::bound_iface(_) { extra_ptrs += [T_ptr(T_dict())]; } + _ {} + } + } + } + let env_ty = T_ptr(T_struct([T_ptr(T_i8())] + extra_ptrs)); + let n_extra_ptrs = vec::len(extra_ptrs); + + let wrap_args = [T_ptr(T_dict())] + vec::slice(real_args, 0u, 2u) + + vec::slice(real_args, 2u + vec::len(extra_ptrs), vec::len(real_args)); + let llfn_ty = T_fn(wrap_args, real_ret); + + let lcx = @{path: pt + ["wrapper", m.ident], module_path: [], + obj_typarams: [], obj_fields: [], ccx: ccx}; + let name = link::mangle_internal_name_by_path_and_seq(ccx, pt, m.ident); + let llfn = decl_internal_cdecl_fn(ccx.llmod, name, llfn_ty); + let fcx = new_fn_ctxt(lcx, ast_util::dummy_sp(), llfn); + let bcx = new_top_block_ctxt(fcx), lltop = bcx.llbb; + + let dict = PointerCast(bcx, LLVMGetParam(llfn, 0u), env_ty); + // retptr, self + let args = [LLVMGetParam(llfn, 1u), LLVMGetParam(llfn, 2u)], i = 0u; + // saved tydescs/dicts + while i < n_extra_ptrs { + i += 1u; + args += [load_inbounds(bcx, dict, [0, i as int])]; + } + // the rest of the parameters + let i = 3u, params_total = llvm::llvm::LLVMCountParamTypes(llfn_ty); + while i < params_total { + args += [LLVMGetParam(llfn, i)]; + i += 1u; + } + Call(bcx, ccx.item_ids.get(m.id), args); + build_return(bcx); + finish_fn(fcx, lltop); + ret llfn; +} + +// FIXME[impl] cache these on the function level somehow +fn get_dict(bcx: @block_ctxt, origin: typeck::dict_origin) -> result { + let bcx = bcx, ccx = bcx_ccx(bcx); + alt origin { + typeck::dict_static(impl_did, tys, sub_origins) { + assert impl_did.crate == ast::local_crate; // FIXME[impl] + let vtable = ccx.item_ids.get(impl_did.node); + let impl_params = ty::lookup_item_type(ccx.tcx, impl_did).bounds; + let ptrs = [vtable], i = 0u, origin = 0u, ti = none; + for param in *impl_params { + let rslt = get_tydesc(bcx, tys[i], false, tps_normal, ti).result; + ptrs += [rslt.val]; + bcx = rslt.bcx; + for bound in *param { + alt bound { + ty::bound_iface(_) { + let res = get_dict(bcx, sub_origins[origin]); + ptrs += [res.val]; + bcx = res.bcx; + origin += 1u; + } + _ {} + } + } + i += 1u; + } + let pty = T_ptr(T_i8()), dict_ty = T_array(pty, vec::len(ptrs)); + let dict = alloca(bcx, dict_ty), i = 0; + for ptr in ptrs { + Store(bcx, PointerCast(bcx, ptr, pty), GEPi(bcx, dict, [0, i])); + i += 1; + } + rslt(bcx, PointerCast(bcx, dict, T_ptr(T_dict()))) + } + typeck::dict_param(n_param, n_bound) { + rslt(bcx, option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound]) + } + } +} \ No newline at end of file diff --git a/src/comp/middle/trans_objects.rs b/src/comp/middle/trans_objects.rs index db50c28b85a0..913cca74192a 100644 --- a/src/comp/middle/trans_objects.rs +++ b/src/comp/middle/trans_objects.rs @@ -158,7 +158,7 @@ fn trans_obj(cx: @local_ctxt, sp: span, ob: ast::_obj, ctor_id: ast::node_id, let typarams_ty: ty::t = ty::mk_tup(ccx.tcx, tps); let i: int = 0; for tp: ast::ty_param in ty_params { - let typaram = bcx.fcx.lltydescs[i]; + let typaram = bcx.fcx.lltyparams[i].desc; // Silly check check type_is_tup_like(bcx, typarams_ty); let capture = @@ -880,8 +880,9 @@ fn process_normal_mthd(cx: @local_ctxt, m: @ast::method, self_ty: ty::t, ty::ty_fn(f) { let out = f.output; check non_ty_var(ccx, out); - llfnty = type_of_fn(ccx, m.span, true, f.inputs, out, - vec::len(ty_params)); + llfnty = type_of_fn( + ccx, m.span, true, f.inputs, out, + vec::map(ty_params, {|p| param_bounds(ccx, p)})); } } let mcx: @local_ctxt = @@ -933,7 +934,8 @@ fn type_of_meth(ccx: @crate_ctxt, sp: span, m: @ty::method, tps: [ast::ty_param]) -> TypeRef { let out_ty = m.fty.output; check non_ty_var(ccx, out_ty); - type_of_fn(ccx, sp, true, m.fty.inputs, out_ty, vec::len(tps)) + type_of_fn(ccx, sp, true, m.fty.inputs, out_ty, + vec::map(tps, {|p| param_bounds(ccx, p)})) } // diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs index 5b12477d873c..10d311a5af91 100644 --- a/src/comp/middle/tstate/auxiliary.rs +++ b/src/comp/middle/tstate/auxiliary.rs @@ -50,12 +50,14 @@ fn comma_str(args: [@constr_arg_use]) -> str { fn constraint_to_str(tcx: ty::ctxt, c: sp_constr) -> str { alt c.node { ninit(id, i) { - ret #fmt("init(%s id=%d [%s])", + ret #fmt("init(%s id=%d - arising from %s)", i, id, tcx.sess.span_str(c.span)); } npred(p, _, args) { - ret path_to_str(p) + "(" + comma_str(args) + ")" + "[" + - tcx.sess.span_str(c.span) + "]"; + ret #fmt("%s(%s) - arising from %s", + path_to_str(p), + comma_str(args), + tcx.sess.span_str(c.span)); } } } diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index 2f853f5eb849..1a0a295ed459 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -18,7 +18,7 @@ import util::common::*; import syntax::util::interner; import util::ppaux::ty_to_str; import util::ppaux::ty_constr_to_str; -import util::ppaux::mode_str_1; +import util::ppaux::mode_str; import syntax::print::pprust::*; export node_id_to_monotype; @@ -185,7 +185,7 @@ export closure_kind; export closure_block; export closure_shared; export closure_send; -export param_bound, bound_copy, bound_send, bound_iface; +export param_bound, param_bounds, bound_copy, bound_send, bound_iface; export param_bounds_to_kind; // Data types @@ -194,7 +194,9 @@ type arg = {mode: mode, ty: t}; type field = {ident: ast::ident, mt: mt}; -type method = {ident: ast::ident, tps: [@[param_bound]], fty: fn_ty}; +type param_bounds = @[param_bound]; + +type method = {ident: ast::ident, tps: @[param_bounds], fty: fn_ty}; type constr_table = hashmap; @@ -218,9 +220,9 @@ type ctxt = needs_drop_cache: hashmap, kind_cache: hashmap, ast_ty_to_ty_cache: hashmap<@ast::ty, option::t>, - tag_var_cache: hashmap, + tag_var_cache: hashmap, iface_method_cache: hashmap, - ty_param_bounds: hashmap}; + ty_param_bounds: hashmap}; type ty_ctxt = ctxt; @@ -268,7 +270,7 @@ tag sty { ty_tup([t]); ty_var(int); // type variable - ty_param(uint, @[param_bound]); // fn/tag type param + ty_param(uint, def_id); // fn/tag type param ty_type; // type_desc* ty_send_type; // type_desc* that has been cloned into exchange heap @@ -308,7 +310,7 @@ tag param_bound { bound_iface(t); } -fn param_bounds_to_kind(bounds: @[param_bound]) -> kind { +fn param_bounds_to_kind(bounds: param_bounds) -> kind { let kind = kind_noncopyable; for bound in *bounds { alt bound { @@ -322,7 +324,7 @@ fn param_bounds_to_kind(bounds: @[param_bound]) -> kind { kind } -type ty_param_bounds_and_ty = @{bounds: [@[param_bound]], ty: t}; +type ty_param_bounds_and_ty = {bounds: @[param_bounds], ty: t}; type type_cache = hashmap; @@ -439,7 +441,7 @@ fn mk_ctxt(s: session::session, dm: resolve::def_map, amap: ast_map::map, map::mk_hashmap(ast_util::hash_ty, ast_util::eq_ty), tag_var_cache: new_def_hash(), iface_method_cache: new_def_hash(), - ty_param_bounds: new_def_hash()}; + ty_param_bounds: map::new_int_hash()}; populate_type_store(cx); ret cx; } @@ -624,7 +626,7 @@ fn mk_res(cx: ctxt, did: ast::def_id, inner: t, tps: [t]) -> t { fn mk_var(cx: ctxt, v: int) -> t { ret gen_ty(cx, ty_var(v)); } -fn mk_param(cx: ctxt, n: uint, k: @[param_bound]) -> t { +fn mk_param(cx: ctxt, n: uint, k: def_id) -> t { ret gen_ty(cx, ty_param(n, k)); } @@ -722,7 +724,7 @@ fn walk_ty(cx: ctxt, walker: ty_walk, ty: t) { tag fold_mode { fm_var(fn@(int) -> t); - fm_param(fn@(uint, @[param_bound]) -> t); + fm_param(fn@(uint, def_id) -> t); fm_general(fn@(t) -> t); } @@ -813,8 +815,14 @@ fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t { ty_var(id) { alt fld { fm_var(folder) { ty = folder(id); } _ {/* no-op */ } } } - ty_param(id, k) { - alt fld { fm_param(folder) { ty = folder(id, k); } _ {/* no-op */ } } + ty_param(id, did) { + alt fld { fm_param(folder) { ty = folder(id, did); } _ {} } + } + ty_constr(subty, cs) { + ty = mk_constr(cx, fold_ty(cx, fld, subty), cs); + } + _ { + cx.sess.fatal("Unsupported sort of type in fold_ty"); } } @@ -1083,7 +1091,9 @@ fn type_kind(cx: ctxt, ty: t) -> kind { } // Resources are always noncopyable. ty_res(did, inner, tps) { kind_noncopyable } - ty_param(_, bounds) { param_bounds_to_kind(bounds) } + ty_param(_, did) { + param_bounds_to_kind(cx.ty_param_bounds.get(did.node)) + } ty_constr(t, _) { type_kind(cx, t) } }; @@ -1131,7 +1141,7 @@ fn type_structurally_contains(cx: ctxt, ty: t, test: fn(sty) -> bool) -> } } -pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool { +pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool unchecked { /* type_structurally_contains can't be declared pure because it takes a function argument. But it should be @@ -1141,15 +1151,9 @@ pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool { actually checkable. It seems to me like a lot of properties that the type context tracks about types should be immutable.) */ - unchecked{ - type_structurally_contains(cx, ty, - fn (sty: sty) -> bool { - ret alt sty { - ty_param(_, _) { true } - _ { false } - }; - }) - } + type_structurally_contains(cx, ty, fn (sty: sty) -> bool { + alt sty { ty_param(_, _) { true } _ { false }} + }) } // Returns true for noncopyable types and types where a copy of a value can be @@ -1464,8 +1468,6 @@ fn constrs_eq(cs: [@constr], ds: [@constr]) -> bool { // Type lookups fn node_id_to_ty_param_substs_opt_and_ty(cx: ctxt, id: ast::node_id) -> ty_param_substs_opt_and_ty { - - // Pull out the node type table. alt smallintmap::find(*cx.node_types, id as uint) { none. { @@ -1737,6 +1739,7 @@ mod unify { export ures_ok; export ures_err; export var_bindings; + export precise, in_bindings; tag result { ures_ok(t); ures_err(type_err); } tag union_result { unres_ok; unres_err(type_err); } @@ -1747,7 +1750,11 @@ mod unify { type var_bindings = {sets: ufind::ufind, types: smallintmap::smallintmap}; - type ctxt = {vb: option::t<@var_bindings>, tcx: ty_ctxt}; + tag unify_style { + precise; + in_bindings(@var_bindings); + } + type ctxt = {st: unify_style, tcx: ty_ctxt}; fn mk_var_bindings() -> @var_bindings { ret @{sets: ufind::make(), types: smallintmap::mk::()}; @@ -1756,7 +1763,9 @@ mod unify { // Unifies two sets. fn union(cx: @ctxt, set_a: uint, set_b: uint, variance: variance) -> union_result { - let vb = option::get(cx.vb); + let vb = alt cx.st { + in_bindings(vb) { vb } + }; ufind::grow(vb.sets, float::max(set_a, set_b) + 1u); let root_a = ufind::find(vb.sets, set_a); let root_b = ufind::find(vb.sets, set_b); @@ -1806,7 +1815,7 @@ mod unify { fn record_var_binding( cx: @ctxt, key: int, typ: t, variance: variance) -> result { - let vb = option::get(cx.vb); + let vb = alt cx.st { in_bindings(vb) { vb } }; ufind::grow(vb.sets, (key as uint) + 1u); let root = ufind::find(vb.sets, key as uint); let result_type = typ; @@ -2142,7 +2151,6 @@ mod unify { // If the RHS is a variable type, then just do the // appropriate binding. ty::ty_var(actual_id) { - assert option::is_some(cx.vb); let actual_n = actual_id as uint; alt struct(cx.tcx, expected) { ty::ty_var(expected_id) { @@ -2167,7 +2175,6 @@ mod unify { } alt struct(cx.tcx, expected) { ty::ty_var(expected_id) { - assert option::is_some(cx.vb); // Add a binding. (`actual` can't actually be a var here.) alt record_var_binding_for_expected( cx, expected_id, actual, @@ -2205,7 +2212,14 @@ mod unify { _ { ret ures_err(terr_mismatch); } } } - ty::ty_param(_, _) { ret struct_cmp(cx, expected, actual); } + ty::ty_param(expected_n, _) { + alt struct(cx.tcx, actual) { + ty::ty_param(actual_n, _) when expected_n == actual_n { + ret ures_ok(expected); + } + _ { ret ures_err(terr_mismatch); } + } + } ty::ty_tag(expected_id, expected_tps) { alt struct(cx.tcx, actual) { ty::ty_tag(actual_id, actual_tps) { @@ -2477,9 +2491,9 @@ mod unify { } } } - fn unify(expected: t, actual: t, vb: option::t<@var_bindings>, + fn unify(expected: t, actual: t, st: unify_style, tcx: ty_ctxt) -> result { - let cx = @{vb: vb, tcx: tcx}; + let cx = @{st: st, tcx: tcx}; ret unify_step(cx, expected, actual, covariant); } fn dump_var_bindings(tcx: ty_ctxt, vb: @var_bindings) { @@ -2552,7 +2566,7 @@ mod unify { } fn same_type(cx: ctxt, a: t, b: t) -> bool { - alt unify::unify(a, b, none, cx) { + alt unify::unify(a, b, unify::precise, cx) { unify::ures_ok(_) { true } _ { false } } @@ -2602,8 +2616,8 @@ fn type_err_to_str(err: ty::type_err) -> str { "' but found one with method '" + a_meth + "'"; } terr_mode_mismatch(e_mode, a_mode) { - ret "expected argument mode " + mode_str_1(e_mode) + " but found " + - mode_str_1(a_mode); + ret "expected argument mode " + mode_str(e_mode) + " but found " + + mode_str(a_mode); } terr_constr_len(e_len, a_len) { ret "Expected a type with " + uint::str(e_len) + @@ -2621,25 +2635,17 @@ fn type_err_to_str(err: ty::type_err) -> str { // Converts type parameters in a type to type variables and returns the // resulting type along with a list of type variable IDs. -fn bind_params_in_type(sp: span, cx: ctxt, next_ty_var: fn@() -> int, typ: t, +fn bind_params_in_type(cx: ctxt, next_ty_var: block() -> int, typ: t, ty_param_count: uint) -> {ids: [int], ty: t} { - let param_var_ids: @mutable [int] = @mutable []; - let i = 0u; - while i < ty_param_count { *param_var_ids += [next_ty_var()]; i += 1u; } - fn binder(sp: span, cx: ctxt, param_var_ids: @mutable [int], - _next_ty_var: fn@() -> int, index: uint, - _bounds: @[param_bound]) -> t { - if index < vec::len(*param_var_ids) { - ret mk_var(cx, param_var_ids[index]); - } else { - cx.sess.span_fatal(sp, "Unbound type parameter in callee's type"); - } + let param_var_ids = [], i = 0u; + while i < ty_param_count { param_var_ids += [next_ty_var()]; i += 1u; } + let param_var_ids = @param_var_ids; + fn binder(cx: ctxt, param_var_ids: @[int], index: uint, + _did: def_id) -> t { + ret mk_var(cx, param_var_ids[index]); } - let new_typ = - fold_ty(cx, - fm_param(bind binder(sp, cx, param_var_ids, next_ty_var, _, - _)), typ); - ret {ids: *param_var_ids, ty: new_typ}; + {ids: *param_var_ids, + ty: fold_ty(cx, fm_param(bind binder(cx, param_var_ids, _, _)), typ)} } @@ -2647,9 +2653,8 @@ fn bind_params_in_type(sp: span, cx: ctxt, next_ty_var: fn@() -> int, typ: t, // substitions. fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t { if !type_contains_params(cx, typ) { ret typ; } - fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint, - _bounds: @[param_bound]) - -> t { + fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint, _did: def_id) + -> t { // FIXME: bounds check can fail ret substs[idx]; } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 5d65ce780499..2becc452ebc0 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -7,7 +7,7 @@ import driver::session; import util::common::*; import syntax::codemap::span; import middle::ty; -import middle::ty::{node_id_to_type, arg, bind_params_in_type, block_ty, +import middle::ty::{node_id_to_type, arg, block_ty, expr_ty, field, node_type_table, mk_nil, ty_param_substs_opt_and_ty, ty_param_bounds_and_ty}; import util::ppaux::ty_to_str; @@ -18,9 +18,24 @@ import std::map::{hashmap, new_int_hash}; import option::{none, some}; import syntax::print::pprust::*; -export check_crate, method_map; +export check_crate, method_map, method_origin, method_static, method_param; +export dict_map, dict_res, dict_origin, dict_static, dict_param; -type method_map = hashmap; +tag method_origin { + method_static(ast::def_id); + // iface id, method num, param num, bound num + method_param(ast::def_id, uint, uint, uint); +} +type method_map = hashmap; + +// Resolutions for bounds of all parameters, left to right, for a given path. +type dict_res = @[dict_origin]; +tag dict_origin { + dict_static(ast::def_id, [ty::t], dict_res); + // Param number, bound number + dict_param(uint, uint); +} +type dict_map = hashmap; type ty_table = hashmap; @@ -33,6 +48,7 @@ tag self_info { type crate_ctxt = {mutable self_infos: [self_info], impl_map: resolve::impl_map, method_map: method_map, + dict_map: dict_map, tcx: ty::ctxt}; type fn_ctxt = @@ -76,22 +92,22 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> ast::def_arg(id, _) { assert (fcx.locals.contains_key(id.node)); let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node)); - ret @{bounds: [], ty: typ}; + ret {bounds: @[], ty: typ}; } ast::def_local(id, _) { assert (fcx.locals.contains_key(id.node)); let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node)); - ret @{bounds: [], ty: typ}; + ret {bounds: @[], ty: typ}; } ast::def_obj_field(id, _) { assert (fcx.locals.contains_key(id.node)); let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node)); - ret @{bounds: [], ty: typ}; + ret {bounds: @[], ty: typ}; } ast::def_self(id) { alt get_self_info(fcx.ccx) { some(self_obj(_, obj_t)) | some(self_impl(obj_t)) { - ret @{bounds: [], ty: obj_t}; + ret {bounds: @[], ty: obj_t}; } } } @@ -102,12 +118,12 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> ast::def_binding(id) { assert (fcx.locals.contains_key(id.node)); let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, id.node)); - ret @{bounds: [], ty: typ}; + ret {bounds: @[], ty: typ}; } ast::def_mod(_) { // Hopefully part of a path. // TODO: return a type that's more poisonous, perhaps? - ret @{bounds: [], ty: ty::mk_nil(fcx.ccx.tcx)}; + ret {bounds: @[], ty: ty::mk_nil(fcx.ccx.tcx)}; } ast::def_ty(_) { fcx.ccx.tcx.sess.span_fatal(sp, "expected value but found type"); @@ -122,16 +138,18 @@ fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> } } +fn bind_params(fcx: @fn_ctxt, tp: ty::t, count: uint) + -> {ids: [int], ty: ty::t} { + ty::bind_params_in_type(fcx.ccx.tcx, {|| next_ty_var_id(fcx)}, tp, count) +} // Instantiates the given path, which must refer to an item with the given // number of type parameters and type. fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path, tpt: ty_param_bounds_and_ty, sp: span) -> ty_param_substs_opt_and_ty { - let ty_param_count = vec::len(tpt.bounds); - let bind_result = - bind_params_in_type(sp, fcx.ccx.tcx, bind next_ty_var_id(fcx), tpt.ty, - ty_param_count); + let ty_param_count = vec::len(*tpt.bounds); + let bind_result = bind_params(fcx, tpt.ty, ty_param_count); let ty_param_vars = bind_result.ids; let ty_substs_opt; let ty_substs_len = vec::len::<@ast::ty>(pth.node.types); @@ -282,13 +300,13 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t { // "foo = int" like OCaml? let ty_param_bounds_and_ty = getter(tcx, mode, id); - if vec::len(ty_param_bounds_and_ty.bounds) == 0u { + if vec::len(*ty_param_bounds_and_ty.bounds) == 0u { ret ty_param_bounds_and_ty.ty; } // The typedef is type-parametric. Do the type substitution. let param_bindings: [ty::t] = []; - if vec::len(args) != vec::len(ty_param_bounds_and_ty.bounds) { + if vec::len(args) != vec::len(*ty_param_bounds_and_ty.bounds) { tcx.sess.span_fatal(sp, "Wrong number of type arguments for a \ polymorphic type"); } @@ -343,7 +361,7 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t { } some(ast::def_native_ty(id)) { typ = getter(tcx, mode, id).ty; } some(ast::def_ty_param(id, n)) { - typ = ty::mk_param(tcx, n, tcx.ty_param_bounds.get(id)); + typ = ty::mk_param(tcx, n, id); } some(_) { tcx.sess.span_fatal(ast_ty.span, @@ -382,7 +400,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item) alt it.node { ast::item_const(t, _) { let typ = ast_ty_to_ty(tcx, mode, t); - let tpt = @{bounds: [], ty: typ}; + let tpt = {bounds: @[], ty: typ}; tcx.tcache.insert(local_def(it.id), tpt); ret tpt; } @@ -401,7 +419,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item) } // Tell ast_ty_to_ty() that we want to perform a recursive // call to resolve any named types. - let tpt = @{bounds: ty_param_bounds(tcx, mode, tps), + let tpt = {bounds: ty_param_bounds(tcx, mode, tps), ty: ty::mk_named(tcx, ast_ty_to_ty(tcx, mode, t), @it.ident)}; tcx.tcache.insert(local_def(it.id), tpt); @@ -413,7 +431,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item) let t = ty::mk_named(tcx, ty::mk_res(tcx, local_def(it.id), t_arg.ty, params), @it.ident); - let t_res = @{bounds: bounds, ty: t}; + let t_res = {bounds: bounds, ty: t}; tcx.tcache.insert(local_def(it.id), t_res); ret t_res; } @@ -422,7 +440,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item) let {bounds, params} = mk_ty_params(tcx, tps); let t = ty::mk_named(tcx, ty::mk_tag(tcx, local_def(it.id), params), @it.ident); - let tpt = @{bounds: bounds, ty: t}; + let tpt = {bounds: bounds, ty: t}; tcx.tcache.insert(local_def(it.id), tpt); ret tpt; } @@ -431,7 +449,7 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item) let t = ty::mk_named(tcx, ty::mk_iface(tcx, local_def(it.id), params), @it.ident); - let tpt = @{bounds: bounds, ty: t}; + let tpt = {bounds: bounds, ty: t}; tcx.tcache.insert(local_def(it.id), tpt); ty::store_iface_methods(tcx, it.id, @vec::map(ms, {|m| ty_of_ty_method(tcx, m_collect, m) @@ -455,7 +473,7 @@ fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item) none. { } } let t = ty::mk_native(tcx, ast_util::local_def(it.id)); - let tpt = @{bounds: [], ty: t}; + let tpt = {bounds: @[], ty: t}; tcx.tcache.insert(local_def(it.id), tpt); ret tpt; } @@ -483,7 +501,7 @@ fn ty_of_fn(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl, -> ty::ty_param_bounds_and_ty { let bounds = ty_param_bounds(tcx, mode, ty_params); let tofd = ty_of_fn_decl(tcx, mode, ast::proto_bare, decl); - let tpt = @{bounds: bounds, ty: ty::mk_fn(tcx, tofd)}; + let tpt = {bounds: bounds, ty: ty::mk_fn(tcx, tofd)}; tcx.tcache.insert(def_id, tpt); ret tpt; } @@ -495,15 +513,15 @@ fn ty_of_native_fn_decl(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl, let output_ty = ast_ty_to_ty(tcx, mode, decl.output); let t_fn = ty::mk_native_fn(tcx, input_tys, output_ty); - let tpt = @{bounds: bounds, ty: t_fn}; + let tpt = {bounds: bounds, ty: t_fn}; tcx.tcache.insert(def_id, tpt); ret tpt; } fn ty_param_bounds(tcx: ty::ctxt, mode: mode, params: [ast::ty_param]) - -> [@[ty::param_bound]] { + -> @[ty::param_bounds] { let result = []; for param in params { - result += [alt tcx.ty_param_bounds.find(local_def(param.id)) { + result += [alt tcx.ty_param_bounds.find(param.id) { some(bs) { bs } none. { let bounds = []; @@ -517,12 +535,12 @@ fn ty_param_bounds(tcx: ty::ctxt, mode: mode, params: [ast::ty_param]) }]; } let boxed = @bounds; - tcx.ty_param_bounds.insert(local_def(param.id), boxed); + tcx.ty_param_bounds.insert(param.id, boxed); boxed } }]; } - result + @result } fn ty_of_method(tcx: ty::ctxt, mode: mode, m: @ast::method) -> ty::method { {ident: m.ident, tps: ty_param_bounds(tcx, mode, m.tps), @@ -539,7 +557,7 @@ fn ty_of_obj(tcx: ty::ctxt, mode: mode, id: ast::ident, ob: ast::_obj, let methods = vec::map(ob.methods, {|m| ty_of_method(tcx, mode, m)}); let t_obj = ty::mk_named(tcx, ty::mk_obj(tcx, ty::sort_methods(methods)), @id); - ret @{bounds: bounds, ty: t_obj}; + ret {bounds: bounds, ty: t_obj}; } fn ty_of_obj_ctor(tcx: ty::ctxt, mode: mode, id: ast::ident, ob: ast::_obj, ctor_id: ast::node_id, ty_params: [ast::ty_param]) @@ -553,7 +571,7 @@ fn ty_of_obj_ctor(tcx: ty::ctxt, mode: mode, id: ast::ident, ob: ast::_obj, let t_fn = ty::mk_fn(tcx, {proto: ast::proto_shared(ast::sugar_normal), inputs: t_inputs, output: t_obj.ty, ret_style: ast::return_val, constraints: []}); - let tpt = @{bounds: ty_param_bounds(tcx, mode, ty_params), ty: t_fn}; + let tpt = {bounds: ty_param_bounds(tcx, mode, ty_params), ty: t_fn}; tcx.tcache.insert(local_def(ctor_id), tpt); ret tpt; } @@ -622,11 +640,11 @@ mod write { } fn mk_ty_params(tcx: ty::ctxt, atps: [ast::ty_param]) - -> {bounds: [@[ty::param_bound]], params: [ty::t]} { + -> {bounds: @[ty::param_bounds], params: [ty::t]} { let i = 0u, bounds = ty_param_bounds(tcx, m_collect, atps); {bounds: bounds, - params: vec::map(atps, {|_atp| - let t = ty::mk_param(tcx, i, bounds[i]); + params: vec::map(atps, {|atp| + let t = ty::mk_param(tcx, i, local_def(atp.id)); i += 1u; t })} @@ -674,7 +692,7 @@ mod collect { inputs: args, output: tag_ty, ret_style: ast::return_val, constraints: []}) }; - let tpt = @{bounds: ty_param_bounds(cx.tcx, m_collect, ty_params), + let tpt = {bounds: ty_param_bounds(cx.tcx, m_collect, ty_params), ty: result_ty}; cx.tcx.tcache.insert(local_def(variant.node.id), tpt); write::ty_only(cx.tcx, variant.node.id, result_ty); @@ -690,14 +708,15 @@ mod collect { get_tag_variant_types(cx, tpt.ty, variants, ty_params); } ast::item_impl(tps, _, selfty, ms) { - ty_param_bounds(cx.tcx, m_collect, tps); + let i_bounds = ty_param_bounds(cx.tcx, m_collect, tps); for m in ms { let bounds = ty_param_bounds(cx.tcx, m_collect, m.tps); let ty = ty::mk_fn(cx.tcx, ty_of_fn_decl(cx.tcx, m_collect, ast::proto_bare, m.decl)); - cx.tcx.tcache.insert(local_def(m.id), @{bounds: bounds, - ty: ty}); + cx.tcx.tcache.insert(local_def(m.id), + {bounds: @(*i_bounds + *bounds), + ty: ty}); write::ty_only(cx.tcx, m.id, ty); } write::ty_only(cx.tcx, it.id, ast_ty_to_ty(cx.tcx, m_collect, @@ -754,8 +773,7 @@ mod collect { write::ty_only(cx.tcx, it.id, t_res); write::ty_only(cx.tcx, ctor_id, t_ctor); cx.tcx.tcache.insert(local_def(ctor_id), - @{bounds: bounds, - ty: t_ctor}); + {bounds: bounds, ty: t_ctor}); write::ty_only(cx.tcx, dtor_id, t_dtor); } _ { @@ -798,7 +816,8 @@ mod collect { mod unify { fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> ty::unify::result { - ret ty::unify::unify(expected, actual, some(fcx.var_bindings), + ret ty::unify::unify(expected, actual, + ty::unify::in_bindings(fcx.var_bindings), fcx.ccx.tcx); } } @@ -1115,7 +1134,8 @@ fn gather_locals(ccx: @crate_ctxt, alt ty_opt { none. {/* nothing to do */ } some(typ) { - ty::unify::unify(ty::mk_var(tcx, var_id), typ, some(vb), tcx); + ty::unify::unify(ty::mk_var(tcx, var_id), typ, + ty::unify::in_bindings(vb), tcx); } } }; @@ -1465,39 +1485,92 @@ fn check_expr_with(fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool { ret check_expr_with_unifier(fcx, expr, demand::simple, expected); } +fn impl_self_ty(tcx: ty::ctxt, did: ast::def_id) -> {n_tps: uint, ty: ty::t} { + if did.crate == ast::local_crate { + alt tcx.items.get(did.node) { + ast_map::node_item(@{node: ast::item_impl(ts, _, st, _), + _}) { + {n_tps: vec::len(ts), ty: ast_ty_to_ty(tcx, m_check, st)} + } + } + } else { + let tpt = csearch::get_type(tcx, did); + {n_tps: vec::len(*tpt.bounds), ty: tpt.ty} + } +} + fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes, name: ast::ident, ty: ty::t, sp: span) - -> option::t<{method: @resolve::method_info, ids: [int]}> { + -> option::t<{method_ty: ty::t, n_tps: uint, substs: [ty::t], + origin: method_origin}> { + let tcx = fcx.ccx.tcx; + + // First, see whether this is an interface-bounded parameter + alt ty::struct(tcx, ty) { + ty::ty_param(n, did) { + let bound_n = 0u; + for bound in *tcx.ty_param_bounds.get(did.node) { + alt bound { + ty::bound_iface(t) { + let (iid, tps) = alt ty::struct(tcx, t) { + ty::ty_iface(i, tps) { (i, tps) } + _ { cont; } + }; + let ifce_methods = ty::iface_methods(tcx, iid); + alt vec::position_pred(*ifce_methods, {|m| m.ident == name}) { + some(pos) { + let m = ifce_methods[pos]; + ret some({method_ty: ty::mk_fn(tcx, m.fty), + n_tps: vec::len(*m.tps), + substs: tps, + origin: method_param(iid, pos, n, bound_n)}); + } + _ {} + } + bound_n += 1u; + } + _ {} + } + } + ret none; + } + _ {} + } + + fn ty_from_did(tcx: ty::ctxt, did: ast::def_id) -> ty::t { + if did.crate == ast::local_crate { + alt tcx.items.get(did.node) { + ast_map::node_method(m) { + let mt = ty_of_method(tcx, m_check, m); + ty::mk_fn(tcx, mt.fty) + } + } + } else { csearch::get_type(tcx, did).ty } + } + let result = none; std::list::iter(isc) {|impls| if option::is_some(result) { ret; } for @{did, methods, _} in *impls { alt vec::find(methods, {|m| m.ident == name}) { some(m) { - let (n_tps, self_ty) = if did.crate == ast::local_crate { - alt fcx.ccx.tcx.items.get(did.node) { - ast_map::node_item(@{node: ast::item_impl(ts, _, st, _), - _}) { - (vec::len(ts), ast_ty_to_ty_crate(fcx.ccx, st)) - } - } - } else { - let tpt = csearch::get_type(fcx.ccx.tcx, did); - (vec::len(tpt.bounds), tpt.ty) - }; + let {n_tps, ty: self_ty} = impl_self_ty(tcx, did); let {ids, ty: self_ty} = if n_tps > 0u { - bind_params_in_type(ast_util::dummy_sp(), fcx.ccx.tcx, - bind next_ty_var_id(fcx), self_ty, - n_tps) + bind_params(fcx, self_ty, n_tps) } else { {ids: [], ty: self_ty} }; alt unify::unify(fcx, ty, self_ty) { ures_ok(_) { if option::is_some(result) { // FIXME[impl] score specificity to resolve ambiguity? - fcx.ccx.tcx.sess.span_err( + tcx.sess.span_err( sp, "multiple applicable methods in scope"); } else { - result = some({method: m, ids: ids}); + result = some({ + method_ty: ty_from_did(tcx, m.did), + n_tps: m.n_tps, + substs: vec::map(ids, {|id| ty::mk_var(tcx, id)}), + origin: method_static(m.did) + }); } } _ {} @@ -1680,7 +1753,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, let element_ty = demand::simple(fcx, local.span, element_ty, ty::mk_var(fcx.ccx.tcx, locid)); let bot = check_decl_local(fcx, local); - check_block(fcx, body); + check_block_no_value(fcx, body); // Unify type of decl with element type of the seq demand::simple(fcx, local.span, ty::node_id_to_type(fcx.ccx.tcx, local.node.id), @@ -1695,22 +1768,27 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, fn check_then_else(fcx: @fn_ctxt, thn: ast::blk, elsopt: option::t<@ast::expr>, id: ast::node_id, _sp: span) -> bool { - let then_bot = check_block(fcx, thn); - let els_bot = false; - let if_t = + let (if_t, if_bot) = alt elsopt { some(els) { + let thn_bot = check_block(fcx, thn); let thn_t = block_ty(fcx.ccx.tcx, thn); - els_bot = check_expr_with(fcx, els, thn_t); - let elsopt_t = expr_ty(fcx.ccx.tcx, els); - if !ty::type_is_bot(fcx.ccx.tcx, elsopt_t) { - elsopt_t - } else { thn_t } + let els_bot = check_expr_with(fcx, els, thn_t); + let els_t = expr_ty(fcx.ccx.tcx, els); + let if_t = if !ty::type_is_bot(fcx.ccx.tcx, els_t) { + els_t + } else { + thn_t + }; + (if_t, thn_bot & els_bot) + } + none. { + check_block_no_value(fcx, thn); + (ty::mk_nil(fcx.ccx.tcx), false) } - none. { ty::mk_nil(fcx.ccx.tcx) } }; write::ty_only_fixup(fcx, id, if_t); - ret then_bot & els_bot; + ret if_bot; } // Checks the compatibility @@ -1932,12 +2010,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, } ast::expr_while(cond, body) { bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)); - check_block(fcx, body); + check_block_no_value(fcx, body); write::ty_only_fixup(fcx, id, ty::mk_nil(tcx)); } ast::expr_do_while(body, cond) { bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) | - check_block(fcx, body); + check_block_no_value(fcx, body); write::ty_only_fixup(fcx, id, block_ty(tcx, body)); } ast::expr_alt(expr, arms) { @@ -2153,7 +2231,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, } ast::expr_field(base, field, tys) { bot |= check_expr(fcx, base); - let expr_t = expr_ty(tcx, base); + let expr_t = structurally_resolved_type(fcx, expr.span, + expr_ty(tcx, base)); let base_t = do_autoderef(fcx, expr.span, expr_t); let handled = false, n_tys = vec::len(tys); alt structure_of(fcx, expr.span, base_t) { @@ -2191,51 +2270,35 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, if !handled { let iscope = fcx.ccx.impl_map.get(expr.id); alt lookup_method(fcx, iscope, field, expr_t, expr.span) { - some({method, ids}) { - let fty = if method.did.crate == ast::local_crate { - alt tcx.items.get(method.did.node) { - ast_map::node_method(m) { - let mt = ty_of_method(tcx, m_check, m); - ty::mk_fn(tcx, mt.fty) - } - } - } else { csearch::get_type(tcx, method.did).ty }; - let tvars = vec::map(ids, {|id| ty::mk_var(tcx, id)}); - let n_tps = vec::len(ids); - if method.n_tps + n_tps > 0u { - let b = bind_params_in_type(expr.span, tcx, - bind next_ty_var_id(fcx), fty, - n_tps + method.n_tps); - let _tvars = vec::map(b.ids, {|id| ty::mk_var(tcx, id)}); - let i = 0; - for v in tvars { - demand::simple(fcx, expr.span, v, _tvars[i]); - i += 1; - } - tvars = _tvars; - fty = b.ty; + some({method_ty: fty, n_tps: method_n_tps, substs, origin}) { + let substs = substs, n_tps = vec::len(substs); + if method_n_tps + n_tps > 0u { if n_tys > 0u { - if n_tys != method.n_tps { + if n_tys != method_n_tps { tcx.sess.span_fatal (expr.span, "incorrect number of type \ parameters given for this method"); } - let i = 0u; for ty in tys { - let tvar = tvars[i + n_tps]; - let t_subst = ast_ty_to_ty_crate(fcx.ccx, ty); - demand::simple(fcx, expr.span, tvar, t_subst); + substs += [ast_ty_to_ty_crate(fcx.ccx, ty)]; + } + } else { + let i = 0u; + while i < method_n_tps { + substs += [ty::mk_var(tcx, next_ty_var_id(fcx))]; i += 1u; } } + write::ty_fixup(fcx, id, {substs: some(substs), ty: fty}); } else if n_tys > 0u { tcx.sess.span_fatal(expr.span, "this method does not take type \ parameters"); + } else { + write::ty_only_fixup(fcx, id, fty); } - write::ty_fixup(fcx, id, {substs: some(tvars), ty: fty}); - fcx.ccx.method_map.insert(id, method.did); + fcx.ccx.method_map.insert(id, origin); } none. { let t_err = resolve_type_vars_if_possible(fcx, expr_t); @@ -2437,6 +2500,16 @@ fn check_stmt(fcx: @fn_ctxt, stmt: @ast::stmt) -> bool { ret bot; } +fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool { + let bot = check_block(fcx, blk); + if !bot { + let blkty = ty::node_id_to_monotype(fcx.ccx.tcx, blk.node.id); + let nilty = ty::mk_nil(fcx.ccx.tcx); + demand::simple(fcx, blk.span, nilty, blkty); + } + ret bot; +} + fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool { let fcx = alt blk.node.rules { ast::unchecked_blk. { @{purity: ast::impure_fn with *fcx0} } @@ -2655,6 +2728,7 @@ fn check_fn(ccx: @crate_ctxt, // If we have an enclosing function scope, our type variables will be // resolved when the enclosing scope finishes up. if option::is_none(old_fcx) { + dict::resolve_in_block(fcx, body); writeback::resolve_type_vars_in_block(fcx, body); } } @@ -2663,16 +2737,40 @@ fn check_method(ccx: @crate_ctxt, method: @ast::method) { check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id, none); } +fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, + impl_tps: uint, if_m: ty::method, substs: [ty::t]) { + if impl_m.tps != if_m.tps { + tcx.sess.span_err(sp, "method `" + if_m.ident + + "` has an incompatible set of type parameters"); + } else { + let impl_fty = ty::mk_fn(tcx, impl_m.fty); + // Add dummy substs for the parameters of the impl method + let substs = substs + vec::init_fn({|i| + ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0}) + }, vec::len(*if_m.tps)); + let if_fty = ty::substitute_type_params(tcx, substs, + ty::mk_fn(tcx, if_m.fty)); + alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) { + ty::unify::ures_err(err) { + tcx.sess.span_err(sp, "method `" + if_m.ident + + "` has an incompatible type: " + + ty::type_err_to_str(err)); + } + _ {} + } + } +} + fn check_item(ccx: @crate_ctxt, it: @ast::item) { alt it.node { ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); } - ast::item_fn(decl, _, body) { + ast::item_fn(decl, tps, body) { check_fn(ccx, ast::proto_bare, decl, body, it.id, none); } - ast::item_res(decl, _, body, dtor_id, _) { + ast::item_res(decl, tps, body, dtor_id, _) { check_fn(ccx, ast::proto_bare, decl, body, dtor_id, none); } - ast::item_obj(ob, _, _) { + ast::item_obj(ob, tps, _) { // We're entering an object, so gather up the info we need. ccx.self_infos += [self_obj(ob.fields, ccx.tcx.tcache.get(local_def(it.id)).ty)]; @@ -2681,7 +2779,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { // Now remove the info from the stack. vec::pop(ccx.self_infos); } - ast::item_impl(_, ifce, ty, ms) { + ast::item_impl(tps, ifce, ty, ms) { ccx.self_infos += [self_impl(ast_ty_to_ty(ccx.tcx, m_check, ty))]; let my_methods = vec::map(ms, {|m| check_method(ccx, m); @@ -2690,16 +2788,14 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { vec::pop(ccx.self_infos); alt ifce { some(ty) { - alt ty::struct(ccx.tcx, ast_ty_to_ty(ccx.tcx, m_check, ty)) { + let iface_ty = ast_ty_to_ty(ccx.tcx, m_check, ty); + alt ty::struct(ccx.tcx, iface_ty) { ty::ty_iface(did, tys) { for if_m in *ty::iface_methods(ccx.tcx, did) { alt vec::find(my_methods, {|m| if_m.ident == m.ident}) { some(m) { - if !ty::same_method(ccx.tcx, m, if_m) { - ccx.tcx.sess.span_err( - ty.span, "method `" + if_m.ident + - "` has the wrong type"); - } + compare_impl_method(ccx.tcx, ty.span, m, + vec::len(tps), if_m, tys); } none. { ccx.tcx.sess.span_err(ty.span, "missing method `" + @@ -2707,6 +2803,9 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { } } } + let tpt = {bounds: ty_param_bounds(ccx.tcx, m_check, tps), + ty: iface_ty}; + ccx.tcx.tcache.insert(local_def(it.id), tpt); } _ { ccx.tcx.sess.span_err(ty.span, "can only implement interface \ @@ -2721,6 +2820,29 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) { } } +fn check_ty_params(ccx: @crate_ctxt, tps: [ast::ty_param]) { + for tp in tps { + let i = 0u; + for bound in *tp.bounds { + alt bound { + ast::bound_iface(at) { + let tbound = ccx.tcx.ty_param_bounds.get(tp.id)[i]; + let bound_ty = alt tbound { ty::bound_iface(t) { t } }; + alt ty::struct(ccx.tcx, bound_ty) { + ty::ty_iface(_, _) {} + _ { + ccx.tcx.sess.span_err(at.span, "type parameter bounds \ + must be interface types"); + } + } + } + _ {} + } + i += 1u; + } + } +} + fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool { alt ty::struct(tcx, a.ty) { ty::ty_vec(mt) { @@ -2769,21 +2891,209 @@ fn check_for_main_fn(tcx: ty::ctxt, crate: @ast::crate) { } } +mod dict { + fn has_iface_bounds(tps: [ty::param_bounds]) -> bool { + vec::any(tps, {|bs| + vec::any(*bs, {|b| + alt b { ty::bound_iface(_) { true } _ { false } } + }) + }) + } + + fn lookup_dicts(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, + bounds: @[ty::param_bounds], tys: [ty::t]) + -> dict_res { + let tcx = fcx.ccx.tcx, result = [], i = 0u; + for ty in tys { + for bound in *bounds[i] { + alt bound { + ty::bound_iface(i_ty) { + let i_ty = ty::substitute_type_params(tcx, tys, i_ty); + result += [lookup_dict(fcx, isc, sp, ty, i_ty)]; + } + _ {} + } + } + i += 1u; + } + @result + } + + fn lookup_dict(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, + ty: ty::t, iface_ty: ty::t) -> dict_origin { + let tcx = fcx.ccx.tcx; + let (iface_id, iface_tps) = alt ty::struct(tcx, iface_ty) { + ty::ty_iface(did, tps) { (did, tps) } + _ { tcx.sess.abort_if_errors(); fail; } + }; + let ty = fixup_ty(fcx, sp, ty); + alt ty::struct(tcx, ty) { + ty::ty_param(n, did) { + let n_bound = 0u; + for bound in *tcx.ty_param_bounds.get(did.node) { + alt bound { + ty::bound_iface(ity) { + alt ty::struct(tcx, ity) { + ty::ty_iface(idid, _) { + if iface_id == idid { ret dict_param(n, n_bound); } + } + } + n_bound += 1u; + } + _ {} + } + } + } + _ { + let found = none; + std::list::iter(isc) {|impls| + if option::is_some(found) { ret; } + for im in *impls { + if im.iface_did == some(iface_id) { + let {n_tps, ty: self_ty} = impl_self_ty(tcx, im.did); + let {ids, ty: self_ty} = if n_tps > 0u { + bind_params(fcx, self_ty, n_tps) + } else { {ids: [], ty: self_ty} }; + let vars = vec::map(ids, {|id| ty::mk_var(tcx, id)}); + let im_bs = ty::lookup_item_type(tcx, im.did).bounds; + // FIXME[impl] don't do this in fcx (or make + // unify transactional by scrubbing bindings on fail) + alt unify::unify(fcx, ty, self_ty) { + ures_ok(_) { + if option::is_some(found) { + tcx.sess.span_err( + sp, "multiple applicable implementations \ + in scope"); + } else { + connect_iface_tps(fcx, sp, vars, iface_tps, + im.did); + let params = vec::map(vars, {|t| + fixup_ty(fcx, sp, t)}); + let subres = lookup_dicts(fcx, isc, sp, im_bs, + params); + found = some(dict_static(im.did, params, + subres)); + } + } + _ {} + } + } + } + } + alt found { + some(rslt) { ret rslt; } + _ {} + } + } + } + + tcx.sess.span_fatal( + sp, "failed to find an implementation of interface " + + ty_to_str(tcx, iface_ty) + " for " + + ty_to_str(tcx, ty)); + } + + fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t { + let tcx = fcx.ccx.tcx; + alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) { + fix_ok(new_type) { new_type } + fix_err(vid) { + tcx.sess.span_fatal(sp, "could not determine a type for a \ + bounded type parameter"); + } + } + } + + fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t], + iface_tys: [ty::t], impl_did: ast::def_id) { + let tcx = fcx.ccx.tcx; + // FIXME[impl] + assert impl_did.crate == ast::local_crate; + let ity = alt tcx.items.get(impl_did.node) { + ast_map::node_item(@{node: ast::item_impl(_, some(ity), _, _), _}) { + ast_ty_to_ty(tcx, m_check, ity) + } + }; + let iface_ty = ty::substitute_type_params(tcx, impl_tys, ity); + alt ty::struct(tcx, iface_ty) { + ty::ty_iface(_, tps) { + vec::iter2(tps, iface_tys, + {|a, b| demand::simple(fcx, sp, a, b);}); + } + } + } + + fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) { + let cx = fcx.ccx; + alt ex.node { + ast::expr_path(_) { + let substs = ty::node_id_to_ty_param_substs_opt_and_ty( + cx.tcx, ex.id); + alt substs.substs { + some(ts) { + let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)); + let item_ty = ty::lookup_item_type(cx.tcx, did); + if has_iface_bounds(*item_ty.bounds) { + let impls = cx.impl_map.get(ex.id); + cx.dict_map.insert(ex.id, lookup_dicts( + fcx, impls, ex.span, item_ty.bounds, ts)); + } + } + _ {} + } + } + // Must resolve bounds on methods with bounded params + ast::expr_field(_, _, _) { + alt cx.method_map.find(ex.id) { + some(method_static(did)) { + let bounds = ty::lookup_item_type(cx.tcx, did).bounds; + if has_iface_bounds(*bounds) { + let ts = ty::node_id_to_type_params(cx.tcx, ex.id); + let iscs = cx.impl_map.get(ex.id); + cx.dict_map.insert(ex.id, lookup_dicts( + fcx, iscs, ex.span, bounds, ts)); + } + } + _ {} + } + } + ast::expr_fn(ast::proto_block., _, _, _) {} + ast::expr_fn(_, _, _, _) { ret; } + _ {} + } + visit::visit_expr(ex, fcx, v); + } + + // Detect points where an interface-bounded type parameter is + // instantiated, resolve the impls for the parameters. + fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) { + visit::visit_block(bl, fcx, visit::mk_vt(@{ + visit_expr: resolve_expr, + visit_item: fn(_i: @ast::item, &&_e: @fn_ctxt, + _v: visit::vt<@fn_ctxt>) {} + with *visit::default_visitor() + })); + } +} + fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map, - crate: @ast::crate) -> method_map { + crate: @ast::crate) -> (method_map, dict_map) { collect::collect_item_types(tcx, crate); let ccx = @{mutable self_infos: [], impl_map: impl_map, method_map: std::map::new_int_hash(), + dict_map: std::map::new_int_hash(), tcx: tcx}; - let visit = - visit::mk_simple_visitor(@{visit_item: bind check_item(ccx, _) - with *visit::default_simple_visitor()}); + let visit = visit::mk_simple_visitor(@{ + visit_item: bind check_item(ccx, _), + visit_ty_params: bind check_ty_params(ccx, _) + with *visit::default_simple_visitor() + }); visit::visit_crate(*crate, (), visit); check_for_main_fn(tcx, crate); tcx.sess.abort_if_errors(); - ccx.method_map + (ccx.method_map, ccx.dict_map) } // // Local Variables: diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 883d8b6f046f..2738f3e4b82c 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -22,6 +22,7 @@ mod middle { mod trans_uniq; mod trans_closure; mod trans_vec; + mod trans_impl; mod ty; mod ast_map; mod resolve; diff --git a/src/comp/syntax/parse/eval.rs b/src/comp/syntax/parse/eval.rs index 1b1713aff33d..b13f0cff92ac 100644 --- a/src/comp/syntax/parse/eval.rs +++ b/src/comp/syntax/parse/eval.rs @@ -56,10 +56,10 @@ fn parse_companion_mod(cx: ctx, prefix: str, suffix: option::t) -> ([@ast::view_item], [@ast::item], [ast::attribute]) { fn companion_file(prefix: str, suffix: option::t) -> str { - alt suffix { + ret alt suffix { option::some(s) { fs::connect(prefix, s) } option::none. { prefix } - } + ".rs" + } + ".rs"; } fn file_exists(path: str) -> bool { diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index dd6497792358..a1e4ddbe5023 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -10,7 +10,12 @@ import util::interner; import ast::{node_id, spanned}; import front::attr; -tag restriction { UNRESTRICTED; RESTRICT_NO_CALL_EXPRS; RESTRICT_NO_BAR_OP; } +tag restriction { + UNRESTRICTED; + RESTRICT_STMT_EXPR; + RESTRICT_NO_CALL_EXPRS; + RESTRICT_NO_BAR_OP; +} tag file_type { CRATE_FILE; SOURCE_FILE; } @@ -977,28 +982,45 @@ fn parse_syntax_ext_naked(p: parser, lo: uint) -> @ast::expr { fn parse_dot_or_call_expr(p: parser) -> @ast::expr { let b = parse_bottom_expr(p); - if expr_has_value(b) { parse_dot_or_call_expr_with(p, b) } - else { b } + parse_dot_or_call_expr_with(p, b) +} + +fn permits_call(p: parser) -> bool { + ret p.get_restriction() != RESTRICT_NO_CALL_EXPRS; } fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr { let lo = e.span.lo; let hi = e.span.hi; let e = e; - while true { + while !expr_is_complete(p, e) { alt p.peek() { - token::LPAREN. { - if p.get_restriction() == RESTRICT_NO_CALL_EXPRS { - ret e; - } else { - // Call expr. - let es = parse_seq(token::LPAREN, token::RPAREN, - seq_sep(token::COMMA), parse_expr, p); - hi = es.span.hi; - let nd = ast::expr_call(e, es.node, false); - e = mk_expr(p, lo, hi, nd); + // expr(...) + token::LPAREN. when permits_call(p) { + let es = parse_seq(token::LPAREN, token::RPAREN, + seq_sep(token::COMMA), parse_expr, p); + hi = es.span.hi; + let nd = ast::expr_call(e, es.node, false); + e = mk_expr(p, lo, hi, nd); + } + + // expr { || ... } + token::LBRACE. when is_bar(p.look_ahead(1u)) && permits_call(p) { + p.bump(); + let blk = parse_fn_block_expr(p); + alt e.node { + ast::expr_call(f, args, false) { + e = @{node: ast::expr_call(f, args + [blk], true) + with *e}; + } + _ { + e = mk_expr(p, lo, p.get_last_hi_pos(), + ast::expr_call(e, [blk], true)); + } } } + + // expr[...] token::LBRACKET. { p.bump(); let ix = parse_expr(p); @@ -1006,6 +1028,8 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr { expect(p, token::RBRACKET); e = mk_expr(p, lo, hi, ast::expr_index(e, ix)); } + + // expr.f token::DOT. { p.bump(); alt p.peek() { @@ -1022,6 +1046,7 @@ fn parse_dot_or_call_expr_with(p: parser, e: @ast::expr) -> @ast::expr { t { unexpected(p, t); } } } + _ { ret e; } } } @@ -1126,7 +1151,7 @@ const ternary_prec: int = 0; fn parse_more_binops(p: parser, lhs: @ast::expr, min_prec: int) -> @ast::expr { - if !expr_has_value(lhs) { ret lhs; } + if expr_is_complete(p, lhs) { ret lhs; } let peeked = p.peek(); if peeked == token::BINOP(token::OR) && p.get_restriction() == RESTRICT_NO_BAR_OP { ret lhs; } @@ -1207,11 +1232,6 @@ fn parse_if_expr_1(p: parser) -> let elexpr = parse_else_expr(p); els = some(elexpr); hi = elexpr.span.hi; - } else if !option::is_none(thn.node.expr) { - let sp = option::get(thn.node.expr).span; - p.span_fatal(sp, "`if` without `else` can not produce a result"); - //TODO: If a suggestion mechanism appears, suggest that the - //user may have forgotten a ';' } ret {cond: cond, then: thn, els: els, lo: lo, hi: hi}; } @@ -1550,84 +1570,52 @@ fn parse_stmt(p: parser) -> @ast::stmt { } } - let maybe_item = parse_item(p, item_attrs); - - // If we have attributes then we should have an item - if vec::len(item_attrs) > 0u { - alt maybe_item { - some(_) {/* fallthrough */ } - _ { ret p.fatal("expected item"); } - } - } - - alt maybe_item { + alt parse_item(p, item_attrs) { some(i) { let hi = i.span.hi; let decl = @spanned(lo, hi, ast::decl_item(i)); ret @spanned(lo, hi, ast::stmt_decl(decl, p.get_id())); } - none. { - // Remainder are line-expr stmts. - let e = parse_expr(p); - // See if it is a block call - if expr_has_value(e) && p.peek() == token::LBRACE && - is_bar(p.look_ahead(1u)) { - p.bump(); - let blk = parse_fn_block_expr(p); - alt e.node { - ast::expr_call(f, args, false) { - e = @{node: ast::expr_call(f, args + [blk], true) - with *e}; - } - _ { - e = mk_expr(p, lo, p.get_last_hi_pos(), - ast::expr_call(e, [blk], true)); - } - } - } - ret @spanned(lo, e.span.hi, ast::stmt_expr(e, p.get_id())); - } - _ { p.fatal("expected statement"); } + none() { /* fallthrough */ } } + + // If we have attributes then we should have an item + if vec::len(item_attrs) > 0u { + ret p.fatal("expected item"); + } + + // Remainder are line-expr stmts. + let e = parse_expr_res(p, RESTRICT_STMT_EXPR); + ret @spanned(lo, e.span.hi, ast::stmt_expr(e, p.get_id())); } } -fn expr_has_value(e: @ast::expr) -> bool { +fn expr_is_complete(p: parser, e: @ast::expr) -> bool { + log(debug, ("expr_is_complete", p.get_restriction(), + print::pprust::expr_to_str(e), + expr_requires_semi_to_be_stmt(e))); + ret p.get_restriction() == RESTRICT_STMT_EXPR && + !expr_requires_semi_to_be_stmt(e); +} + +fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool { alt e.node { - ast::expr_if(_, th, els) | ast::expr_if_check(_, th, els) { - if option::is_none(els) { false } - else { !option::is_none(th.node.expr) || - expr_has_value(option::get(els)) } + ast::expr_if(_, _, _) | ast::expr_if_check(_, _, _) + | ast::expr_alt(_, _) | ast::expr_block(_) + | ast::expr_do_while(_, _) | ast::expr_while(_, _) + | ast::expr_for(_, _, _) + | ast::expr_call(_, _, true) { + false } - ast::expr_alt(_, arms) { - let found_expr = false; - for arm in arms { - if !option::is_none(arm.body.node.expr) { found_expr = true; } - } - found_expr - } - ast::expr_block(blk) | ast::expr_while(_, blk) | - ast::expr_for(_, _, blk) | ast::expr_do_while(blk, _) { - !option::is_none(blk.node.expr) - } - ast::expr_call(_, _, true) { false } _ { true } } } -fn stmt_is_expr(stmt: @ast::stmt) -> bool { - ret alt stmt.node { - ast::stmt_expr(e, _) { expr_has_value(e) } - _ { false } - }; -} - fn stmt_to_expr(stmt: @ast::stmt) -> option::t<@ast::expr> { - ret if stmt_is_expr(stmt) { - alt stmt.node { - ast::stmt_expr(e, _) { some(e) } - } - } else { none }; + alt stmt.node { + ast::stmt_expr(e, _) { some(e) } + _ { none } + } } fn stmt_ends_with_semi(stmt: ast::stmt) -> bool { @@ -1639,7 +1627,7 @@ fn stmt_ends_with_semi(stmt: ast::stmt) -> bool { } } ast::stmt_expr(e, _) { - ret expr_has_value(e); + ret expr_requires_semi_to_be_stmt(e); } } } @@ -1659,14 +1647,10 @@ fn parse_block(p: parser) -> ast::blk { } fn parse_block_no_value(p: parser) -> ast::blk { - let blk = parse_block(p); - if !option::is_none(blk.node.expr) { - let sp = option::get(blk.node.expr).span; - p.span_fatal(sp, "this block must not have a result"); - //TODO: If a suggestion mechanism appears, suggest that the - //user may have forgotten a ';' - } - ret blk; + // We parse blocks that cannot have a value the same as any other block; + // the type checker will make sure that the tail expression (if any) has + // unit type. + ret parse_block(p); } // Precondition: already parsed the '{' or '#{' @@ -1850,34 +1834,29 @@ fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item { ast::item_iface(tps, meths), attrs); } +// Parses three variants (with the initial params always optional): +// impl of to_str for [T] { ... } +// impl name of to_str for [T] { ... } +// impl name for [T] { ... } fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item { - let lo = p.get_last_lo_pos(), ident, tps, ifce; + let lo = p.get_last_lo_pos(); fn wrap_path(p: parser, pt: @ast::path) -> @ast::ty { @{node: ast::ty_path(pt, p.get_id()), span: pt.span} } - if eat_word(p, "of") { + let (ident, tps) = if !is_word(p, "of") { + if p.peek() == token::LT { (none, parse_ty_params(p)) } + else { (some(parse_ident(p)), parse_ty_params(p)) } + } else { (none, []) }; + let ifce = if eat_word(p, "of") { let path = parse_path_and_ty_param_substs(p, false); - tps = vec::map(path.node.types, {|tp| - alt tp.node { - ast::ty_path(pt, _) { - if vec::len(pt.node.idents) == 1u && - vec::len(pt.node.types) == 0u { - ret {ident: pt.node.idents[0], id: p.get_id(), - bounds: @[]}; - } - } - _ {} - } - p.fatal("only single-word, parameter-less types allowed here"); - }); - ident = path.node.idents[vec::len(path.node.idents)-1u]; - ifce = some(wrap_path(p, path)); - } else { - ident = parse_ident(p); - tps = parse_ty_params(p); - ifce = if eat_word(p, "of") { - some(wrap_path(p, parse_path_and_ty_param_substs(p, false))) - } else { none }; + if option::is_none(ident) { + ident = some(path.node.idents[vec::len(path.node.idents) - 1u]); + } + some(wrap_path(p, path)) + } else { none }; + let ident = alt ident { + some(name) { name } + none. { expect_word(p, "of"); fail; } }; expect_word(p, "for"); let ty = parse_ty(p, false), meths = []; diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index 72e0879e4bf3..da592b6d3c10 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -558,11 +558,39 @@ fn print_attribute(s: ps, attr: ast::attribute) { word(s.s, "]"); } +// An expression that begins with a dual-form statement/expression like `{ +// ... }-10` would be parsed as `{ ... };-10` unless parentheses are used (ie, +// `({...}-10)`). These parentheses are not, however, preserved by the +// parser. This function specifies whether parentheses must be inserted. +fn stmt_expr_requires_parens(ex: @ast::expr) -> bool { + fn helper(ex: @ast::expr, inner: bool) -> bool { + alt ex.node { + ast::expr_call(subex, _, _) | ast::expr_binary(_, subex, _) { + be helper(subex, true); + } + _ when !inner { ret false; } + _ { ret !parse::parser::expr_requires_semi_to_be_stmt(ex); } + } + } + ret helper(ex, false); +} + fn print_stmt(s: ps, st: ast::stmt) { maybe_print_comment(s, st.span.lo); alt st.node { - ast::stmt_decl(decl, _) { print_decl(s, decl); } - ast::stmt_expr(expr, _) { space_if_not_bol(s); print_expr(s, expr); } + ast::stmt_decl(decl, _) { + print_decl(s, decl); + } + ast::stmt_expr(expr, _) { + space_if_not_bol(s); + if stmt_expr_requires_parens(expr) { + popen(s); + print_expr(s, expr); + pclose(s); + } else { + print_expr(s, expr); + } + } } if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); } maybe_print_trailing_comment(s, st.span, none::); diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs index e27671603029..1e5b88e1aa65 100644 --- a/src/comp/syntax/visit.rs +++ b/src/comp/syntax/visit.rs @@ -52,6 +52,7 @@ type visitor = visit_decl: fn@(@decl, E, vt), visit_expr: fn@(@expr, E, vt), visit_ty: fn@(@ty, E, vt), + visit_ty_params: fn@([ty_param], E, vt), visit_constr: fn@(@path, span, node_id, E, vt), visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt)}; @@ -68,6 +69,7 @@ fn default_visitor() -> visitor { visit_decl: bind visit_decl::(_, _, _), visit_expr: bind visit_expr::(_, _, _), visit_ty: bind skip_ty::(_, _, _), + visit_ty_params: bind visit_ty_params::(_, _, _), visit_constr: bind visit_constr::(_, _, _, _, _), visit_fn: bind visit_fn::(_, _, _, _, _, _, _)}; } @@ -113,19 +115,19 @@ fn visit_item(i: @item, e: E, v: vt) { for vi: @view_item in nm.view_items { v.visit_view_item(vi, e, v); } for ni: @native_item in nm.items { v.visit_native_item(ni, e, v); } } - item_ty(t, tps) { v.visit_ty(t, e, v); visit_ty_params(tps, e, v); } + item_ty(t, tps) { v.visit_ty(t, e, v); v.visit_ty_params(tps, e, v); } item_res(decl, tps, body, dtor_id, _) { v.visit_fn(fk_res(i.ident, tps), decl, body, i.span, dtor_id, e, v); } item_tag(variants, tps) { - visit_ty_params(tps, e, v); + v.visit_ty_params(tps, e, v); for vr: variant in variants { for va: variant_arg in vr.node.args { v.visit_ty(va.ty, e, v); } } } item_obj(ob, tps, _) { - visit_ty_params(tps, e, v); + v.visit_ty_params(tps, e, v); for f: obj_field in ob.fields { v.visit_ty(f.ty, e, v); } for m: @method in ob.methods { v.visit_fn(fk_method(m.ident, m.tps), m.decl, m.body, m.span, @@ -133,7 +135,7 @@ fn visit_item(i: @item, e: E, v: vt) { } } item_impl(tps, ifce, ty, methods) { - visit_ty_params(tps, e, v); + v.visit_ty_params(tps, e, v); alt ifce { some(ty) { v.visit_ty(ty, e, v); } _ {} } v.visit_ty(ty, e, v); for m in methods { @@ -142,7 +144,7 @@ fn visit_item(i: @item, e: E, v: vt) { } } item_iface(tps, methods) { - visit_ty_params(tps, e, v); + v.visit_ty_params(tps, e, v); for m in methods { for a in m.decl.inputs { v.visit_ty(a.ty, e, v); } v.visit_ty(m.decl.output, e, v); @@ -217,7 +219,7 @@ fn visit_pat(p: @pat, e: E, v: vt) { fn visit_native_item(ni: @native_item, e: E, v: vt) { alt ni.node { native_item_fn(fd, tps) { - visit_ty_params(tps, e, v); + v.visit_ty_params(tps, e, v); visit_fn_decl(fd, e, v); } native_item_ty. { } @@ -246,7 +248,7 @@ fn visit_fn_decl(fd: fn_decl, e: E, v: vt) { fn visit_fn(fk: fn_kind, decl: fn_decl, body: blk, _sp: span, _id: node_id, e: E, v: vt) { visit_fn_decl(decl, e, v); - visit_ty_params(tps_of_fn(fk), e, v); + v.visit_ty_params(tps_of_fn(fk), e, v); v.visit_block(body, e, v); } @@ -414,6 +416,7 @@ type simple_visitor = visit_decl: fn@(@decl), visit_expr: fn@(@expr), visit_ty: fn@(@ty), + visit_ty_params: fn@([ty_param]), visit_constr: fn@(@path, span, node_id), visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id)}; @@ -432,6 +435,7 @@ fn default_simple_visitor() -> simple_visitor { visit_decl: fn(_d: @decl) { }, visit_expr: fn(_e: @expr) { }, visit_ty: simple_ignore_ty, + visit_ty_params: fn(_ps: [ty_param]) {}, visit_constr: fn(_p: @path, _sp: span, _id: node_id) { }, visit_fn: fn(_fk: fn_kind, _d: fn_decl, _b: blk, _sp: span, _id: node_id) { } @@ -488,6 +492,10 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> { f(ty); visit_ty(ty, e, v); } + fn v_ty_params(f: fn@([ty_param]), ps: [ty_param], &&e: (), v: vt<()>) { + f(ps); + visit_ty_params(ps, e, v); + } fn v_constr(f: fn@(@path, span, node_id), pt: @path, sp: span, id: node_id, &&e: (), v: vt<()>) { f(pt, sp, id); @@ -517,6 +525,7 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> { visit_decl: bind v_decl(v.visit_decl, _, _, _), visit_expr: bind v_expr(v.visit_expr, _, _, _), visit_ty: visit_ty, + visit_ty_params: bind v_ty_params(v.visit_ty_params, _, _, _), visit_constr: bind v_constr(v.visit_constr, _, _, _, _, _), visit_fn: bind v_fn(v.visit_fn, _, _, _, _, _, _, _) }); diff --git a/src/comp/util/ppaux.rs b/src/comp/util/ppaux.rs index ec1b90572c0b..d4c01edbb15a 100644 --- a/src/comp/util/ppaux.rs +++ b/src/comp/util/ppaux.rs @@ -19,10 +19,6 @@ fn mode_str(m: ty::mode) -> str { } } -fn mode_str_1(m: ty::mode) -> str { - alt m { ast::by_ref. { "ref" } _ { mode_str(m) } } -} - fn ty_to_str(cx: ctxt, typ: t) -> str { fn fn_input_to_str(cx: ctxt, input: {mode: middle::ty::mode, ty: t}) -> str { @@ -95,6 +91,7 @@ fn ty_to_str(cx: ctxt, typ: t) -> str { ty_str. { "str" } ty_box(tm) { "@" + mt_to_str(cx, tm) } ty_uniq(tm) { "~" + mt_to_str(cx, tm) } + ty_ptr(tm) { "*" + mt_to_str(cx, tm) } ty_vec(tm) { "[" + mt_to_str(cx, tm) + "]" } ty_type. { "type" } ty_rec(elems) { diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs index 17f2c7390623..39ecb909a858 100644 --- a/src/compiletest/procsrv.rs +++ b/src/compiletest/procsrv.rs @@ -163,6 +163,7 @@ fn maybe_with_lib_path(path: str, f: fn@() -> T) -> T { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] fn maybe_with_lib_path(_path: str, f: fn@() -> T) -> T { f() } diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index a97253119fb8..fefa9d7e6172 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -309,6 +309,7 @@ fn program_output(cx: cx, testfile: str, lib_path: str, prog: str, // Linux and mac don't require adjusting the library search path #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] fn make_cmdline(_libpath: str, prog: str, args: [str]) -> str { #fmt["%s %s", prog, str::connect(args, " ")] } diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs index 6ffe1bda6b83..eb8460374829 100644 --- a/src/compiletest/util.rs +++ b/src/compiletest/util.rs @@ -17,6 +17,7 @@ fn make_new_path(path: str) -> str { } #[cfg(target_os = "linux")] +#[cfg(target_os = "freebsd")] fn lib_path_env_var() -> str { "LD_LIBRARY_PATH" } #[cfg(target_os = "macos")] @@ -27,6 +28,7 @@ fn lib_path_env_var() -> str { "PATH" } #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] fn path_div() -> str { ":" } #[cfg(target_os = "win32")] diff --git a/src/etc/snapshot.py b/src/etc/snapshot.py index 130304fdab83..673facfe70a7 100644 --- a/src/etc/snapshot.py +++ b/src/etc/snapshot.py @@ -33,7 +33,13 @@ snapshot_files = { "lib/std-*.dll", "lib/rustc-*.dll", "lib/rustrt.dll", - "lib/rustllvm.dll"] + "lib/rustllvm.dll"], + "freebsd": ["bin/rustc", + "lib/libcore-*.so", + "lib/libstd-*.so", + "lib/librustc-*.so", + "lib/librustrt.so", + "lib/librustllvm.so"] } def parse_line(n, line): @@ -73,6 +79,8 @@ def get_kernel(triple): return "winnt" if os_name == "darwin": return "macos" + if os_name == "freebsd": + return "freebsd" return "linux" def get_cpu(triple): diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 0bdda6f0afbd..5475e695a799 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -41,7 +41,7 @@ export is_alphabetic, is_XID_start, is_XID_continue, is_lowercase, is_uppercase, is_whitespace, is_alphanumeric, - to_digit, maybe_digit, cmp; + to_digit, to_lower, to_upper, maybe_digit, cmp; import is_alphabetic = unicode::derived_property::Alphabetic; import is_XID_start = unicode::derived_property::XID_Start; @@ -122,7 +122,7 @@ pure fn to_digit(c: char) -> u8 unsafe { } /* - Function: to_digit + Function: maybe_digit Convert a char to the corresponding digit. Returns none when the character is not a valid hexadecimal digit. @@ -136,6 +136,34 @@ pure fn maybe_digit(c: char) -> option::t { } } +/* + Function: to_lower + + Convert a char to the corresponding lower case. + + FIXME: works only on ASCII +*/ +pure fn to_lower(c: char) -> char { + alt c { + 'A' to 'Z' { ((c as u8) + 32u8) as char } + _ { c } + } +} + +/* + Function: to_upper + + Convert a char to the corresponding upper case. + + FIXME: works only on ASCII +*/ +pure fn to_upper(c: char) -> char { + alt c { + 'a' to 'z' { ((c as u8) - 32u8) as char } + _ { c } + } +} + /* Function: cmp diff --git a/src/libcore/cmath.rs b/src/libcore/cmath.rs index dd922ddf1bcb..4f3c5a8d68b8 100644 --- a/src/libcore/cmath.rs +++ b/src/libcore/cmath.rs @@ -5,6 +5,9 @@ import ctypes::c_int; import ctypes::c_float; import ctypes::c_double; +// function names are almost identical to C's libmath, a few have been +// renamed, grep for "rename:" + #[link_name = "m"] #[abi = "cdecl"] native mod c_double { @@ -26,8 +29,10 @@ native mod c_double { pure fn expm1(n: c_double) -> c_double; pure fn exp2(n: c_double) -> c_double; #[link_name="fabs"] pure fn abs(n: c_double) -> c_double; - #[link_name="fdim"] pure fn sub_pos(a: c_double, b: c_double) -> c_double; + // rename: for clarity and consistency with add/sub/mul/div + #[link_name="fdim"] pure fn abs_sub(a: c_double, b: c_double) -> c_double; pure fn floor(n: c_double) -> c_double; + // rename: for clarity and consistency with add/sub/mul/div #[link_name="fma"] pure fn mul_add(a: c_double, b: c_double, c: c_double) -> c_double; #[link_name="fmax"] pure fn fmax(a: c_double, b: c_double) -> c_double; @@ -38,17 +43,26 @@ native mod c_double { pure fn ldexp(x: c_double, n: c_int) -> c_double; #[link_name="lgamma_r"] pure fn lgamma(n: c_double, &sign: c_int) -> c_double; + // renamed: log is a reserved keyword; ln seems more natural, too #[link_name="log"] pure fn ln(n: c_double) -> c_double; - pure fn logb(n: c_double) -> c_double; + // renamed: "logb" /often/ is confused for log2 by beginners + #[link_name="logb"] pure fn log_radix(n: c_double) -> c_double; + // renamed: to be consitent with log as ln #[link_name="log1p"] pure fn ln1p(n: c_double) -> c_double; pure fn log10(n: c_double) -> c_double; + #[cfg(target_os="linux")] + #[cfg(target_os="macos")] + #[cfg(target_os="win32")] pure fn log2(n: c_double) -> c_double; - pure fn ilogb(n: c_double) -> c_int; + #[link_name="ilogb"] pure fn ilogradix(n: c_double) -> c_int; pure fn modf(n: c_double, &iptr: c_double) -> c_double; pure fn pow(n: c_double, e: c_double) -> c_double; - pure fn rint(n: c_double) -> c_double; +// FIXME enable when rounding modes become available +// pure fn rint(n: c_double) -> c_double; pure fn round(n: c_double) -> c_double; - pure fn scalbn(n: c_double, i: c_int) -> c_double; + // rename: for consistency with logradix + #[link_name="scalbn"] pure fn ldexp_radix(n: c_double, i: c_int) -> + c_double; pure fn sin(n: c_double) -> c_double; pure fn sinh(n: c_double) -> c_double; pure fn sqrt(n: c_double) -> c_double; @@ -90,7 +104,7 @@ native mod c_float { #[link_name="expm1f"]pure fn expm1(n: c_float) -> c_float; #[link_name="exp2f"] pure fn exp2(n: c_float) -> c_float; #[link_name="fabsf"] pure fn abs(n: c_float) -> c_float; - #[link_name="fdimf"] pure fn sub_pos(a: c_float, b: c_float) -> c_float; + #[link_name="fdimf"] pure fn abs_sub(a: c_float, b: c_float) -> c_float; #[link_name="floorf"] pure fn floor(n: c_float) -> c_float; #[link_name="frexpf"] pure fn frexp(n: c_float, &value: c_int) -> c_float; @@ -105,17 +119,21 @@ native mod c_float { #[link_name="lgammaf_r"] pure fn lgamma(n: c_float, &sign: c_int) -> c_float; #[link_name="logf"] pure fn ln(n: c_float) -> c_float; - #[link_name="logbf"] pure fn logb(n: c_float) -> c_float; + #[link_name="logbf"] pure fn log_radix(n: c_float) -> c_float; #[link_name="log1pf"] pure fn ln1p(n: c_float) -> c_float; + #[cfg(target_os="linux")] + #[cfg(target_os="macos")] + #[cfg(target_os="win32")] #[link_name="log2f"] pure fn log2(n: c_float) -> c_float; #[link_name="log10f"] pure fn log10(n: c_float) -> c_float; - #[link_name="ilogbf"] pure fn ilogb(n: c_float) -> c_int; + #[link_name="ilogbf"] pure fn ilog_radix(n: c_float) -> c_int; #[link_name="modff"] pure fn modf(n: c_float, &iptr: c_float) -> c_float; #[link_name="powf"] pure fn pow(n: c_float, e: c_float) -> c_float; - #[link_name="rintf"] pure fn rint(n: c_float) -> c_float; +// FIXME enable when rounding modes become available +// #[link_name="rintf"] pure fn rint(n: c_float) -> c_float; #[link_name="roundf"] pure fn round(n: c_float) -> c_float; - #[link_name="scalbnf"] pure fn scalbn(n: c_float, i: c_int) -> c_float; + #[link_name="scalbnf"] pure fn ldexp_radix(n: c_float, i: c_int) -> c_float; #[link_name="sinf"] pure fn sin(n: c_float) -> c_float; #[link_name="sinhf"] pure fn sinh(n: c_float) -> c_float; #[link_name="sqrtf"] pure fn sqrt(n: c_float) -> c_float; diff --git a/src/libcore/either.rs b/src/libcore/either.rs index 60ad12d2e026..f3571ea5bcf7 100644 --- a/src/libcore/either.rs +++ b/src/libcore/either.rs @@ -103,6 +103,24 @@ pure fn to_result(eith: t) -> result::t { } } +/* +Function: is_left + +Checks whether the given value is a left +*/ +pure fn is_left(eith: t) -> bool { + alt eith { left(_) { true } _ { false } } +} + +/* +Function: is_left + +Checks whether the given value is a right +*/ +pure fn is_right(eith: t) -> bool { + alt eith { right(_) { true } _ { false } } +} + // // Local Variables: // mode: rust diff --git a/src/libcore/f32.rs b/src/libcore/f32.rs index 26ec20a2ed9f..7c7d45694e50 100644 --- a/src/libcore/f32.rs +++ b/src/libcore/f32.rs @@ -16,6 +16,8 @@ type t = f32; // PORT check per architecture +// FIXME obtain these in a different way + const radix: uint = 2u; const mantissa_digits: uint = 24u; @@ -42,7 +44,7 @@ const infinity: f32 = 1.0_f32/0.0_f32; const neg_infinity: f32 = -1.0_f32/0.0_f32; /* Predicate: isNaN */ -pure fn isNaN(f: f32) -> bool { f != f } +pure fn is_NaN(f: f32) -> bool { f != f } /* Function: add */ pure fn add(x: f32, y: f32) -> f32 { ret x + y; } @@ -77,29 +79,32 @@ pure fn ge(x: f32, y: f32) -> bool { ret x >= y; } /* Predicate: gt */ pure fn gt(x: f32, y: f32) -> bool { ret x > y; } +// FIXME replace the predicates below with llvm intrinsics or calls +// to the libmath macros in the rust runtime for performance + /* -Predicate: positive +Predicate: is_positive Returns true if `x` is a positive number, including +0.0f320 and +Infinity. */ -pure fn positive(x: f32) -> bool +pure fn is_positive(x: f32) -> bool { ret x > 0.0f32 || (1.0f32/x) == infinity; } /* -Predicate: negative +Predicate: is_negative Returns true if `x` is a negative number, including -0.0f320 and -Infinity. */ -pure fn negative(x: f32) -> bool +pure fn is_negative(x: f32) -> bool { ret x < 0.0f32 || (1.0f32/x) == neg_infinity; } /* -Predicate: nonpositive +Predicate: is_nonpositive Returns true if `x` is a negative number, including -0.0f320 and -Infinity. (This is the same as `f32::negative`.) */ -pure fn nonpositive(x: f32) -> bool { +pure fn is_nonpositive(x: f32) -> bool { ret x < 0.0f32 || (1.0f32/x) == neg_infinity; } @@ -109,10 +114,39 @@ Predicate: nonnegative Returns true if `x` is a positive number, including +0.0f320 and +Infinity. (This is the same as `f32::positive`.) */ -pure fn nonnegative(x: f32) -> bool { +pure fn is_nonnegative(x: f32) -> bool { ret x > 0.0f32 || (1.0f32/x) == infinity; } +/* +Predicate: is_zero + +Returns true if `x` is a zero number (positive or negative zero) +*/ +pure fn is_zero(x: f32) -> bool { + ret x == 0.0f32 || x == -0.0f32; +} + +/* +Predicate: is_infinite + +Returns true if `x`is an infinite numer +*/ +pure fn is_infinite(x: f32) -> bool { + ret x == infinity || x == neg_infinity; +} + +/* +Predicate: is_finite + +Returns true if `x`is a finite numer +*/ +pure fn is_finite(x: f32) -> bool { + ret !(is_nan(x) || is_infinite(x)); +} + +// FIXME add is_normal, is_subnormal, and fpclassify + /* Module: consts */ mod consts { @@ -208,6 +242,15 @@ mod consts { const ln_10: f32 = 2.30258509299404568401799145468436421_f32; } +pure fn logarithm(n: f32, b: f32) -> f32 { + ret ln(n) / ln(b); +} + +#[cfg(target_os="freebsd")] +pure fn log2(n: f32) -> f32 { + ret ln(n) / consts::ln_2; +} + // // Local Variables: // mode: rust diff --git a/src/libcore/f64.rs b/src/libcore/f64.rs index c7ee7e1a5dbf..058d66201060 100644 --- a/src/libcore/f64.rs +++ b/src/libcore/f64.rs @@ -16,6 +16,8 @@ type t = f64; // PORT check per architecture +// FIXME obtain these in a different way + const radix: uint = 2u; const mantissa_digits: uint = 53u; @@ -42,7 +44,7 @@ const infinity: f64 = 1.0_f64/0.0_f64; const neg_infinity: f64 = -1.0_f64/0.0_f64; /* Predicate: isNaN */ -pure fn isNaN(f: f64) -> bool { f != f } +pure fn is_NaN(f: f64) -> bool { f != f } /* Function: add */ pure fn add(x: f64, y: f64) -> f64 { ret x + y; } @@ -78,41 +80,70 @@ pure fn ge(x: f64, y: f64) -> bool { ret x >= y; } pure fn gt(x: f64, y: f64) -> bool { ret x > y; } /* -Predicate: positive +Predicate: is_positive Returns true if `x` is a positive number, including +0.0f640 and +Infinity. */ -pure fn positive(x: f64) -> bool +pure fn is_positive(x: f64) -> bool { ret x > 0.0f64 || (1.0f64/x) == infinity; } /* -Predicate: negative +Predicate: is_negative Returns true if `x` is a negative number, including -0.0f640 and -Infinity. */ -pure fn negative(x: f64) -> bool +pure fn is_negative(x: f64) -> bool { ret x < 0.0f64 || (1.0f64/x) == neg_infinity; } /* -Predicate: nonpositive +Predicate: is_nonpositive Returns true if `x` is a negative number, including -0.0f640 and -Infinity. (This is the same as `f64::negative`.) */ -pure fn nonpositive(x: f64) -> bool { +pure fn is_nonpositive(x: f64) -> bool { ret x < 0.0f64 || (1.0f64/x) == neg_infinity; } /* -Predicate: nonnegative +Predicate: is_nonnegative Returns true if `x` is a positive number, including +0.0f640 and +Infinity. (This is the same as `f64::positive`.) */ -pure fn nonnegative(x: f64) -> bool { +pure fn is_nonnegative(x: f64) -> bool { ret x > 0.0f64 || (1.0f64/x) == infinity; } +/* +Predicate: is_zero + +Returns true if `x` is a zero number (positive or negative zero) +*/ +pure fn is_zero(x: f64) -> bool { + ret x == 0.0f64 || x == -0.0f64; +} + +/* +Predicate: is_infinite + +Returns true if `x`is an infinite numer +*/ +pure fn is_infinite(x: f64) -> bool { + ret x == infinity || x == neg_infinity; +} + +/* +Predicate: is_finite + +Returns true if `x`is a finite numer +*/ +pure fn is_finite(x: f64) -> bool { + ret !(is_nan(x) || is_infinite(x)); +} + +// FIXME add is_normal, is_subnormal, and fpclassify + /* Module: consts */ mod consts { @@ -208,6 +239,15 @@ mod consts { const ln_10: f64 = 2.30258509299404568401799145468436421_f64; } +pure fn logarithm(n: f64, b: f64) -> f64 { + ret ln(n) / ln(b); +} + +#[cfg(target_os="freebsd")] +pure fn log2(n: f64) -> f64 { + ret ln(n) / consts::ln_2; +} + // // Local Variables: // mode: rust diff --git a/src/libcore/option.rs b/src/libcore/option.rs index aaaf82eb7cd3..e0a137ca9e84 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -36,7 +36,7 @@ pure fn get(opt: t) -> T { /* */ -fn map(opt: t, f: block(T) -> U) -> t { +fn map(opt: t, f: block(T) -> U) -> t { alt opt { some(x) { some(f(x)) } none. { none } } } @@ -61,7 +61,7 @@ Function: from_maybe Returns the contained value or a default */ -pure fn from_maybe(def: T, opt: t) -> T { +pure fn from_maybe(def: T, opt: t) -> T { alt opt { some(x) { x } none. { def } } } @@ -70,7 +70,7 @@ Function: maybe Applies a function to the contained value or returns a default */ -fn maybe(def: U, opt: t, f: block(T) -> U) -> U { +fn maybe(def: U, opt: t, f: block(T) -> U) -> U { alt opt { none. { def } some(t) { f(t) } } } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 84fbc3b7f90e..91038474eadd 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -37,7 +37,7 @@ Failure: If the result is an error */ -fn get(res: t) -> T { +fn get(res: t) -> T { alt res { ok(t) { t } err(_) { @@ -57,7 +57,7 @@ Failure: If the result is not an error */ -fn get_err(res: t) -> U { +fn get_err(res: t) -> U { alt res { err(u) { u } ok(_) { diff --git a/src/libcore/str.rs b/src/libcore/str.rs index 6cb3ada6a880..cba9b11cb3d3 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -7,12 +7,12 @@ String manipulation. export eq, lteq, hash, is_empty, is_not_empty, is_whitespace, byte_len, byte_len_range, index, rindex, find, starts_with, ends_with, substr, slice, split, splitn, - split_str, concat, connect, to_upper, replace, char_slice, trim_left, - trim_right, trim, unshift_char, shift_char, pop_char, push_char, - is_utf8, from_chars, to_chars, char_len, char_len_range, char_at, - bytes, is_ascii, shift_byte, pop_byte, + split_str, concat, connect, to_lower, to_upper, replace, char_slice, + trim_left, trim_right, trim, unshift_char, shift_char, pop_char, + push_char, is_utf8, from_chars, to_chars, char_len, char_len_range, + char_at, bytes, is_ascii, shift_byte, pop_byte, unsafe_from_byte, unsafe_from_bytes, from_char, char_range_at, - str_from_cstr, sbuf, as_buf, push_byte, utf8_char_width, safe_slice, + from_cstr, sbuf, as_buf, push_byte, utf8_char_width, safe_slice, contains, iter_chars, loop_chars, loop_chars_sub, escape; @@ -116,14 +116,7 @@ Function: is_whitespace Returns true if the string contains only whitespace */ fn is_whitespace(s: str) -> bool { - let i = 0u; - let len = char_len(s); - while i < len { - // FIXME: This is not how char_at works - if !char::is_whitespace(char_at(s, i)) { ret false; } - i += 1u; - } - ret true; + ret loop_chars(s, char::is_whitespace); } /* @@ -832,7 +825,18 @@ fn connect(v: [str], sep: str) -> str { ret s; } -// FIXME: This only handles ASCII +/* +Function: to_lower + +Convert a string to lowercase +*/ +fn to_lower(s: str) -> str { + let outstr = ""; + iter_chars(s) { |c| + push_char(outstr, char::to_lower(c)); + } + ret outstr; +} /* Function: to_upper @@ -840,15 +844,8 @@ Convert a string to uppercase */ fn to_upper(s: str) -> str { let outstr = ""; - let ascii_a = 'a' as u8; - let ascii_z = 'z' as u8; - let diff = 32u8; - for byte: u8 in s { - let next; - if ascii_a <= byte && byte <= ascii_z { - next = byte - diff; - } else { next = byte; } - push_byte(outstr, next); + iter_chars(s) { |c| + push_char(outstr, char::to_upper(c)); } ret outstr; } @@ -976,11 +973,11 @@ fn as_buf(s: str, f: block(sbuf) -> T) -> T unsafe { } /* -Function: str_from_cstr +Function: from_cstr Create a Rust string from a null-terminated C string */ -unsafe fn str_from_cstr(cstr: sbuf) -> str { +unsafe fn from_cstr(cstr: sbuf) -> str { let res = ""; let start = cstr; let curr = start; diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 90e1ff378649..0c1db7119af8 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -742,6 +742,17 @@ fn iter(v: [const T], f: block(T)) { iteri(v) { |_i, v| f(v) } } +/* +Function: iter2 + +Iterates over two vectors in parallel + +*/ +fn iter2(v: [U], v2: [T], f: block(U, T)) { + let i = 0; + for elt in v { f(elt, v2[i]); i += 1; } +} + /* Function: iteri diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs index 7c0a13bb5f8a..8da26c55b105 100644 --- a/src/libstd/deque.rs +++ b/src/libstd/deque.rs @@ -57,7 +57,7 @@ fn create() -> t { ret rv; } - fn get(elts: [mutable cell], i: uint) -> T { + fn get(elts: [mutable cell], i: uint) -> T { ret alt elts[i] { option::some(t) { t } _ { fail } }; } obj deque(mutable nelts: uint, diff --git a/src/libstd/freebsd_os.rs b/src/libstd/freebsd_os.rs new file mode 100644 index 000000000000..4fecf418213e --- /dev/null +++ b/src/libstd/freebsd_os.rs @@ -0,0 +1,155 @@ +/* +Module: os + +TODO: Restructure and document +*/ + +import core::option; +import core::ctypes::*; + +export libc; +export libc_constants; +export pipe; +export fd_FILE; +export close; +export fclose; +export waitpid; +export getcwd; +export exec_suffix; +export target_os; +export dylib_filename; +export get_exe_path; +export fsync_fd; + +// FIXME Somehow merge stuff duplicated here and macosx_os.rs. Made difficult +// by https://github.com/graydon/rust/issues#issue/268 + +#[link_name = ""] // FIXME remove after #[nolink] is snapshotted +#[nolink] +#[abi = "cdecl"] +native mod libc { + fn read(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn write(fd: fd_t, buf: *u8, count: size_t) -> ssize_t; + fn fread(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn fwrite(buf: *u8, size: size_t, n: size_t, f: libc::FILE) -> size_t; + fn open(s: str::sbuf, flags: c_int, mode: unsigned) -> fd_t; + fn close(fd: fd_t) -> c_int; + type FILE; + fn fopen(path: str::sbuf, mode: str::sbuf) -> FILE; + fn fdopen(fd: fd_t, mode: str::sbuf) -> FILE; + fn fclose(f: FILE); + fn fflush(f: FILE) -> c_int; + fn fsync(fd: fd_t) -> c_int; + fn fileno(f: FILE) -> fd_t; + fn fgetc(f: FILE) -> c_int; + fn ungetc(c: c_int, f: FILE); + fn feof(f: FILE) -> c_int; + fn fseek(f: FILE, offset: long, whence: c_int) -> c_int; + fn ftell(f: FILE) -> long; + type dir; + fn opendir(d: str::sbuf) -> dir; + fn closedir(d: dir) -> c_int; + type dirent; + fn readdir(d: dir) -> dirent; + fn getenv(n: str::sbuf) -> str::sbuf; + fn setenv(n: str::sbuf, v: str::sbuf, overwrite: c_int) -> c_int; + fn unsetenv(n: str::sbuf) -> c_int; + fn pipe(buf: *mutable fd_t) -> c_int; + fn waitpid(pid: pid_t, &status: c_int, options: c_int) -> pid_t; + fn readlink(path: str::sbuf, buf: str::sbuf, bufsize: size_t) -> ssize_t; + fn mkdir(path: str::sbuf, mode: c_int) -> c_int; + fn rmdir(path: str::sbuf) -> c_int; + fn chdir(path: str::sbuf) -> c_int; + + fn sysctl(name: *c_int, namelen: c_uint, + oldp: *u8, &oldlenp: size_t, + newp: *u8, newlen: size_t) -> c_int; +} + +mod libc_constants { + const O_RDONLY: c_int = 0i32; + const O_WRONLY: c_int = 1i32; + const O_RDWR: c_int = 2i32; + const O_APPEND: c_int = 8i32; + const O_CREAT: c_int = 512i32; + const O_EXCL: c_int = 2048i32; + const O_TRUNC: c_int = 1024i32; + const O_TEXT: c_int = 0i32; // nonexistent in FreeBSD libc + const O_BINARY: c_int = 0i32; // nonexistent in FreeBSD libc + + const S_IRUSR: unsigned = 256u32; + const S_IWUSR: unsigned = 128u32; + + const CTL_KERN: c_int = 1i32; + const KERN_PROC: c_int = 14i32; + const KERN_PROC_PATHNAME: c_int = 12i32; +} + +fn pipe() -> {in: fd_t, out: fd_t} { + let fds = {mutable in: 0i32, mutable out: 0i32}; + assert (os::libc::pipe(ptr::mut_addr_of(fds.in)) == 0i32); + ret {in: fds.in, out: fds.out}; +} + +fn fd_FILE(fd: fd_t) -> libc::FILE { + ret str::as_buf("r", {|modebuf| libc::fdopen(fd, modebuf) }); +} + +fn close(fd: fd_t) -> c_int { + libc::close(fd) +} + +fn fclose(file: libc::FILE) { + libc::fclose(file) +} + +fn fsync_fd(fd: fd_t, _l: io::fsync::level) -> c_int { + ret libc::fsync(fd); +} + +fn waitpid(pid: pid_t) -> i32 { + let status = 0i32; + assert (os::libc::waitpid(pid, status, 0i32) != -1i32); + ret status; +} + +#[abi = "cdecl"] +native mod rustrt { + fn rust_getcwd() -> str; +} + +fn getcwd() -> str { ret rustrt::rust_getcwd(); } + +fn exec_suffix() -> str { ret ""; } + +fn target_os() -> str { ret "freebsd"; } + +fn dylib_filename(base: str) -> str { ret "lib" + base + ".so"; } + +/// Returns the directory containing the running program +/// followed by a path separator +fn get_exe_path() -> option::t unsafe { + let bufsize = 1023u; + let path = str::unsafe_from_bytes(vec::init_elt(0u8, bufsize)); + let mib = [libc_constants::CTL_KERN, + libc_constants::KERN_PROC, + libc_constants::KERN_PROC_PATHNAME, -1i32]; + ret str::as_buf(path, { |path_buf| + if libc::sysctl(vec::unsafe::to_ptr(mib), + vec::len(mib) as c_uint, + path_buf, bufsize, + ptr::null(), 0u) == 0i32 { + option::some(fs::dirname(path) + fs::path_sep()) + } else { + option::none + } + }); +} + +// Local Variables: +// mode: rust; +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 6c8c614ab559..330305651436 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -148,6 +148,7 @@ fn make_dir(p: path, mode: ctypes::c_int) -> bool { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn mkdir(_p: path, _mode: ctypes::c_int) -> bool { ret str::as_buf(_p, {|buf| os::libc::mkdir(buf, _mode) == 0i32 }); } @@ -186,6 +187,7 @@ fn remove_dir(p: path) -> bool { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn rmdir(_p: path) -> bool { ret str::as_buf(_p, {|buf| os::libc::rmdir(buf) == 0i32 }); } @@ -201,6 +203,7 @@ fn change_dir(p: path) -> bool { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn chdir(_p: path) -> bool { ret str::as_buf(_p, {|buf| os::libc::chdir(buf) == 0i32 }); } @@ -367,6 +370,7 @@ fn normalize(p: path) -> path { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn reabsolute(orig: path, new: path) -> path { if path_is_absolute(orig) { path_sep() + new diff --git a/src/libstd/generic_os.rs b/src/libstd/generic_os.rs index babfd0281ba1..c1312ad46080 100644 --- a/src/libstd/generic_os.rs +++ b/src/libstd/generic_os.rs @@ -28,18 +28,20 @@ fn setenv(n: str, v: str) { } #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] fn getenv(n: str) -> option::t unsafe { let s = str::as_buf(n, {|buf| os::libc::getenv(buf) }); ret if unsafe::reinterpret_cast(s) == 0 { option::none:: } else { let s = unsafe::reinterpret_cast(s); - option::some::(str::str_from_cstr(s)) + option::some::(str::from_cstr(s)) }; } #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] fn setenv(n: str, v: str) { // FIXME (868) str::as_buf( diff --git a/src/libstd/list.rs b/src/libstd/list.rs index c6a24d275eb3..f6b46ed7d2e8 100644 --- a/src/libstd/list.rs +++ b/src/libstd/list.rs @@ -97,6 +97,27 @@ fn has(ls: list, elt: T) -> bool { ret false; } +/* +Function: is_empty + +Returns true if the list is empty. +*/ +pure fn is_empty(ls: list) -> bool { + alt ls { + nil. { true } + _ { false } + } +} + +/* +Function: is_not_empty + +Returns true if the list is not empty. +*/ +pure fn is_not_empty(ls: list) -> bool { + ret !is_empty(ls); +} + /* Function: len @@ -112,8 +133,11 @@ Function: tail Returns all but the first element of a list */ -pure fn tail(ls: list) -> list { - alt ls { cons(_, tl) { ret *tl; } nil. { fail "list empty" } } +pure fn tail(ls: list) : is_not_empty(ls) -> list { + alt ls { + cons(_, tl) { ret *tl; } + nil. { fail "list empty" } + } } /* @@ -121,8 +145,11 @@ Function: head Returns the first element of a list */ -pure fn head(ls: list) -> T { - alt ls { cons(hd, _) { ret hd; } nil. { fail "list empty" } } +pure fn head(ls: list) : is_not_empty(ls) -> T { + alt ls { + cons(hd, _) { ret hd; } + nil. { fail "list empty" } + } } /* diff --git a/src/libstd/rope.rs b/src/libstd/rope.rs index dea9d187110b..6b821dc62231 100644 --- a/src/libstd/rope.rs +++ b/src/libstd/rope.rs @@ -443,7 +443,7 @@ fn iter_chars(rope: rope, it: block(char)) { loop_chars(rope) {|x| it(x); ret true - } + }; } /* diff --git a/src/libstd/run_program.rs b/src/libstd/run_program.rs index 55163696579a..11bc15d578f8 100644 --- a/src/libstd/run_program.rs +++ b/src/libstd/run_program.rs @@ -266,6 +266,7 @@ fn waitpid(pid: pid_t) -> int { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn waitpid_os(pid: pid_t) -> int { #[cfg(target_os = "linux")] fn WIFEXITED(status: i32) -> bool { @@ -273,6 +274,7 @@ fn waitpid(pid: pid_t) -> int { } #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn WIFEXITED(status: i32) -> bool { (status & 0x7fi32) == 0i32 } @@ -283,6 +285,7 @@ fn waitpid(pid: pid_t) -> int { } #[cfg(target_os = "macos")] + #[cfg(target_os = "freebsd")] fn WEXITSTATUS(status: i32) -> i32 { status >> 8i32 } diff --git a/src/libstd/std.rc b/src/libstd/std.rc index bcd2b82cf1a1..8acb1ac9ef92 100644 --- a/src/libstd/std.rc +++ b/src/libstd/std.rc @@ -98,6 +98,13 @@ mod os; #[path = "posix_fs.rs"] mod os_fs; +#[cfg(target_os = "freebsd")] +#[path = "freebsd_os.rs"] +mod os; +#[cfg(target_os = "freebsd")] +#[path = "posix_fs.rs"] +mod os_fs; + // Local Variables: // mode: rust; // fill-column: 78; diff --git a/src/libstd/util.rs b/src/libstd/util.rs index a15b5291546f..f4d984a8937c 100644 --- a/src/libstd/util.rs +++ b/src/libstd/util.rs @@ -7,7 +7,7 @@ Function: id The identity function */ -pure fn id(x: T) -> T { x } +pure fn id(x: T) -> T { x } /* Function: unreachable diff --git a/src/libstd/uv.rs b/src/libstd/uv.rs index a12fdc8ceccb..b24008448bb7 100644 --- a/src/libstd/uv.rs +++ b/src/libstd/uv.rs @@ -5,6 +5,7 @@ the C libuv API. Does very little right now pending scheduler improvements. #[cfg(target_os = "linux")]; #[cfg(target_os = "macos")]; +#[cfg(target_os = "freebsd")]; export sanity_check; export loop_t, idle_t; @@ -39,6 +40,7 @@ type idle_cb = opaque_cb; #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] type handle_private_fields = { a00: ctypes::c_int, a01: ctypes::c_int, @@ -121,6 +123,7 @@ fn sanity_check() { #[cfg(target_os = "linux")] #[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] fn handle_fields_new() -> handle_fields { { loop: ptr::null(), @@ -149,4 +152,4 @@ fn idle_new() -> idle_t { { fields: handle_fields_new() } -} \ No newline at end of file +} diff --git a/src/llvm b/src/llvm index a320b2aa41fb..4fb132c80351 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit a320b2aa41fbe3f944bad33780626d65d1b11e6f +Subproject commit 4fb132c803512f06f7cbc38baa6e86280912f800 diff --git a/src/rt/arch/i386/ccall.S b/src/rt/arch/i386/ccall.S index a8b89dc6b0f8..c04c3e01c7ea 100644 --- a/src/rt/arch/i386/ccall.S +++ b/src/rt/arch/i386/ccall.S @@ -16,20 +16,20 @@ ___morestack: __morestack: #endif -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_startproc #endif pushl %ebp -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_def_cfa_offset 8 .cfi_offset %ebp, -8 #endif movl %esp,%ebp // save esp -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_def_cfa_register %ebp #endif @@ -42,6 +42,6 @@ __morestack: ret -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_endproc -#endif \ No newline at end of file +#endif diff --git a/src/rt/arch/i386/morestack.S b/src/rt/arch/i386/morestack.S index 6b2e55022c62..d1433213b2d0 100644 --- a/src/rt/arch/i386/morestack.S +++ b/src/rt/arch/i386/morestack.S @@ -72,7 +72,7 @@ #define UPCALL_DEL_STACK L_upcall_del_stack$stub #define MORESTACK ___morestack #else -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) #define UPCALL_NEW_STACK upcall_new_stack #define UPCALL_DEL_STACK upcall_del_stack #define RUST_GET_TASK rust_get_task @@ -93,7 +93,7 @@ .globl MORESTACK // FIXME: What about _WIN32? -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) .hidden MORESTACK #else #if defined(__APPLE__) @@ -106,7 +106,7 @@ #endif MORESTACK: -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_startproc #endif @@ -125,7 +125,7 @@ MORESTACK: // __morestack, and an extra return address. pushl %ebp -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) // The CFA is 20 bytes above the register that it is // associated with for this frame (which will be %ebp) .cfi_def_cfa_offset 20 @@ -133,7 +133,7 @@ MORESTACK: .cfi_offset %ebp, -20 #endif movl %esp, %ebp -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) // Calculate the CFA as an offset from %ebp .cfi_def_cfa_register %ebp #endif @@ -216,7 +216,7 @@ MORESTACK: // FIXME: I don't think these rules are necessary // since the unwinder should never encounter an instruction // pointer pointing here. -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) // Restore the rule for how to find %ebp .cfi_restore %ebp // Tell the unwinder how to find the CFA in terms of %esp @@ -234,7 +234,7 @@ MORESTACK: jmpl *%eax -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_endproc #endif diff --git a/src/rt/arch/i386/record_sp.S b/src/rt/arch/i386/record_sp.S index b9c42a650d84..1b698ed74bf5 100644 --- a/src/rt/arch/i386/record_sp.S +++ b/src/rt/arch/i386/record_sp.S @@ -14,7 +14,7 @@ .globl GET_SP .globl CHECK_STACK -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) RECORD_SP: movl 4(%esp), %eax movl %eax, %gs:48 diff --git a/src/rt/arch/x86_64/ccall.S b/src/rt/arch/x86_64/ccall.S index c208c7ae6433..42415e84a52f 100644 --- a/src/rt/arch/x86_64/ccall.S +++ b/src/rt/arch/x86_64/ccall.S @@ -23,19 +23,19 @@ ___morestack: __morestack: #endif -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_startproc #endif push %rbp -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_def_cfa_offset 16 .cfi_offset %rbp, -16 #endif mov %rsp,%rbp // save rsp -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_def_cfa_register %rbp #endif @@ -46,6 +46,6 @@ __morestack: ret -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) .cfi_endproc #endif diff --git a/src/rt/arch/x86_64/context.h b/src/rt/arch/x86_64/context.h index 75902fd79548..5784a4b85fda 100644 --- a/src/rt/arch/x86_64/context.h +++ b/src/rt/arch/x86_64/context.h @@ -31,7 +31,11 @@ extern "C" void __morestack(void *args, void *fn_ptr, uintptr_t stack_ptr); class context { public: +#ifdef __FreeBSD__ + registers_t regs __attribute__((aligned(16))); +#else registers_t regs; +#endif context(); diff --git a/src/rt/arch/x86_64/morestack.S b/src/rt/arch/x86_64/morestack.S index 3f25f786b6ca..89ca9d21452c 100644 --- a/src/rt/arch/x86_64/morestack.S +++ b/src/rt/arch/x86_64/morestack.S @@ -20,7 +20,7 @@ .globl UPCALL_DEL_STACK .globl MORESTACK -#if defined(__linux__) +#if defined(__linux__) || defined(__FreeBSD__) .hidden MORESTACK #else #if defined(__APPLE__) @@ -33,7 +33,7 @@ #endif -#if defined(__linux__) || defined(__APPLE__) +#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) MORESTACK: .cfi_startproc @@ -92,6 +92,9 @@ MORESTACK: #ifdef __linux__ call UPCALL_NEW_STACK@PLT #endif +#ifdef __FreeBSD__ + call UPCALL_NEW_STACK@PLT +#endif // Pop the saved arguments movdqa (%rsp), %xmm0 @@ -135,6 +138,9 @@ MORESTACK: #ifdef __linux__ call UPCALL_DEL_STACK@PLT #endif +#ifdef __FreeBSD__ + call UPCALL_DEL_STACK@PLT +#endif popq %rax // Restore the return value popq %rbp diff --git a/src/rt/arch/x86_64/record_sp.S b/src/rt/arch/x86_64/record_sp.S index af217d0f37fa..3b238d33c778 100644 --- a/src/rt/arch/x86_64/record_sp.S +++ b/src/rt/arch/x86_64/record_sp.S @@ -18,17 +18,19 @@ RECORD_SP: movq %rdi, %fs:112 ret -#else -#if defined(__APPLE__) +#elif defined(__APPLE__) RECORD_SP: movq $0x60+90*8, %rsi movq %rdi, %gs:(%rsi) ret +#elif defined(__FreeBSD__) +RECORD_SP: + movq %rdi, %fs:24 + ret #else RECORD_SP: ret #endif -#endif GET_SP: movq %rsp, %rax diff --git a/src/rt/rust_abi.cpp b/src/rt/rust_abi.cpp index e17abde7bd73..b533ab1f673a 100644 --- a/src/rt/rust_abi.cpp +++ b/src/rt/rust_abi.cpp @@ -7,7 +7,7 @@ #include #include "rust_abi.h" -#if defined(__APPLE__) || defined(__linux__) +#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) #define HAVE_DLFCN_H #include #elif defined(_WIN32) diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 2e6c41a8e795..92f24c8c7fa6 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -23,6 +23,8 @@ #define RZ_MAC_32 (1024*20) #define RZ_MAC_64 (1024*20) #define RZ_WIN_32 (1024*20) +#define RZ_BSD_32 (1024*20) +#define RZ_BSD_64 (1024*20) #ifdef __linux__ #ifdef __i386__ @@ -48,6 +50,14 @@ #define RED_ZONE_SIZE RZ_WIN_64 #endif #endif +#ifdef __FreeBSD__ +#ifdef __i386__ +#define RED_ZONE_SIZE RZ_BSD_32 +#endif +#ifdef __x86_64__ +#define RED_ZONE_SIZE RZ_BSD_64 +#endif +#endif // A value that goes at the end of the stack and must not be touched const uint8_t stack_canary[] = {0xAB, 0xCD, 0xAB, 0xCD, diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 4eafb8fcbba8..7d64842994e8 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -20,7 +20,7 @@ // the rust stack and happen frequently enough to catch most stack changes, // including at the beginning of all landing pads. // FIXME: Enable this for windows -#if defined __linux__ || defined __APPLE__ +#if defined __linux__ || defined __APPLE__ || defined __FreeBSD__ extern "C" void check_stack_alignment() __attribute__ ((aligned (16))); #else diff --git a/src/test/compile-fail/binop-add-ptr.rs b/src/test/compile-fail/binop-add-ptr.rs index e21f6a97fc20..83d73da94d60 100644 --- a/src/test/compile-fail/binop-add-ptr.rs +++ b/src/test/compile-fail/binop-add-ptr.rs @@ -1,4 +1,4 @@ -// error-pattern:binary operation + cannot be applied to type `*i` +// error-pattern:binary operation + cannot be applied to type `*int` fn die() -> *int { (0 as *int) + (0 as *int) } fn main() { } \ No newline at end of file diff --git a/src/test/compile-fail/block-must-not-have-result-do.rs b/src/test/compile-fail/block-must-not-have-result-do.rs index 7a1a384e64b2..f842b0f02b49 100644 --- a/src/test/compile-fail/block-must-not-have-result-do.rs +++ b/src/test/compile-fail/block-must-not-have-result-do.rs @@ -1,4 +1,4 @@ -// error-pattern:this block must not have a result +// error-pattern:mismatched types: expected `()` but found `bool` fn main() { do { diff --git a/src/test/compile-fail/block-must-not-have-result-for.rs b/src/test/compile-fail/block-must-not-have-result-for.rs index 5b5e1fafca2e..d2e7edbfc0f1 100644 --- a/src/test/compile-fail/block-must-not-have-result-for.rs +++ b/src/test/compile-fail/block-must-not-have-result-for.rs @@ -1,4 +1,4 @@ -// error-pattern:this block must not have a result +// error-pattern:mismatched types: expected `()` but found `bool` fn main() { for i in [0] { diff --git a/src/test/compile-fail/block-must-not-have-result-res.rs b/src/test/compile-fail/block-must-not-have-result-res.rs index 4e0a1a542324..d617aba2fdec 100644 --- a/src/test/compile-fail/block-must-not-have-result-res.rs +++ b/src/test/compile-fail/block-must-not-have-result-res.rs @@ -1,4 +1,4 @@ -// error-pattern:this block must not have a result +// error-pattern:mismatched types: expected `()` but found `bool` resource r(i: int) { true diff --git a/src/test/compile-fail/block-must-not-have-result-while.rs b/src/test/compile-fail/block-must-not-have-result-while.rs index d0417fc27eca..7f172998c285 100644 --- a/src/test/compile-fail/block-must-not-have-result-while.rs +++ b/src/test/compile-fail/block-must-not-have-result-while.rs @@ -1,4 +1,4 @@ -// error-pattern:this block must not have a result +// error-pattern:mismatched types: expected `()` but found `bool` fn main() { while true { diff --git a/src/test/compile-fail/forgot-ret.rs b/src/test/compile-fail/forgot-ret.rs index 0f780f1b3358..4e422c970c68 100644 --- a/src/test/compile-fail/forgot-ret.rs +++ b/src/test/compile-fail/forgot-ret.rs @@ -3,6 +3,6 @@ fn god_exists(a: int) -> bool { be god_exists(a); } -fn f(a: int) -> int { if god_exists(a) { ret 5; } } +fn f(a: int) -> int { if god_exists(a) { ret 5; }; } fn main() { f(12); } diff --git a/src/test/compile-fail/if-without-else-result.rs b/src/test/compile-fail/if-without-else-result.rs index 0ff469a871c0..2454f4f37bf6 100644 --- a/src/test/compile-fail/if-without-else-result.rs +++ b/src/test/compile-fail/if-without-else-result.rs @@ -1,4 +1,4 @@ -// error-pattern:`if` without `else` can not produce a result +// error-pattern:mismatched types: expected `()` but found `bool` fn main() { let a = if true { true }; diff --git a/src/test/compile-fail/missing-return2.rs b/src/test/compile-fail/missing-return2.rs index a73db075196c..26a9febd3c78 100644 --- a/src/test/compile-fail/missing-return2.rs +++ b/src/test/compile-fail/missing-return2.rs @@ -3,7 +3,7 @@ fn f() -> int { // Make sure typestate doesn't interpret this alt expression // as the function result - alt true { true { } } + alt true { true { } }; } fn main() { } diff --git a/src/test/compile-fail/native-type-mismatch.rs b/src/test/compile-fail/native-type-mismatch.rs index 381c89c3aa32..d4b8e44cad0c 100644 --- a/src/test/compile-fail/native-type-mismatch.rs +++ b/src/test/compile-fail/native-type-mismatch.rs @@ -1,4 +1,4 @@ -// error-pattern:expected `*Mb` but found `native` +// error-pattern:expected `*u8` but found `native` use std; fn main() unsafe { diff --git a/src/test/pretty/disamb-stmt-expr.rs b/src/test/pretty/disamb-stmt-expr.rs new file mode 100644 index 000000000000..5f9d57f94a73 --- /dev/null +++ b/src/test/pretty/disamb-stmt-expr.rs @@ -0,0 +1,8 @@ +// pp-exact + +// Here we check that the parentheses around the body of `wsucc()` are +// preserved. They are needed to disambiguate `{ret n+1}; - 0` from +// `({ret n+1}-0)`. + +fn wsucc(n: int) -> int { ({ ret n + 1 } - 0); } +fn main() { } diff --git a/src/test/run-pass/block-arg-as-stmt.rs b/src/test/run-pass/block-arg-as-stmt.rs new file mode 100644 index 000000000000..53187408af11 --- /dev/null +++ b/src/test/run-pass/block-arg-as-stmt.rs @@ -0,0 +1,25 @@ + +fn compute1() -> float { + let v = [0f, 1f, 2f, 3f]; + + // Here the "-10f" parses as a second + // statement in tail position: + vec::foldl(0f, v) { |x, y| x + y } - 10f +} + +fn compute2() -> float { + let v = [0f, 1f, 2f, 3f]; + + // Here the ret makes this explicit: + ret vec::foldl(0f, v) { |x, y| x + y } - 10f; +} + +fn main() { + let x = compute1(); + log(debug, x); + assert(x == -10f); + + let y = compute2(); + log(debug, y); + assert(y == -4f); +} diff --git a/src/test/run-pass/block-arg-can-be-followed-by-binop.rs b/src/test/run-pass/block-arg-can-be-followed-by-binop.rs new file mode 100644 index 000000000000..a8e763e96a68 --- /dev/null +++ b/src/test/run-pass/block-arg-can-be-followed-by-binop.rs @@ -0,0 +1,8 @@ +fn main() { + let v = [-1f, 0f, 1f, 2f, 3f]; + + // Trailing expressions require parentheses: + let y = vec::foldl(0f, v) { |x, y| x + y } + 10f; + + assert y == 15f; +} diff --git a/src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs b/src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs new file mode 100644 index 000000000000..8fd797405125 --- /dev/null +++ b/src/test/run-pass/block-arg-can-be-followed-by-block-arg.rs @@ -0,0 +1,6 @@ +fn main() { + fn f(i: block() -> uint) -> uint { i() } + let v = [-1f, 0f, 1f, 2f, 3f]; + let z = vec::foldl(f, v) { |x, _y| x } { || 22u }; + assert z == 22u; +} diff --git a/src/test/run-pass/block-arg-can-be-followed-by-call.rs b/src/test/run-pass/block-arg-can-be-followed-by-call.rs new file mode 100644 index 000000000000..b570a8bdd82b --- /dev/null +++ b/src/test/run-pass/block-arg-can-be-followed-by-call.rs @@ -0,0 +1,6 @@ +fn main() { + fn f(i: uint) -> uint { i } + let v = [-1f, 0f, 1f, 2f, 3f]; + let z = vec::foldl(f, v) { |x, _y| x } (22u); + assert z == 22u; +} diff --git a/src/test/run-pass/block-arg-in-parentheses.rs b/src/test/run-pass/block-arg-in-parentheses.rs new file mode 100644 index 000000000000..205c9144833a --- /dev/null +++ b/src/test/run-pass/block-arg-in-parentheses.rs @@ -0,0 +1,23 @@ +// xfail-test + +// FIXME: Parser doesn't distinguish expression in parentheses (as in +// this example) from one that is not! It is somewhat of a pain to +// fix this though there are no theoretical difficulties. We could +// either add paren to the AST (better for pretty-print, I suppose) or +// modify the parser to track whether the expression in question is +// parenthesized. I did the latter once and it was a bit of pain but +// not terribly difficult. We could also the decision as to whether +// something is an "expression with a value" down into the +// parse_expr() codepath, where we *know* if there are parentheses or +// not, but we'd probably have to be a bit more careful then with +// clearing the top-level restrction flag (which we ought to do +// anyhow!) + +fn main() { + let v = [1f, 2f, 3f]; + let w = + if true { (vec::any(v) { |e| float::nonnegative(e) }) } + else { false }; + assert w; +} + diff --git a/src/test/run-pass/block-arg-in-ternary.rs b/src/test/run-pass/block-arg-in-ternary.rs new file mode 100644 index 000000000000..d48331b3c71c --- /dev/null +++ b/src/test/run-pass/block-arg-in-ternary.rs @@ -0,0 +1,6 @@ +// Allow block arguments with ternary... why not, no chance of ambig. +fn main() { + let v = [-1f, 1f]; + let foo = vec::any(v) { |e| float::negative(e) } ? true : false; + assert foo; +} diff --git a/src/test/run-pass/block-arg.rs b/src/test/run-pass/block-arg.rs new file mode 100644 index 000000000000..6fa2ed5b0f3c --- /dev/null +++ b/src/test/run-pass/block-arg.rs @@ -0,0 +1,52 @@ +// Check usage and precedence of block arguments in expressions: +fn main() { + let v = [-1f, 0f, 1f, 2f, 3f]; + + // Statement form does not require parentheses: + vec::iter(v) { |i| + log(info, i); + } + + // Usable at all: + let any_negative = vec::any(v) { |e| float::negative(e) }; + assert any_negative; + + // Higher precedence than assignments: + any_negative = vec::any(v) { |e| float::negative(e) }; + assert any_negative; + + // Higher precedence than unary operations: + let abs_v = vec::map(v) { |e| float::abs(e) }; + assert vec::all(abs_v) { |e| float::nonnegative(e) }; + assert !vec::any(abs_v) { |e| float::negative(e) }; + + // Usable in funny statement-like forms: + if !vec::any(v) { |e| float::positive(e) } { + assert false; + } + alt vec::all(v) { |e| float::negative(e) } { + true { fail "incorrect answer."; } + false { } + } + alt 3 { + _ when vec::any(v) { |e| float::negative(e) } { + } + _ { + fail "wrong answer."; + } + } + + + // Lower precedence than binary operations: + let w = vec::foldl(0f, v, { |x, y| x + y }) + 10f; + let y = vec::foldl(0f, v) { |x, y| x + y } + 10f; + let z = 10f + vec::foldl(0f, v) { |x, y| x + y }; + assert w == y; + assert y == z; + + // They are not allowed as the tail of a block without parentheses: + let w = + if true { vec::any(abs_v, { |e| float::nonnegative(e) }) } + else { false }; + assert w; +} diff --git a/src/test/run-pass/dupe-first-attr.rc b/src/test/run-pass/dupe-first-attr.rc index 65798b048f90..663bc50fd025 100644 --- a/src/test/run-pass/dupe-first-attr.rc +++ b/src/test/run-pass/dupe-first-attr.rc @@ -8,4 +8,7 @@ mod hello; mod hello; #[cfg(target_os = "win32")] -mod hello; \ No newline at end of file +mod hello; + +#[cfg(target_os = "freebsd")] +mod hello; diff --git a/src/test/run-pass/early-ret-binop-add.rs b/src/test/run-pass/early-ret-binop-add.rs index f7719afc5915..21eca8fefacf 100644 --- a/src/test/run-pass/early-ret-binop-add.rs +++ b/src/test/run-pass/early-ret-binop-add.rs @@ -1,2 +1,2 @@ -fn wsucc(n: int) -> int { { ret n + 1 } + 0; } +fn wsucc(n: int) -> int { ({ ret n + 1 } + 0); } fn main() { } diff --git a/src/test/run-pass/early-ret-binop.rs b/src/test/run-pass/early-ret-binop.rs index ffa6efb4e805..118bec708b88 100644 --- a/src/test/run-pass/early-ret-binop.rs +++ b/src/test/run-pass/early-ret-binop.rs @@ -1,2 +1,2 @@ -fn wsucc(n: int) -> int { { ret n + 1 } == 0; } +fn wsucc(n: int) -> int { ({ ret n + 1 } == 0); } fn main() { } diff --git a/src/test/run-pass/expr-alt-generic-box2.rs b/src/test/run-pass/expr-alt-generic-box2.rs index 53cab4546e22..bc19200e3237 100644 --- a/src/test/run-pass/expr-alt-generic-box2.rs +++ b/src/test/run-pass/expr-alt-generic-box2.rs @@ -4,7 +4,7 @@ // -*- rust -*- type compare = fn@(T, T) -> bool; -fn test_generic(expected: T, eq: compare) { +fn test_generic(expected: T, eq: compare) { let actual: T = alt true { true { expected } }; assert (eq(expected, actual)); } diff --git a/src/test/run-pass/expr-alt-generic-unique1.rs b/src/test/run-pass/expr-alt-generic-unique1.rs index e8b949e9410d..301d56389b12 100644 --- a/src/test/run-pass/expr-alt-generic-unique1.rs +++ b/src/test/run-pass/expr-alt-generic-unique1.rs @@ -3,7 +3,7 @@ // -*- rust -*- type compare = fn@(~T, ~T) -> bool; -fn test_generic(expected: ~T, eq: compare) { +fn test_generic(expected: ~T, eq: compare) { let actual: ~T = alt true { true { expected } }; assert (eq(expected, actual)); } diff --git a/src/test/run-pass/expr-alt-generic-unique2.rs b/src/test/run-pass/expr-alt-generic-unique2.rs index ee0be17ffa26..5640dca0b4eb 100644 --- a/src/test/run-pass/expr-alt-generic-unique2.rs +++ b/src/test/run-pass/expr-alt-generic-unique2.rs @@ -4,7 +4,7 @@ // -*- rust -*- type compare = fn@(T, T) -> bool; -fn test_generic(expected: T, eq: compare) { +fn test_generic(expected: T, eq: compare) { let actual: T = alt true { true { expected } }; assert (eq(expected, actual)); } diff --git a/src/test/run-pass/expr-alt-generic.rs b/src/test/run-pass/expr-alt-generic.rs index c3df90f7fdc3..5a2f7d8b71cd 100644 --- a/src/test/run-pass/expr-alt-generic.rs +++ b/src/test/run-pass/expr-alt-generic.rs @@ -4,7 +4,7 @@ // -*- rust -*- type compare = fn@(T, T) -> bool; -fn test_generic(expected: T, eq: compare) { +fn test_generic(expected: T, eq: compare) { let actual: T = alt true { true { expected } }; assert (eq(expected, actual)); } diff --git a/src/test/run-pass/expr-fn.rs b/src/test/run-pass/expr-fn.rs index 30ba5fa79e4b..5cd3f2a178b2 100644 --- a/src/test/run-pass/expr-fn.rs +++ b/src/test/run-pass/expr-fn.rs @@ -9,7 +9,7 @@ fn test_vec() { } fn test_generic() { - fn f(t: T) -> T { t } + fn f(t: T) -> T { t } assert (f(10) == 10); } diff --git a/src/test/run-pass/iface-generic.rs b/src/test/run-pass/iface-generic.rs new file mode 100644 index 000000000000..07727035a28a --- /dev/null +++ b/src/test/run-pass/iface-generic.rs @@ -0,0 +1,35 @@ +// xfail-pretty + +iface to_str { + fn to_str() -> str; +} +impl of to_str for int { + fn to_str() -> str { int::str(self) } +} +impl of to_str for str { + fn to_str() -> str { self } +} + +iface map { + fn map(f: block(T) -> U) -> [U]; +} +impl of map for [T] { + fn map(f: block(T) -> U) -> [U] { + let r = []; + for x in self { r += [f(x)]; } + r + } +} + +fn foo>(x: T) -> [str] { + x.map({|_e| "hi" }) +} +fn bar>(x: T) -> [str] { + x.map({|_e| _e.to_str() }) +} + +fn main() { + assert foo([1]) == ["hi"]; + assert bar::([4, 5]) == ["4", "5"]; + assert bar::(["x", "y"]) == ["x", "y"]; +} diff --git a/src/test/run-pass/iface-to-str.rs b/src/test/run-pass/iface-to-str.rs new file mode 100644 index 000000000000..cf30cefbd33c --- /dev/null +++ b/src/test/run-pass/iface-to-str.rs @@ -0,0 +1,28 @@ +iface to_str { + fn to_str() -> str; +} + +impl of to_str for int { + fn to_str() -> str { int::str(self) } +} + +impl of to_str for [T] { + fn to_str() -> str { + "[" + str::connect(vec::map(self, {|e| e.to_str()}), ", ") + "]" + } +} + +fn main() { + assert 1.to_str() == "1"; + assert [2, 3, 4].to_str() == "[2, 3, 4]"; + + fn indirect(x: T) -> str { + x.to_str() + "!" + } + assert indirect([10, 20]) == "[10, 20]!"; + + fn indirect2(x: T) -> str { + indirect(x) + } + assert indirect2([1]) == "[1]!"; +} diff --git a/src/test/run-pass/issue-970.rs b/src/test/run-pass/issue-970.rs new file mode 100644 index 000000000000..a19b6ccbc7ba --- /dev/null +++ b/src/test/run-pass/issue-970.rs @@ -0,0 +1,6 @@ +tag maybe_ordered_pair { + yes({low: int, high: int} : less_than(*.low, *.high)); + no; +} +pure fn less_than(x: int, y: int) -> bool { ret x < y; } +fn main() { } diff --git a/src/test/run-pass/last-use-is-capture.rs b/src/test/run-pass/last-use-is-capture.rs new file mode 100644 index 000000000000..1399839a1ed0 --- /dev/null +++ b/src/test/run-pass/last-use-is-capture.rs @@ -0,0 +1,8 @@ +// Make sure #1399 stays fixed + +fn main() { + fn invoke(f: lambda()) { f(); } + let k = ~22; + let _u = {a: k}; + invoke {||log(error, k);} +} diff --git a/src/test/run-pass/non-boolean-pure-fns.rs b/src/test/run-pass/non-boolean-pure-fns.rs index 3a85cba61621..27f3b530154d 100644 --- a/src/test/run-pass/non-boolean-pure-fns.rs +++ b/src/test/run-pass/non-boolean-pure-fns.rs @@ -14,7 +14,10 @@ pure fn nonempty_list(ls: list) -> bool { pure_length(ls) > 0u } // knowledge that ls is a cons node. Future work. // Also, this is pretty contrived since nonempty_list // could be a "tag refinement", if we implement those. -fn safe_head(ls: list) : nonempty_list(ls) -> T { head(ls) } +fn safe_head(ls: list) : nonempty_list(ls) -> T { + check is_not_empty(ls); + ret head(ls); +} fn main() { let mylist = cons(@1u, @nil); diff --git a/src/test/run-pass/unchecked-predicates.rs b/src/test/run-pass/unchecked-predicates.rs index a4c80b575499..035b18f80512 100644 --- a/src/test/run-pass/unchecked-predicates.rs +++ b/src/test/run-pass/unchecked-predicates.rs @@ -1,4 +1,6 @@ // Uses foldl to exhibit the unchecked block syntax. +// TODO: since list's head/tail require the predicate "is_not_empty" now and +// we have unit tests for list, this test might me not necessary anymore? use std; import std::list::*; @@ -6,7 +8,10 @@ import std::list::*; // Can't easily be written as a "pure fn" because there's // no syntax for specifying that f is pure. fn pure_foldl(ls: list, u: U, f: block(T, U) -> U) -> U { - alt ls { nil. { u } cons(hd, tl) { f(hd, pure_foldl(*tl, f(hd, u), f)) } } + alt ls { + nil. { u } + cons(hd, tl) { f(hd, pure_foldl(*tl, f(hd, u), f)) } + } } // Shows how to use an "unchecked" block to call a general @@ -22,7 +27,10 @@ pure fn nonempty_list(ls: list) -> bool { pure_length(ls) > 0u } // knowledge that ls is a cons node. Future work. // Also, this is pretty contrived since nonempty_list // could be a "tag refinement", if we implement those. -fn safe_head(ls: list) : nonempty_list(ls) -> T { head(ls) } +fn safe_head(ls: list) : nonempty_list(ls) -> T { + check is_not_empty(ls); + ret head(ls) +} fn main() { let mylist = cons(@1u, @nil); diff --git a/src/test/run-pass/x86stdcall.rs b/src/test/run-pass/x86stdcall.rs index 765f87de7b29..b9d9b84a526c 100644 --- a/src/test/run-pass/x86stdcall.rs +++ b/src/test/run-pass/x86stdcall.rs @@ -19,4 +19,5 @@ fn main() { #[cfg(target_os = "macos")] #[cfg(target_os = "linux")] +#[cfg(target_os = "freebsd")] fn main() { } diff --git a/src/test/run-pass/x86stdcall2.rs b/src/test/run-pass/x86stdcall2.rs index 6c5093a13299..e58656a73a44 100644 --- a/src/test/run-pass/x86stdcall2.rs +++ b/src/test/run-pass/x86stdcall2.rs @@ -24,4 +24,5 @@ fn main() { #[cfg(target_os = "macos")] #[cfg(target_os = "linux")] +#[cfg(target_os = "freebsd")] fn main() { } diff --git a/src/test/stdtest/char.rs b/src/test/stdtest/char.rs index 1ed61f5632f0..7da2443d6c2c 100644 --- a/src/test/stdtest/char.rs +++ b/src/test/stdtest/char.rs @@ -60,3 +60,19 @@ fn test_to_digit_fail_1() { fn test_to_digit_fail_2() { char::to_digit('$'); } + +#[test] +fn test_to_lower() { + assert (char::to_lower('H') == 'h'); + assert (char::to_lower('e') == 'e'); + //assert (char::to_lower('Ö') == 'ö'); + assert (char::to_lower('ß') == 'ß'); +} + +#[test] +fn test_to_upper() { + assert (char::to_upper('l') == 'L'); + assert (char::to_upper('Q') == 'Q'); + //assert (char::to_upper('ü') == 'Ü'); + assert (char::to_upper('ß') == 'ß'); +} diff --git a/src/test/stdtest/list.rs b/src/test/stdtest/list.rs index 227e8ad299de..b96bc9cd16a5 100644 --- a/src/test/stdtest/list.rs +++ b/src/test/stdtest/list.rs @@ -2,17 +2,38 @@ import core::*; use std; import std::list; -import std::list::head; -import std::list::tail; -import std::list::from_vec; +import std::list::{from_vec, head, is_empty, is_not_empty, tail}; import option; +#[test] +fn test_is_empty() { + let empty : list::list = from_vec([]); + let full1 = from_vec([1]); + let full2 = from_vec(['r', 'u']); + + assert is_empty(empty); + assert !is_empty(full1); + assert !is_empty(full2); + + assert !is_not_empty(empty); + assert is_not_empty(full1); + assert is_not_empty(full2); +} + #[test] fn test_from_vec() { let l = from_vec([0, 1, 2]); + + check is_not_empty(l); assert (head(l) == 0); - assert (head(tail(l)) == 1); - assert (head(tail(tail(l))) == 2); + + let tail_l = tail(l); + check is_not_empty(tail_l); + assert (head(tail_l) == 1); + + let tail_tail_l = tail(tail_l); + check is_not_empty(tail_tail_l); + assert (head(tail_tail_l) == 2); } #[test] @@ -24,9 +45,17 @@ fn test_from_vec_empty() { #[test] fn test_from_vec_mut() { let l = from_vec([mutable 0, 1, 2]); + + check is_not_empty(l); assert (head(l) == 0); - assert (head(tail(l)) == 1); - assert (head(tail(tail(l))) == 2); + + let tail_l = tail(l); + check is_not_empty(tail_l); + assert (head(tail_l) == 1); + + let tail_tail_l = tail(tail_l); + check is_not_empty(tail_tail_l); + assert (head(tail_tail_l) == 2); } #[test] diff --git a/src/test/stdtest/os.rs b/src/test/stdtest/os.rs index 7f7b2c4f3b39..fd773063e8d0 100644 --- a/src/test/stdtest/os.rs +++ b/src/test/stdtest/os.rs @@ -14,6 +14,7 @@ fn test_setenv() { } #[test] +#[ignore(reason = "fails periodically on mac")] fn test_setenv_overwrite() { setenv("NAME2", "1"); setenv("NAME2", "2"); @@ -23,12 +24,14 @@ fn test_setenv_overwrite() { // Windows GetEnvironmentVariable requires some extra work to make sure // the buffer the variable is copied into is the right size #[test] +#[ignore(reason = "fails periodically on mac")] fn test_getenv_big() { let s = ""; let i = 0; while i < 100 { s += "aaaaaaaaaa"; i += 1; } - setenv("NAME3", s); - assert (getenv("NAME3") == option::some(s)); + setenv("test_getenv_big", s); + log(debug, s); + assert (getenv("test_getenv_big") == option::some(s)); } #[test] diff --git a/src/test/stdtest/str.rs b/src/test/stdtest/str.rs index c3f0e53bac59..39217fb8a8b5 100644 --- a/src/test/stdtest/str.rs +++ b/src/test/stdtest/str.rs @@ -287,10 +287,10 @@ fn unsafe_from_bytes() { } #[test] -fn str_from_cstr() unsafe { +fn from_cstr() unsafe { let a = [65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 65u8, 0u8]; let b = vec::to_ptr(a); - let c = str::str_from_cstr(b); + let c = str::from_cstr(b); assert (c == "AAAAAAA"); } @@ -312,7 +312,7 @@ fn as_buf_small() unsafe { fn as_buf2() unsafe { let s = "hello"; let sb = str::as_buf(s, {|b| b }); - let s_cstr = str::str_from_cstr(sb); + let s_cstr = str::from_cstr(sb); assert (str::eq(s_cstr, s)); } diff --git a/src/test/stdtest/uv.rs b/src/test/stdtest/uv.rs index 5529ee7a2bbf..a7f6e0a8f157 100644 --- a/src/test/stdtest/uv.rs +++ b/src/test/stdtest/uv.rs @@ -1,6 +1,7 @@ #[cfg(target_os = "linux")]; #[cfg(target_os = "macos")]; +#[cfg(target_os = "freebsd")]; import core::*; @@ -44,4 +45,4 @@ mod test_ref { uv::loop_delete(loop); */ } -} \ No newline at end of file +}