* `g++` 4.7 or later or `clang++` 3.x
* `python` 2.7 (but not 3.x)
* GNU `make` 3.81 or later
+ * `cmake` 2.8.8 or later
* `curl`
* `git`
probe CFG_GDB gdb
probe CFG_LLDB lldb
+# For building LLVM
+probe_need CFG_CMAKE cmake
+
# On MacOS X, invoking `javac` pops up a dialog if the JDK is not
# installed. Since `javac` is only used if `antlr4` is available,
# probe for it only in this case.
"
fi
- # MSVC requires cmake because that's how we're going to build LLVM
- probe_need CFG_CMAKE cmake
-
# There are three builds of cmake on windows: MSVC, MinGW and Cygwin
# The Cygwin build does not have generators for Visual Studio, so
# detect that here and error.
elif [ -z $CFG_LLVM_ROOT ]
then
LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm
- if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]
- then
- LLVM_DBG_OPTS="--enable-debug-symbols --disable-optimized"
- # Just use LLVM straight from its build directory to
- # avoid 'make install' time
- LLVM_INST_DIR=$LLVM_BUILD_DIR/Debug
- else
- LLVM_DBG_OPTS="--enable-optimized"
- LLVM_INST_DIR=$LLVM_BUILD_DIR/Release
- fi
- if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ]
- then
- LLVM_ASSERTION_OPTS="--disable-assertions"
- else
- LLVM_ASSERTION_OPTS="--enable-assertions"
-
- # Apparently even if we request assertions be enabled for MSVC,
- # LLVM's CMake build system ignore this and outputs in `Release`
- # anyway.
- if [ ${is_msvc} -eq 0 ]; then
- LLVM_INST_DIR=${LLVM_INST_DIR}+Asserts
+ LLVM_INST_DIR=$LLVM_BUILD_DIR
+ # For some crazy reason the MSVC output dir is different than Unix
+ if [ ${is_msvc} -ne 0 ]; then
+ if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]
+ then
+ # Just use LLVM straight from its build directory to
+ # avoid 'make install' time
+ LLVM_INST_DIR=$LLVM_BUILD_DIR/Debug
+ else
+ LLVM_INST_DIR=$LLVM_BUILD_DIR/Release
fi
fi
else
err "can only build LLVM for x86 platforms"
;;
esac
- CFG_CMAKE_GENERATOR=$generator
- putvar CFG_CMAKE_GENERATOR
- fi
-
- if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -ne 0 ]
- then
- msg "configuring LLVM for $t with cmake"
-
- CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF"
- if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then
- CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"
- else
- CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release"
- fi
- if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ]
- then
- CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF"
- else
- CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON"
- fi
-
- msg "configuring LLVM with:"
- msg "$CMAKE_ARGS"
-
- (cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \
- -G "$CFG_CMAKE_GENERATOR" \
- $CMAKE_ARGS)
- need_ok "LLVM cmake configure failed"
+ else
+ generator="Unix Makefiles"
fi
-
- if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -eq 0 ]
- then
- # LLVM's configure doesn't recognize the new Windows triples yet
- gnu_t=$(to_gnu_triple $t)
-
- msg "configuring LLVM for $gnu_t"
-
- LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc"
- LLVM_BUILD="--build=$gnu_t"
- LLVM_HOST="--host=$gnu_t"
- LLVM_TARGET="--target=$gnu_t"
-
- # Disable unused LLVM features
- LLVM_OPTS="$LLVM_DBG_OPTS $LLVM_ASSERTION_OPTS --disable-docs --enable-bindings=none"
- # Disable term-info, linkage of which comes in multiple forms,
- # making our snapshots incompatible (#9334)
- LLVM_OPTS="$LLVM_OPTS --disable-terminfo"
- # Try to have LLVM pull in as few dependencies as possible (#9397)
- LLVM_OPTS="$LLVM_OPTS --disable-zlib --disable-libffi"
-
- # Use win32 native thread/lock apis instead of pthread wrapper.
- # (llvm's configure tries to find pthread first, so we have to disable it explicitly.)
- # Also note that pthreads works badly on mingw-w64 systems: #8996
- case "$CFG_BUILD" in
- (*-windows-gnu)
- LLVM_OPTS="$LLVM_OPTS --disable-pthreads"
- ;;
- esac
-
- case "$CFG_CC" in
- ("ccache clang")
- LLVM_CXX_32="ccache clang++ -Qunused-arguments"
- LLVM_CC_32="ccache clang -Qunused-arguments"
-
- LLVM_CXX_64="ccache clang++ -Qunused-arguments"
- LLVM_CC_64="ccache clang -Qunused-arguments"
+ CFG_CMAKE_GENERATOR=$generator
+ putvar CFG_CMAKE_GENERATOR
+
+ msg "configuring LLVM for $t"
+
+ LLVM_CFLAGS_32=""
+ LLVM_CXXFLAGS_32=""
+ LLVM_LDFLAGS_32=""
+ LLVM_CFLAGS_64=""
+ LLVM_CXXFLAGS_64=""
+ LLVM_LDFLAGS_64=""
+
+ case "$CFG_CC" in
+ ("ccache clang")
+ LLVM_CXX_32="ccache"
+ LLVM_CC_32="ccache"
+ LLVM_CXX_32_ARG1="clang++"
+ LLVM_CC_32_ARG1="clang"
+ LLVM_CFLAGS_32="-Qunused-arguments"
+ LLVM_CXXFLAGS_32="-Qunused-arguments"
+
+ LLVM_CXX_64="ccache"
+ LLVM_CC_64="ccache"
+ LLVM_CXX_64_ARG1="clang++"
+ LLVM_CC_64_ARG1="clang"
+ LLVM_CFLAGS_64="-Qunused-arguments"
+ LLVM_CXXFLAGS_64="-Qunused-arguments"
;;
- ("clang")
- LLVM_CXX_32="clang++ -Qunused-arguments"
- LLVM_CC_32="clang -Qunused-arguments"
-
- LLVM_CXX_64="clang++ -Qunused-arguments"
- LLVM_CC_64="clang -Qunused-arguments"
+ ("clang")
+ LLVM_CXX_32="clang++"
+ LLVM_CC_32="clang"
+ LLVM_CFLAGS_32="-Qunused-arguments"
+ LLVM_CXXFLAGS_32="-Qunused-arguments"
+
+ LLVM_CXX_64="clang++"
+ LLVM_CC_64="clang"
+ LLVM_CFLAGS_64="-Qunused-arguments"
+ LLVM_CXXFLAGS_64="-Qunused-arguments"
;;
- ("ccache gcc")
- LLVM_CXX_32="ccache g++"
- LLVM_CC_32="ccache gcc"
-
- LLVM_CXX_64="ccache g++"
- LLVM_CC_64="ccache gcc"
+ ("ccache gcc")
+ LLVM_CXX_32="ccache"
+ LLVM_CC_32="ccache"
+ LLVM_CXX_32_ARG1="clang++"
+ LLVM_CC_32_ARG1="clang"
+
+ LLVM_CXX_64="ccache"
+ LLVM_CC_64="ccache"
+ LLVM_CXX_64_ARG1="g++"
+ LLVM_CC_64_ARG1="gcc"
;;
- ("gcc")
+ ("gcc")
LLVM_CXX_32="g++"
LLVM_CC_32="gcc"
LLVM_CC_64="gcc"
;;
- (*)
+ (*)
msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC"
if [ -n "$CFG_ENABLE_CCACHE" ]
then
err "ccache requested but not found"
fi
- LLVM_CXX_32="ccache $CXX"
- LLVM_CC_32="ccache $CC"
+ LLVM_CXX_32="ccache"
+ LLVM_CC_32="ccache"
+ LLVM_CXX_32_ARG1="$CXX"
+ LLVM_CC_32_ARG1="$CC"
- LLVM_CXX_64="ccache $CXX"
- LLVM_CC_64="ccache $CC"
+ LLVM_CXX_64="ccache"
+ LLVM_CC_64="ccache"
+ LLVM_CXX_64_ARG1="$CXX"
+ LLVM_CC_64_ARG1="$CC"
else
LLVM_CXX_32="$CXX"
LLVM_CC_32="$CC"
fi
;;
- esac
+ esac
- case "$CFG_CPUTYPE" in
- (x86*)
- LLVM_CXX_32="$LLVM_CXX_32 -m32"
- LLVM_CC_32="$LLVM_CC_32 -m32"
+ case "$CFG_CPUTYPE" in
+ (x86*)
+ LLVM_CFLAGS_32="$LLVM_CFLAGS_32 -m32"
+ LLVM_CXXFLAGS_32="$LLVM_CXXFLAGS_32 -m32"
+ LLVM_LDFLAGS_32="$LLVM_LDFLAGS_32 -m32"
+ ;;
+ esac
- LLVM_CFLAGS_32="-m32"
- LLVM_CXXFLAGS_32="-m32"
- LLVM_LDFLAGS_32="-m32"
+ if echo $t | grep -q x86_64
+ then
+ LLVM_CXX=$LLVM_CXX_64
+ LLVM_CC=$LLVM_CC_64
+ LLVM_CXX_ARG1=$LLVM_CXX_64_ARG1
+ LLVM_CC_ARG1=$LLVM_CC_64_ARG1
+ LLVM_CFLAGS=$LLVM_CFLAGS_64
+ LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64
+ LLVM_LDFLAGS=$LLVM_LDFLAGS_64
+ else
+ LLVM_CXX=$LLVM_CXX_32
+ LLVM_CC=$LLVM_CC_32
+ LLVM_CXX_ARG1=$LLVM_CXX_32_ARG1
+ LLVM_CC_ARG1=$LLVM_CC_32_ARG1
+ LLVM_CFLAGS=$LLVM_CFLAGS_32
+ LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32
+ LLVM_LDFLAGS=$LLVM_LDFLAGS_32
+ fi
- LLVM_CFLAGS_64=""
- LLVM_CXXFLAGS_64=""
- LLVM_LDFLAGS_64=""
+ if [ "$CFG_USING_LIBCPP" != "0" ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_LIBCXX=ON"
+ fi
- LLVM_CXX_32="$LLVM_CXX_32 -m32"
- LLVM_CC_32="$LLVM_CC_32 -m32"
- ;;
+ # Turn off things we don't need
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_INCLUDE_TESTS=OFF"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_INCLUDE_EXAMPLES=OFF"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_INCLUDE_DOCS=OFF"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ZLIB=OFF"
+ CMAKE_ARGS="$CMAKE_ARGS -DWITH_POLY=OFF"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_TERMINFO=OFF"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_LIBEDIT=OFF"
- (*)
- LLVM_CFLAGS_32=""
- LLVM_CXXFLAGS_32=""
- LLVM_LDFLAGS_32=""
+ arch="$(echo "$t" | cut -d - -f 1)"
- LLVM_CFLAGS_64=""
- LLVM_CXXFLAGS_64=""
- LLVM_LDFLAGS_64=""
- ;;
- esac
+ if [ "$arch" = i686 ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_BUILD_32_BITS=ON"
+ fi
- if echo $t | grep -q x86_64
- then
- LLVM_CXX=$LLVM_CXX_64
- LLVM_CC=$LLVM_CC_64
- LLVM_CFLAGS=$LLVM_CFLAGS_64
- LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64
- LLVM_LDFLAGS=$LLVM_LDFLAGS_64
- else
- LLVM_CXX=$LLVM_CXX_32
- LLVM_CC=$LLVM_CC_32
- LLVM_CFLAGS=$LLVM_CFLAGS_32
- LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32
- LLVM_LDFLAGS=$LLVM_LDFLAGS_32
+ if [ "$t" != "$CFG_BUILD" ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CROSSCOMPILING=True"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_TARGET_ARCH=$arch"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_TABLEGEN=$LLVM_INST_DIR/bin/llvm-tablegen"
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_DEFAULT_TARGET_TRIPLE=$t"
+ fi
+
+ # MSVC handles compiler business itself
+ if [ ${is_msvc} -eq 0 ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_C_COMPILER=$LLVM_CC"
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CXX_COMPILER=$LLVM_CXX"
+ CMAKE_ARGS="$CMAKE_ARGS '-DCMAKE_C_FLAGS=$LLVM_CFLAGS'"
+ CMAKE_ARGS="$CMAKE_ARGS '-DCMAKE_CXX_FLAGS=$LLVM_CXXFLAGS'"
+ if [ -n "$LLVM_CC_ARG1" ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_C_COMPILER_ARG1=$LLVM_CC_ARG1"
fi
+ if [ -n "$LLVM_CXX_ARG1" ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CXX_COMPILER_ARG1=$LLVM_CXX_ARG1"
+ fi
+ # FIXME: What about LDFLAGS?
+ fi
- CXX=$LLVM_CXX
- CC=$LLVM_CC
- CFLAGS="$CFLAGS $LLVM_CFLAGS"
- CXXFLAGS="$CXXFLAGS $LLVM_CXXFLAGS"
- LDFLAGS="$LDFLAGS $LLVM_LDFLAGS"
+ if [ -n "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug"
+ else
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release"
+ fi
+ if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ]
+ then
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF"
+ else
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON"
+ fi
- if [ "$CFG_USING_LIBCPP" != "0" ]; then
- LLVM_OPTS="$LLVM_OPTS --enable-libcpp"
- fi
+ CMAKE_ARGS="$CMAKE_ARGS -DLLVM_TARGETS_TO_BUILD='X86;ARM;AArch64;Mips;PowerPC'"
+ CMAKE_ARGS="$CMAKE_ARGS -G '$CFG_CMAKE_GENERATOR'"
+ CMAKE_ARGS="$CMAKE_ARGS $CFG_LLVM_SRC_DIR"
- LLVM_FLAGS="$LLVM_TARGETS $LLVM_OPTS $LLVM_BUILD \
- $LLVM_HOST $LLVM_TARGET --with-python=$CFG_PYTHON"
+ if [ ${do_reconfigure} -ne 0 ]
+ then
+ msg "configuring LLVM for $t with cmake"
msg "configuring LLVM with:"
- msg "$LLVM_FLAGS"
-
- export CXX
- export CC
- export CFLAGS
- export CXXFLAGS
- export LDFLAGS
-
- cd $LLVM_BUILD_DIR
- case $CFG_SRC_DIR in
- /* | [a-z]:* | [A-Z]:*)
- ${CFG_LLVM_SRC_DIR}configure $LLVM_FLAGS
- ;;
- *)
- ${CFG_BUILD_DIR}${CFG_LLVM_SRC_DIR}configure \
- $LLVM_FLAGS
- ;;
- esac
- need_ok "LLVM configure failed"
+ msg "$CMAKE_ARGS"
- cd $CFG_BUILD_DIR
+ (cd $LLVM_BUILD_DIR && eval "$CFG_CMAKE" $CMAKE_ARGS)
+ need_ok "LLVM cmake configure failed"
fi
# Construct variables for LLVM build and install directories for
# i686-unknown-linux-musl configuration
CC_i686-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc
-CXX_i686-unknown-linux-musl=notaprogram
+CXX_i686-unknown-linux-musl=$(CXX)
CPP_i686-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc -E
AR_i686-unknown-linux-musl=$(AR)
CFG_INSTALL_ONLY_RLIB_i686-unknown-linux-musl = 1
# x86_64-unknown-linux-musl configuration
CC_x86_64-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc
-CXX_x86_64-unknown-linux-musl=notaprogram
+CXX_x86_64-unknown-linux-musl=$(CXX)
CPP_x86_64-unknown-linux-musl=$(CFG_MUSL_ROOT)/bin/musl-gcc -E
AR_x86_64-unknown-linux-musl=$(AR)
CFG_INSTALL_ONLY_RLIB_x86_64-unknown-linux-musl = 1
LLVM_STAMP_$(1) = $$(CFG_LLVM_BUILD_DIR_$(1))/llvm-auto-clean-stamp
-ifeq ($$(findstring msvc,$(1)),msvc)
-
$$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1))
@$$(call E, cmake: llvm)
+ifeq ($$(findstring msvc,$(1)),msvc)
$$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \
--config $$(LLVM_BUILD_CONFIG_MODE)
- $$(Q)touch $$(LLVM_CONFIG_$(1))
-
-clean-llvm$(1):
-
else
-
-$$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1))
- @$$(call E, make: llvm)
- $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) $$(CFG_LLVM_BUILD_ENV_$(1)) ONLY_TOOLS="$$(LLVM_TOOLS)"
+ $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1))
+endif
$$(Q)touch $$(LLVM_CONFIG_$(1))
clean-llvm$(1):
- $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) clean
-
-endif
else
clean-llvm$(1):
$(S)src/compiler-rt/*/*/*/*)
endif
+# compiler-rt's build system is a godawful mess. Here we figure out
+# the ridiculous platform-specific values and paths necessary to get
+# useful artifacts out of it.
+
COMPRT_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt)
COMPRT_LIB_$(1) := $$(RT_OUTPUT_DIR_$(1))/$$(COMPRT_NAME_$(1))
COMPRT_BUILD_DIR_$(1) := $$(RT_OUTPUT_DIR_$(1))/compiler-rt
-ifeq ($$(findstring msvc,$(1)),msvc)
-$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD))
- @$$(call E, cmake: compiler-rt)
- $$(Q)cd "$$(COMPRT_BUILD_DIR_$(1))"; $$(CFG_CMAKE) "$(S)src/compiler-rt" \
- -DCMAKE_BUILD_TYPE=$$(LLVM_BUILD_CONFIG_MODE) \
- -DLLVM_CONFIG_PATH=$$(LLVM_CONFIG_$$(CFG_BUILD)) \
- -G"$$(CFG_CMAKE_GENERATOR)"
- $$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \
- --target lib/builtins/builtins \
- --config $$(LLVM_BUILD_CONFIG_MODE) \
- -- //v:m //nologo
- $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/lib/windows/$$(LLVM_BUILD_CONFIG_MODE)/clang_rt.builtins-$$(HOST_$(1)).lib $$@
+COMPRT_ARCH_$(1) := $$(word 1,$$(subst -, ,$(1)))
+
+# All this is to figure out the path to the compiler-rt bin
+ifeq ($$(findstring windows-msvc,$(1)),windows-msvc)
+COMPRT_DIR_$(1) := windows/Release
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-$$(patsubst i%86,i386,$$(COMPRT_ARCH_$(1)))
+endif
+
+ifeq ($$(findstring windows-gnu,$(1)),windows-gnu)
+COMPRT_DIR_$(1) := windows
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-$$(COMPRT_ARCH_$(1))
+endif
+
+ifeq ($$(findstring darwin,$(1)),darwin)
+COMPRT_DIR_$(1) := builtins
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins_$$(patsubst i686,i386,$$(COMPRT_ARCH_$(1)))_osx
+endif
+
+ifeq ($$(findstring ios,$(1)),ios)
+COMPRT_DIR_$(1) := builtins
+COMPRT_ARCH_$(1) := $$(patsubst armv7s,armv7em,$$(COMPRT_ARCH_$(1)))
+COMPRT_LIB_NAME_$(1) := clang_rt.hard_pic_$$(COMPRT_ARCH_$(1))_macho_embedded
+ifeq ($$(COMPRT_ARCH_$(1)),aarch64)
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins_arm64_ios
+endif
+COMPRT_DEFINES_$(1) := -DCOMPILER_RT_ENABLE_IOS=ON
+endif
+
+ifndef COMPRT_DIR_$(1)
+# NB: FreeBSD and NetBSD output to "linux"...
+COMPRT_DIR_$(1) := linux
+COMPRT_ARCH_$(1) := $$(patsubst i586,i386,$$(COMPRT_ARCH_$(1)))
+
+ifeq ($$(findstring android,$(1)),android)
+ifeq ($$(findstring arm,$$(COMPRT_ARCH_$(1))),arm)
+COMPRT_ARCH_$(1) := armhf
+endif
+endif
+
+ifeq ($$(findstring eabihf,$(1)),eabihf)
+ifeq ($$(findstring armv7,$(1)),)
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-armhf
+endif
+endif
+
+ifndef COMPRT_LIB_NAME_$(1)
+COMPRT_LIB_NAME_$(1) := clang_rt.builtins-$$(COMPRT_ARCH_$(1))
+endif
+endif
+
+
+ifeq ($$(findstring windows-gnu,$(1)),windows-gnu)
+COMPRT_LIB_FILE_$(1) := lib$$(COMPRT_LIB_NAME_$(1)).a
+endif
+
+ifeq ($$(findstring android,$(1)),android)
+ifeq ($$(findstring arm,$(1)),arm)
+COMPRT_LIB_FILE_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$$(COMPRT_LIB_NAME_$(1))-android)
+endif
+endif
+
+ifndef COMPRT_LIB_FILE_$(1)
+COMPRT_LIB_FILE_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$$(COMPRT_LIB_NAME_$(1)))
+endif
+
+COMPRT_OUTPUT_$(1) := $$(COMPRT_BUILD_DIR_$(1))/lib/$$(COMPRT_DIR_$(1))/$$(COMPRT_LIB_FILE_$(1))
+
+ifeq ($$(findstring windows-msvc,$(1)),windows-msvc)
+COMPRT_BUILD_ARGS_$(1) := //v:m //nologo
+COMPRT_BUILD_TARGET_$(1) := lib/builtins/builtins
+COMPRT_BUILD_CC_$(1) :=
else
-COMPRT_CC_$(1) := $$(CC_$(1))
-COMPRT_AR_$(1) := $$(AR_$(1))
-# We chomp -Werror here because GCC warns about the type signature of
-# builtins not matching its own and the build fails. It's a bit hacky,
-# but what can we do, we're building libclang-rt using GCC ......
-COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -Wno-error -std=c99
-
-# FreeBSD Clang's packaging is problematic; it doesn't copy unwind.h to
-# the standard include directory. This should really be in our changes to
-# compiler-rt, but we override the CFLAGS here so there isn't much choice
-ifeq ($$(findstring freebsd,$(1)),freebsd)
- COMPRT_CFLAGS_$(1) += -I/usr/include/c++/v1
+COMPRT_BUILD_ARGS_$(1) :=
+ifndef COMPRT_BUILD_TARGET_$(1)
+COMPRT_BUILD_TARGET_$(1) := $$(COMPRT_LIB_NAME_$(1))
+endif
+COMPRT_BUILD_CC_$(1) := -DCMAKE_C_COMPILER=$$(call FIND_COMPILER,$$(CC_$(1))) \
+ -DCMAKE_CXX_COMPILER=$$(call FIND_COMPILER,$$(CXX_$(1)))
+
+ifeq ($$(findstring ios,$(1)),)
+COMPRT_BUILD_CC_$(1) := $$(COMPRT_BUILD_CC_$(1)) \
+ -DCMAKE_C_FLAGS="$$(CFG_GCCISH_CFLAGS_$(1)) -Wno-error"
+endif
+
endif
ifeq ($$(findstring emscripten,$(1)),emscripten)
else
-$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS)
- @$$(call E, make: compiler-rt)
- $$(Q)$$(MAKE) -C "$(S)src/compiler-rt" \
- ProjSrcRoot="$(S)src/compiler-rt" \
- ProjObjRoot="$$(abspath $$(COMPRT_BUILD_DIR_$(1)))" \
- CC='$$(COMPRT_CC_$(1))' \
- AR='$$(COMPRT_AR_$(1))' \
- RANLIB='$$(COMPRT_AR_$(1)) s' \
- CFLAGS="$$(COMPRT_CFLAGS_$(1))" \
- TargetTriple=$(1) \
- triple-builtins
- $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$@
-
-endif # if emscripten
+$$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD))
+ @$$(call E, cmake: compiler-rt)
+ $$(Q)rm -rf $$(COMPRT_BUILD_DIR_$(1))
+ $$(Q)mkdir $$(COMPRT_BUILD_DIR_$(1))
+ $$(Q)cd "$$(COMPRT_BUILD_DIR_$(1))"; \
+ $$(CFG_CMAKE) "$(S)src/compiler-rt" \
+ -DCMAKE_BUILD_TYPE=$$(LLVM_BUILD_CONFIG_MODE) \
+ -DLLVM_CONFIG_PATH=$$(LLVM_CONFIG_$$(CFG_BUILD)) \
+ -DCOMPILER_RT_DEFAULT_TARGET_TRIPLE=$(1) \
+ -DCOMPILER_RT_BUILD_SANITIZERS=OFF \
+ -DCOMPILER_RT_BUILD_EMUTLS=OFF \
+ $$(COMPRT_DEFINES_$(1)) \
+ $$(COMPRT_BUILD_CC_$(1)) \
+ -G"$$(CFG_CMAKE_GENERATOR)"
+ $$(Q)$$(CFG_CMAKE) --build "$$(COMPRT_BUILD_DIR_$(1))" \
+ --target $$(COMPRT_BUILD_TARGET_$(1)) \
+ --config $$(LLVM_BUILD_CONFIG_MODE) \
+ -- $$(COMPRT_BUILD_ARGS_$(1)) $$(MFLAGS)
+ $$(Q)cp "$$(COMPRT_OUTPUT_$(1))" $$@
+
endif
################################################################################
$$(BACKTRACE_LIB_$(1)):
touch $$@
-else
-ifeq ($$(findstring ios,$$(OSTYPE_$(1))),ios)
+else ifeq ($$(findstring ios,$$(OSTYPE_$(1))),ios)
# See comment above
$$(BACKTRACE_LIB_$(1)):
touch $$@
-else
-
-ifeq ($$(findstring msvc,$(1)),msvc)
+else ifeq ($$(findstring msvc,$(1)),msvc)
# See comment above
$$(BACKTRACE_LIB_$(1)):
touch $$@
-else
-
-ifeq ($$(findstring emscripten,$(1)),emscripten)
+else ifeq ($$(findstring emscripten,$(1)),emscripten)
# FIXME: libbacktrace doesn't understand the emscripten triple
$$(BACKTRACE_LIB_$(1)):
touch $$@
INCDIR=$(S)src/libbacktrace
$$(Q)cp $$(BACKTRACE_BUILD_DIR_$(1))/.libs/libbacktrace.a $$@
-endif # endif for emscripten
-endif # endif for msvc
-endif # endif for ios
-endif # endif for darwin
+endif
################################################################################
# libc/libunwind for musl
1. The entry point script, `src/bootstrap/bootstrap.py` is run. This script is
responsible for downloading the stage0 compiler/Cargo binaries, and it then
compiles the build system itself (this folder). Finally, it then invokes the
- actual `boostrap` binary build system.
+ actual `bootstrap` binary build system.
2. In Rust, `bootstrap` will slurp up all configuration, perform a number of
sanity checks (compilers exist for example), and then start building the
stage0 artifacts.
-Subproject commit 57315f7e07d09b6f0341ebbcd50dded6c20d782f
+Subproject commit a1ef94b76029780a510bc2dc9c6a791bd091ff19
ignore the middle element.
It’s worth noting that using `_` never binds the value in the first place,
-which means a value may not move:
+which means that the value does not move:
```rust
let tuple: (u32, String) = (5, String::from("five"));
RUN apt-get update && apt-get -y install \
curl g++ gdb git make \
libedit-dev zlib1g-dev \
- llvm-3.7-tools
+ llvm-3.7-tools cmake
+
+# When we compile compiler-rt we pass it the llvm-config we just installed on
+# the system, but unfortunately it doesn't infer correctly where
+# LLVMConfig.cmake is so we need to coerce it a bit...
+RUN mkdir -p /usr/lib/llvm-3.7/build/share/llvm
+RUN ln -s /usr/share/llvm-3.7/cmake /usr/lib/llvm-3.7/build/share/llvm/cmake
RUN mkdir /build
WORKDIR /build
#![feature(specialization)]
#![feature(staged_api)]
#![feature(step_by)]
-#![feature(str_char)]
#![feature(unboxed_closures)]
#![feature(unicode)]
#![feature(unique)]
pub use core::str::{FromStr, Utf8Error};
#[allow(deprecated)]
#[stable(feature = "rust1", since = "1.0.0")]
-pub use core::str::{Lines, LinesAny, CharRange};
+pub use core::str::{Lines, LinesAny};
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::str::{Split, RSplit};
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
fn count(self) -> usize {
- self.size_hint().0
+ self.len()
}
}
for (i, c) in self.char_indices() {
let esc = c.escape_default();
// If char needs escaping, flush backlog so far and write, else skip
- if esc.size_hint() != (1, Some(1)) {
+ if esc.len() != 1 {
f.write_str(&self[from..i])?;
for c in esc {
f.write_char(c)?;
#[inline]
fn count(self) -> usize {
- self.size_hint().0
+ self.len()
}
#[inline]
#[inline]
fn count(self) -> usize {
- self.size_hint().0
+ self.len()
}
#[inline]
#[inline]
fn count(self) -> usize {
- self.size_hint().0
+ self.len()
}
#[inline]
#[inline]
fn count(self) -> usize {
- self.size_hint().0
+ self.len()
}
#[inline]
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
- let (len, _) = self.iter.size_hint();
+ let len = self.iter.len();
// `(len + 3)` can't overflow, because we know that the `slice::Iter`
// belongs to a slice in memory which has a maximum length of
// `isize::MAX` (that's well below `usize::MAX`).
#[inline]
fn next(&mut self) -> Option<(usize, char)> {
- let (pre_len, _) = self.iter.iter.size_hint();
+ let pre_len = self.iter.iter.len();
match self.iter.next() {
None => None,
Some(ch) => {
let index = self.front_offset;
- let (len, _) = self.iter.iter.size_hint();
+ let len = self.iter.iter.len();
self.front_offset += pre_len - len;
Some((index, ch))
}
match self.iter.next_back() {
None => None,
Some(ch) => {
- let (len, _) = self.iter.iter.size_hint();
- let index = self.front_offset + len;
+ let index = self.front_offset + self.iter.iter.len();
Some((index, ch))
}
}
4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0, // 0xFF
];
-/// Struct that contains a `char` and the index of the first byte of
-/// the next `char` in a string. This can be used as a data structure
-/// for iterating over the UTF-8 bytes of a string.
-#[derive(Copy, Clone, Debug)]
-#[unstable(feature = "str_char",
- reason = "existence of this struct is uncertain as it is frequently \
- able to be replaced with char.len_utf8() and/or \
- char/char_indices iterators",
- issue = "27754")]
-pub struct CharRange {
- /// Current `char`
- pub ch: char,
- /// Index of the first byte of the next `char`
- pub next: usize,
-}
-
/// Mask of the value bits of a continuation byte
const CONT_MASK: u8 = 0b0011_1111;
/// Value of the tag bits (tag mask is !CONT_MASK) of a continuation byte
let s = &mut self.char_indices;
// Compare lengths of the internal byte slice iterator
// to find length of current char
- let (pre_len, _) = s.iter.iter.size_hint();
+ let pre_len = s.iter.iter.len();
if let Some((i, c)) = s.next() {
- let (len, _) = s.iter.iter.size_hint();
+ let len = s.iter.iter.len();
let char_len = pre_len - len;
if self.char_eq.matches(c) {
return SearchStep::Match(i, i + char_len);
let s = &mut self.char_indices;
// Compare lengths of the internal byte slice iterator
// to find length of current char
- let (pre_len, _) = s.iter.iter.size_hint();
+ let pre_len = s.iter.iter.len();
if let Some((i, c)) = s.next_back() {
- let (len, _) = s.iter.iter.size_hint();
+ let len = s.iter.iter.len();
let char_len = pre_len - len;
if self.char_eq.matches(c) {
return SearchStep::Match(i, i + char_len);
Consider the following erroneous definition of a type for a list of bytes:
-```compile_fail
+```compile_fail,E0072
// error, invalid recursive struct type
struct ListNode {
head: u8,
You tried to give a type parameter to a type which doesn't need it. Erroneous
code example:
-```compile_fail
+```compile_fail,E0109
type X = u32<i32>; // error: type parameters are not allowed on this type
```
You tried to give a lifetime parameter to a type which doesn't need it.
Erroneous code example:
-```compile_fail
+```compile_fail,E0110
type X = u32<'static>; // error: lifetime parameters are not allowed on
// this type
```
Erroneous code example:
-```compile_fail
+```compile_fail,E0133
unsafe fn f() { return; } // This is the unsafe code
fn main() {
Erroneous code example:
-```compile_fail
+```compile_fail,E0137
#![feature(main)]
#[main]
Erroneous code example:
-```compile_fail
+```compile_fail,E0138
#![feature(start)]
#[start]
```
"##,
-// FIXME link this to the relevant turpl chapters for instilling fear of the
-// transmute gods in the user
+// isn't thrown anymore
E0139: r##"
There are various restrictions on transmuting between types in Rust; for example
types being transmuted must have the same size. To apply all these restrictions,
So, for example, the following is not allowed:
-```compile_fail
+```
+use std::mem::transmute;
+
struct Foo<T>(Vec<T>);
fn foo<T>(x: Vec<T>) {
- // we are transmuting between Vec<T> and Foo<T> here
+ // we are transmuting between Vec<T> and Foo<F> here
let y: Foo<T> = unsafe { transmute(x) };
// do something with y
}
Erroneous code example:
-```compile_fail
+```compile_fail,E0152
#![feature(lang_items)]
#[lang = "panic_fmt"]
An associated type binding was done outside of the type parameter declaration
and `where` clause. Erroneous code example:
-```compile_fail
+```compile_fail,E0229
pub trait Foo {
type A;
fn boo(&self) -> <Self as Foo>::A;
These two examples illustrate the problem:
-```compile_fail
+```compile_fail,E0261
// error, use of undeclared lifetime name `'a`
fn foo(x: &'a str) { }
because the `'static` lifetime is a special built-in lifetime name denoting
the lifetime of the entire program, this is an error:
-```compile_fail
+```compile_fail,E0262
// error, invalid lifetime parameter name `'static`
fn foo<'static>(x: &'static str) { }
```
A lifetime name cannot be declared more than once in the same scope. For
example:
-```compile_fail
+```compile_fail,E0263
// error, lifetime name `'a` declared twice in the same scope
fn foo<'a, 'b, 'a>(x: &'a str, y: &'b str) { }
```
E0264: r##"
An unknown external lang item was used. Erroneous code example:
-```compile_fail
+```compile_fail,E0264
#![feature(lang_items)]
extern "C" {
Functions must eventually return a value of their return type. For example, in
the following function:
-```compile_fail
-fn foo(x: u8) -> u8 {
- if x > 0 {
- x // alternatively, `return x`
- }
- // nothing here
+```compile_fail,E0269
+fn abracada_FAIL() -> String {
+ "this won't work".to_string();
}
```
Here is a basic example:
-```compile_fail
+```compile_fail,E0271
trait Trait { type AssociatedType; }
fn foo<T>(t: T) where T: Trait<AssociatedType=u32> {
compiled:
```compile_fail
+#![feature(on_unimplemented)]
+
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
compiled:
```compile_fail
+#![feature(on_unimplemented)]
+
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
compiled:
```compile_fail
+#![feature(on_unimplemented)]
+
fn foo<T: Index<u8>>(x: T){}
#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"]
For example, in the following code:
-```compile_fail
+```compile_fail,E0275
trait Foo {}
struct Bar<T>(T);
This error occurs when a bound in an implementation of a trait does not match
the bounds specified in the original trait. For example:
-```compile_fail
+```compile_fail,E0276
trait Foo {
fn foo<T>(x: T);
}
You tried to use a type which doesn't implement some trait in a place which
expected that trait. Erroneous code example:
-```compile_fail
+```compile_fail,E0277
// here we declare the Foo trait with a bar method
trait Foo {
fn bar(&self);
Or in a generic context, an erroneous code example would look like:
-```compile_fail
+```compile_fail,E0277
fn some_func<T>(foo: T) {
println!("{:?}", foo); // error: the trait `core::fmt::Debug` is not
// implemented for the type `T`
which expected that trait. This error typically occurs when working with
`Fn`-based types. Erroneous code example:
-```compile_fail
+```compile_fail,E0281
fn foo<F: Fn()>(x: F) { }
fn main() {
implemented by `Vec` and `String` among others. Consider the following snippet
that reverses the characters of a string:
-```compile_fail
+```compile_fail,E0282
let x = "hello".chars().rev().collect();
```
case it is not always possible to use a type annotation, because all candidates
have the same return type. For instance:
-```compile_fail
+```compile_fail,E0282
struct Foo<T> {
num: T,
}
For example:
-```compile_fail
+```compile_fail,E0283
trait Generator {
fn create() -> u32;
}
E0296: r##"
This error indicates that the given recursion limit could not be parsed. Ensure
-that the value provided is a positive integer between quotes, like so:
+that the value provided is a positive integer between quotes.
+
+Erroneous code example:
+
+```compile_fail,E0296
+#![recursion_limit]
+
+fn main() {}
+```
+
+And a working example:
```
#![recursion_limit="1000"]
+
+fn main() {}
```
"##,
For example:
-```compile_fail
+```compile_fail,E0308
let x: i32 = "I am not a number!";
// ~~~ ~~~~~~~~~~~~~~~~~~~~
// | |
Another situation in which this occurs is when you attempt to use the `try!`
macro inside a function that does not return a `Result<T, E>`:
-```compile_fail
+```compile_fail,E0308
use std::fs::File;
fn main() {
must be as long as the data needs to be alive, and missing the constraint that
denotes this will cause this error.
-```compile_fail
+```compile_fail,E0309
// This won't compile because T is not constrained, meaning the data
// stored in it is not guaranteed to last as long as the reference
struct Foo<'a, T> {
must be as long as the data needs to be alive, and missing the constraint that
denotes this will cause this error.
-```compile_fail
+```compile_fail,E0310
// This won't compile because T is not constrained to the static lifetime
// the reference needs
struct Foo<T> {
E0452: r##"
An invalid lint attribute has been given. Erroneous code example:
-```compile_fail
+```compile_fail,E0452
#![allow(foo = "")] // error: malformed lint attribute
```
Example of erroneous code:
-```compile_fail
+```compile_fail,E0453
#![forbid(non_snake_case)]
#[allow(non_snake_case)]
E0496: r##"
A lifetime name is shadowing another lifetime name. Erroneous code example:
-```compile_fail
+```compile_fail,E0496
struct Foo<'a> {
a: &'a i32,
}
Transmute with two differently sized types was attempted. Erroneous code
example:
-```compile_fail
+```compile_fail,E0512
fn takes_u8(_: u8) {}
fn main() {
Examples of erroneous code:
-```compile_fail
+```compile_fail,E0517
#[repr(C)]
type Foo = u8;
Examples of erroneous code:
-```compile_fail
+```compile_fail,E0518
#[inline(always)]
struct Foo;
invoked (such as the handler for out-of-bounds accesses when indexing a slice).
Erroneous code example:
-```compile_fail
+```compile_fail,E0522
#![feature(lang_items)]
#[lang = "cookie"]
let unknown_variable = 12u32;
let x = unknown_variable; // ok!
```
+
+If the item is not defined in the current module, it must be imported using a
+`use` statement, like so:
+
+```ignore
+use foo::bar;
+bar();
+```
+
+If the item you are importing is not defined in some super-module of the
+current module, then it must also be declared as public (e.g., `pub fn`).
"##,
E0426: r##"
Erroneous code example:
```compile_fail
-trait Trait {
+trait T1 {
type Bar;
}
-type Foo = Trait<F=i32>; // error: associated type `F` not found for
- // `Trait`
+type Foo = T1<F=i32>; // error: associated type `F` not found for `T1`
+
+// or:
+
+trait T2 {
+ type Bar;
+
+ // error: Baz is used but not declared
+ fn return_bool(&self, &Self::Bar, &Self::Baz) -> bool;
+}
```
-Please verify you used the right trait or you didn't misspell the
+Make sure that you have defined the associated type in the trait body.
+Also, verify that you used the right trait or you didn't misspell the
associated type name. Example:
```
-trait Trait {
+trait T1 {
type Bar;
}
-type Foo = Trait<Bar=i32>; // ok!
+type Foo = T1<Bar=i32>; // ok!
+
+// or:
+
+trait T2 {
+ type Bar;
+ type Baz; // we declare `Baz` in our trait.
+
+ // and now we can use it here:
+ fn return_bool(&self, &Self::Bar, &Self::Baz) -> bool;
+}
```
"##,
if !is_static || render_static {
if !is_default_item {
if let Some(t) = trait_ {
- let it = t.items.iter().find(|i| i.name == item.name).unwrap();
- // We need the stability of the item from the trait because
- // impls can't have a stability.
- document_stability(w, cx, it)?;
- if item.doc_value().is_some() {
- document_full(w, item)?;
- } else {
- // In case the item isn't documented,
- // provide short documentation from the trait.
- document_short(w, it, link)?;
+ // The trait item may have been stripped so we might not
+ // find any documentation or stability for it.
+ if let Some(it) = t.items.iter().find(|i| i.name == item.name) {
+ // We need the stability of the item from the trait
+ // because impls can't have a stability.
+ document_stability(w, cx, it)?;
+ if item.doc_value().is_some() {
+ document_full(w, item)?;
+ } else {
+ // In case the item isn't documented,
+ // provide short documentation from the trait.
+ document_short(w, it, link)?;
+ }
}
} else {
document(w, cx, item)?;
sidebar.append(div);
}
+ block("primitive", "Primitive Types");
block("mod", "Modules");
+ block("macro", "Macros");
block("struct", "Structs");
block("enum", "Enums");
+ block("constant", "Constants");
+ block("static", "Statics");
block("trait", "Traits");
block("fn", "Functions");
- block("macro", "Macros");
+ block("type", "Type Definitions");
}
window.initSidebarItems = initSidebarItems;
use os::raw::c_long;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_long;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_long;
#[doc(inline)]
#[stable(feature = "raw_ext", since = "1.1.0")]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_ulong;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_ulong;
#[doc(inline)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_ulong;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_ulong;
#[doc(inline)]
#[stable(feature = "raw_ext", since = "1.1.0")]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Copy, Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64;
#[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64;
-#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize;
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = usize;
#[repr(C)]
#[derive(Clone)]
use marker::{Sync, Send};
use mem;
use clone::Clone;
+use time::Instant;
struct Inner {
thread: Thread,
pub unsafe fn cast_from_usize(signal_ptr: usize) -> SignalToken {
SignalToken { inner: mem::transmute(signal_ptr) }
}
-
}
impl WaitToken {
thread::park()
}
}
+
+ /// Returns true if we wake up normally, false otherwise.
+ pub fn wait_max_until(self, end: Instant) -> bool {
+ while !self.inner.woken.load(Ordering::SeqCst) {
+ let now = Instant::now();
+ if now >= end {
+ return false;
+ }
+ thread::park_timeout(end - now)
+ }
+ true
+ }
}
// senders. Under the hood, however, there are actually three flavors of
// channels in play.
//
-// * Flavor::Oneshots - these channels are highly optimized for the one-send use case.
-// They contain as few atomics as possible and involve one and
-// exactly one allocation.
+// * Flavor::Oneshots - these channels are highly optimized for the one-send use
+// case. They contain as few atomics as possible and
+// involve one and exactly one allocation.
// * Streams - these channels are optimized for the non-shared use case. They
// use a different concurrent queue that is more tailored for this
// use case. The initial allocation of this flavor of channel is not
//
// ## Concurrent queues
//
-// The basic idea of Rust's Sender/Receiver types is that send() never blocks, but
-// recv() obviously blocks. This means that under the hood there must be some
-// shared and concurrent queue holding all of the actual data.
+// The basic idea of Rust's Sender/Receiver types is that send() never blocks,
+// but recv() obviously blocks. This means that under the hood there must be
+// some shared and concurrent queue holding all of the actual data.
//
// With two flavors of channels, two flavors of queues are also used. We have
// chosen to use queues from a well-known author that are abbreviated as SPSC
use mem;
use cell::UnsafeCell;
use marker::Reflect;
+use time::{Duration, Instant};
#[unstable(feature = "mpsc_select", issue = "27800")]
pub use self::select::{Select, Handle};
Disconnected,
}
+/// This enumeration is the list of possible errors that `recv_timeout` could
+/// not return data when called.
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+#[unstable(feature = "mpsc_recv_timeout", issue = "34029")]
+pub enum RecvTimeoutError {
+ /// This channel is currently empty, but the sender(s) have not yet
+ /// disconnected, so data may yet become available.
+ Timeout,
+ /// This channel's sending half has become disconnected, and there will
+ /// never be any more data received on this channel
+ Disconnected,
+}
+
/// This enumeration is the list of the possible error outcomes for the
/// `SyncSender::try_send` method.
#[stable(feature = "rust1", since = "1.0.0")]
loop {
let new_port = match *unsafe { self.inner() } {
Flavor::Oneshot(ref p) => {
- match unsafe { (*p.get()).recv() } {
+ match unsafe { (*p.get()).recv(None) } {
Ok(t) => return Ok(t),
- Err(oneshot::Empty) => return unreachable!(),
Err(oneshot::Disconnected) => return Err(RecvError),
Err(oneshot::Upgraded(rx)) => rx,
+ Err(oneshot::Empty) => unreachable!(),
}
}
Flavor::Stream(ref p) => {
- match unsafe { (*p.get()).recv() } {
+ match unsafe { (*p.get()).recv(None) } {
Ok(t) => return Ok(t),
- Err(stream::Empty) => return unreachable!(),
Err(stream::Disconnected) => return Err(RecvError),
Err(stream::Upgraded(rx)) => rx,
+ Err(stream::Empty) => unreachable!(),
}
}
Flavor::Shared(ref p) => {
- match unsafe { (*p.get()).recv() } {
+ match unsafe { (*p.get()).recv(None) } {
Ok(t) => return Ok(t),
- Err(shared::Empty) => return unreachable!(),
Err(shared::Disconnected) => return Err(RecvError),
+ Err(shared::Empty) => unreachable!(),
}
}
Flavor::Sync(ref p) => return unsafe {
- (*p.get()).recv().map_err(|()| RecvError)
+ (*p.get()).recv(None).map_err(|_| RecvError)
}
};
unsafe {
}
}
+ /// Attempts to wait for a value on this receiver, returning an error if the
+ /// corresponding channel has hung up, or if it waits more than `timeout`.
+ ///
+ /// This function will always block the current thread if there is no data
+ /// available and it's possible for more data to be sent. Once a message is
+ /// sent to the corresponding `Sender`, then this receiver will wake up and
+ /// return that message.
+ ///
+ /// If the corresponding `Sender` has disconnected, or it disconnects while
+ /// this call is blocking, this call will wake up and return `Err` to
+ /// indicate that no more messages can ever be received on this channel.
+ /// However, since channels are buffered, messages sent before the disconnect
+ /// will still be properly received.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(mpsc_recv_timeout)]
+ ///
+ /// use std::sync::mpsc::{self, RecvTimeoutError};
+ /// use std::time::Duration;
+ ///
+ /// let (send, recv) = mpsc::channel::<()>();
+ ///
+ /// let timeout = Duration::from_millis(100);
+ /// assert_eq!(Err(RecvTimeoutError::Timeout), recv.recv_timeout(timeout));
+ /// ```
+ #[unstable(feature = "mpsc_recv_timeout", issue = "34029")]
+ pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> {
+ // Do an optimistic try_recv to avoid the performance impact of
+ // Instant::now() in the full-channel case.
+ match self.try_recv() {
+ Ok(result)
+ => Ok(result),
+ Err(TryRecvError::Disconnected)
+ => Err(RecvTimeoutError::Disconnected),
+ Err(TryRecvError::Empty)
+ => self.recv_max_until(Instant::now() + timeout)
+ }
+ }
+
+ fn recv_max_until(&self, deadline: Instant) -> Result<T, RecvTimeoutError> {
+ use self::RecvTimeoutError::*;
+
+ loop {
+ let port_or_empty = match *unsafe { self.inner() } {
+ Flavor::Oneshot(ref p) => {
+ match unsafe { (*p.get()).recv(Some(deadline)) } {
+ Ok(t) => return Ok(t),
+ Err(oneshot::Disconnected) => return Err(Disconnected),
+ Err(oneshot::Upgraded(rx)) => Some(rx),
+ Err(oneshot::Empty) => None,
+ }
+ }
+ Flavor::Stream(ref p) => {
+ match unsafe { (*p.get()).recv(Some(deadline)) } {
+ Ok(t) => return Ok(t),
+ Err(stream::Disconnected) => return Err(Disconnected),
+ Err(stream::Upgraded(rx)) => Some(rx),
+ Err(stream::Empty) => None,
+ }
+ }
+ Flavor::Shared(ref p) => {
+ match unsafe { (*p.get()).recv(Some(deadline)) } {
+ Ok(t) => return Ok(t),
+ Err(shared::Disconnected) => return Err(Disconnected),
+ Err(shared::Empty) => None,
+ }
+ }
+ Flavor::Sync(ref p) => {
+ match unsafe { (*p.get()).recv(Some(deadline)) } {
+ Ok(t) => return Ok(t),
+ Err(sync::Disconnected) => return Err(Disconnected),
+ Err(sync::Empty) => None,
+ }
+ }
+ };
+
+ if let Some(new_port) = port_or_empty {
+ unsafe {
+ mem::swap(self.inner_mut(), new_port.inner_mut());
+ }
+ }
+
+ // If we're already passed the deadline, and we're here without
+ // data, return a timeout, else try again.
+ if Instant::now() >= deadline {
+ return Err(Timeout);
+ }
+ }
+ }
+
/// Returns an iterator that will block waiting for messages, but never
/// `panic!`. It will return `None` when the channel has hung up.
#[stable(feature = "rust1", since = "1.0.0")]
use env;
use super::*;
use thread;
+ use time::{Duration, Instant};
pub fn stress_factor() -> usize {
match env::var("RUST_TEST_STRESS") {
}
}
+ #[test]
+ fn oneshot_single_thread_recv_timeout() {
+ let (tx, rx) = channel();
+ tx.send(()).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+ tx.send(()).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+ }
+
+ #[test]
+ fn stress_recv_timeout_two_threads() {
+ let (tx, rx) = channel();
+ let stress = stress_factor() + 100;
+ let timeout = Duration::from_millis(100);
+
+ thread::spawn(move || {
+ for i in 0..stress {
+ if i % 2 == 0 {
+ thread::sleep(timeout * 2);
+ }
+ tx.send(1usize).unwrap();
+ }
+ });
+
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(timeout) {
+ Ok(n) => {
+ assert_eq!(n, 1usize);
+ recv_count += 1;
+ }
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, stress);
+ }
+
+ #[test]
+ fn recv_timeout_upgrade() {
+ let (tx, rx) = channel::<()>();
+ let timeout = Duration::from_millis(1);
+ let _tx_clone = tx.clone();
+
+ let start = Instant::now();
+ assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout));
+ assert!(Instant::now() >= start + timeout);
+ }
+
+ #[test]
+ fn stress_recv_timeout_shared() {
+ let (tx, rx) = channel();
+ let stress = stress_factor() + 100;
+
+ for i in 0..stress {
+ let tx = tx.clone();
+ thread::spawn(move || {
+ thread::sleep(Duration::from_millis(i as u64 * 10));
+ tx.send(1usize).unwrap();
+ });
+ }
+
+ drop(tx);
+
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(Duration::from_millis(10)) {
+ Ok(n) => {
+ assert_eq!(n, 1usize);
+ recv_count += 1;
+ }
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, stress);
+ }
+
#[test]
fn recv_a_lot() {
// Regression test that we don't run out of stack in scheduler context
for _ in 0..10000 { rx.recv().unwrap(); }
}
+ #[test]
+ fn shared_recv_timeout() {
+ let (tx, rx) = channel();
+ let total = 5;
+ for _ in 0..total {
+ let tx = tx.clone();
+ thread::spawn(move|| {
+ tx.send(()).unwrap();
+ });
+ }
+
+ for _ in 0..total { rx.recv().unwrap(); }
+
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+ tx.send(()).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(()));
+ }
+
#[test]
fn shared_chan_stress() {
let (tx, rx) = channel();
use env;
use thread;
use super::*;
+ use time::Duration;
pub fn stress_factor() -> usize {
match env::var("RUST_TEST_STRESS") {
assert_eq!(rx.recv().unwrap(), 1);
}
+ #[test]
+ fn recv_timeout() {
+ let (tx, rx) = sync_channel::<i32>(1);
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
+ tx.send(1).unwrap();
+ assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1));
+ }
+
#[test]
fn smoke_threads() {
let (tx, rx) = sync_channel::<i32>(0);
}
}
+ #[test]
+ fn stress_recv_timeout_two_threads() {
+ let (tx, rx) = sync_channel::<i32>(0);
+
+ thread::spawn(move|| {
+ for _ in 0..10000 { tx.send(1).unwrap(); }
+ });
+
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(Duration::from_millis(1)) {
+ Ok(v) => {
+ assert_eq!(v, 1);
+ recv_count += 1;
+ },
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, 10000);
+ }
+
+ #[test]
+ fn stress_recv_timeout_shared() {
+ const AMT: u32 = 1000;
+ const NTHREADS: u32 = 8;
+ let (tx, rx) = sync_channel::<i32>(0);
+ let (dtx, drx) = sync_channel::<()>(0);
+
+ thread::spawn(move|| {
+ let mut recv_count = 0;
+ loop {
+ match rx.recv_timeout(Duration::from_millis(10)) {
+ Ok(v) => {
+ assert_eq!(v, 1);
+ recv_count += 1;
+ },
+ Err(RecvTimeoutError::Timeout) => continue,
+ Err(RecvTimeoutError::Disconnected) => break,
+ }
+ }
+
+ assert_eq!(recv_count, AMT * NTHREADS);
+ assert!(rx.try_recv().is_err());
+
+ dtx.send(()).unwrap();
+ });
+
+ for _ in 0..NTHREADS {
+ let tx = tx.clone();
+ thread::spawn(move|| {
+ for _ in 0..AMT { tx.send(1).unwrap(); }
+ });
+ }
+
+ drop(tx);
+
+ drx.recv().unwrap();
+ }
+
#[test]
fn stress_shared() {
const AMT: u32 = 1000;
use sync::mpsc::blocking::{self, SignalToken};
use core::mem;
use sync::atomic::{AtomicUsize, Ordering};
+use time::Instant;
// Various states you can find a port in.
const EMPTY: usize = 0; // initial state: no data, no blocked receiver
}
}
- pub fn recv(&mut self) -> Result<T, Failure<T>> {
+ pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
// Attempt to not block the thread (it's a little expensive). If it looks
// like we're not empty, then immediately go through to `try_recv`.
if self.state.load(Ordering::SeqCst) == EMPTY {
// race with senders to enter the blocking state
if self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) == EMPTY {
- wait_token.wait();
- debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY);
+ if let Some(deadline) = deadline {
+ let timed_out = !wait_token.wait_max_until(deadline);
+ // Try to reset the state
+ if timed_out {
+ try!(self.abort_selection().map_err(Upgraded));
+ }
+ } else {
+ wait_token.wait();
+ debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY);
+ }
} else {
// drop the signal token, since we never blocked
drop(unsafe { SignalToken::cast_from_usize(ptr) });
use sync::mpsc::select::StartResult;
use sync::{Mutex, MutexGuard};
use thread;
+use time::Instant;
const DISCONNECTED: isize = isize::MIN;
const FUDGE: isize = 1024;
// Creation of a packet *must* be followed by a call to postinit_lock
// and later by inherit_blocker
pub fn new() -> Packet<T> {
- let p = Packet {
+ Packet {
queue: mpsc::Queue::new(),
cnt: AtomicIsize::new(0),
steals: 0,
port_dropped: AtomicBool::new(false),
sender_drain: AtomicIsize::new(0),
select_lock: Mutex::new(()),
- };
- return p;
+ }
}
// This function should be used after newly created Packet
Ok(())
}
- pub fn recv(&mut self) -> Result<T, Failure> {
+ pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure> {
// This code is essentially the exact same as that found in the stream
// case (see stream.rs)
match self.try_recv() {
let (wait_token, signal_token) = blocking::tokens();
if self.decrement(signal_token) == Installed {
- wait_token.wait()
+ if let Some(deadline) = deadline {
+ let timed_out = !wait_token.wait_max_until(deadline);
+ if timed_out {
+ self.abort_selection(false);
+ }
+ } else {
+ wait_token.wait();
+ }
}
match self.try_recv() {
use core::cmp;
use core::isize;
use thread;
+use time::Instant;
use sync::atomic::{AtomicIsize, AtomicUsize, Ordering, AtomicBool};
use sync::mpsc::Receiver;
Err(unsafe { SignalToken::cast_from_usize(ptr) })
}
- pub fn recv(&mut self) -> Result<T, Failure<T>> {
+ pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> {
// Optimistic preflight check (scheduling is expensive).
match self.try_recv() {
Err(Empty) => {}
// initiate the blocking protocol.
let (wait_token, signal_token) = blocking::tokens();
if self.decrement(signal_token).is_ok() {
- wait_token.wait()
+ if let Some(deadline) = deadline {
+ let timed_out = !wait_token.wait_max_until(deadline);
+ if timed_out {
+ try!(self.abort_selection(/* was_upgrade = */ false)
+ .map_err(Upgraded));
+ }
+ } else {
+ wait_token.wait();
+ }
}
match self.try_recv() {
// the internal state.
match self.queue.peek() {
Some(&mut GoUp(..)) => {
- match self.recv() {
+ match self.recv(None) {
Err(Upgraded(port)) => Err(port),
_ => unreachable!(),
}
use sync::mpsc::blocking::{self, WaitToken, SignalToken};
use sync::mpsc::select::StartResult::{self, Installed, Abort};
use sync::{Mutex, MutexGuard};
+use time::Instant;
pub struct Packet<T> {
/// Only field outside of the mutex. Just done for kicks, but mainly because
lock.lock().unwrap() // relock
}
+/// Same as wait, but waiting at most until `deadline`.
+fn wait_timeout_receiver<'a, 'b, T>(lock: &'a Mutex<State<T>>,
+ deadline: Instant,
+ mut guard: MutexGuard<'b, State<T>>,
+ success: &mut bool)
+ -> MutexGuard<'a, State<T>>
+{
+ let (wait_token, signal_token) = blocking::tokens();
+ match mem::replace(&mut guard.blocker, BlockedReceiver(signal_token)) {
+ NoneBlocked => {}
+ _ => unreachable!(),
+ }
+ drop(guard); // unlock
+ *success = wait_token.wait_max_until(deadline); // block
+ let mut new_guard = lock.lock().unwrap(); // relock
+ if !*success {
+ abort_selection(&mut new_guard);
+ }
+ new_guard
+}
+
+fn abort_selection<'a, T>(guard: &mut MutexGuard<'a , State<T>>) -> bool {
+ match mem::replace(&mut guard.blocker, NoneBlocked) {
+ NoneBlocked => true,
+ BlockedSender(token) => {
+ guard.blocker = BlockedSender(token);
+ true
+ }
+ BlockedReceiver(token) => { drop(token); false }
+ }
+}
+
/// Wakes up a thread, dropping the lock at the correct time
fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) {
// We need to be careful to wake up the waiting thread *outside* of the mutex
//
// When reading this, remember that there can only ever be one receiver at
// time.
- pub fn recv(&self) -> Result<T, ()> {
+ pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure> {
let mut guard = self.lock.lock().unwrap();
- // Wait for the buffer to have something in it. No need for a while loop
- // because we're the only receiver.
- let mut waited = false;
+ let mut woke_up_after_waiting = false;
+ // Wait for the buffer to have something in it. No need for a
+ // while loop because we're the only receiver.
if !guard.disconnected && guard.buf.size() == 0 {
- guard = wait(&self.lock, guard, BlockedReceiver);
- waited = true;
+ if let Some(deadline) = deadline {
+ guard = wait_timeout_receiver(&self.lock,
+ deadline,
+ guard,
+ &mut woke_up_after_waiting);
+ } else {
+ guard = wait(&self.lock, guard, BlockedReceiver);
+ woke_up_after_waiting = true;
+ }
+ }
+
+ // NB: Channel could be disconnected while waiting, so the order of
+ // these conditionals is important.
+ if guard.disconnected && guard.buf.size() == 0 {
+ return Err(Disconnected);
}
- if guard.disconnected && guard.buf.size() == 0 { return Err(()) }
// Pick up the data, wake up our neighbors, and carry on
- assert!(guard.buf.size() > 0);
+ assert!(guard.buf.size() > 0 || (deadline.is_some() && !woke_up_after_waiting));
+
+ if guard.buf.size() == 0 { return Err(Empty); }
+
let ret = guard.buf.dequeue();
- self.wakeup_senders(waited, guard);
+ self.wakeup_senders(woke_up_after_waiting, guard);
Ok(ret)
}
// The return value indicates whether there's data on this port.
pub fn abort_selection(&self) -> bool {
let mut guard = self.lock.lock().unwrap();
- match mem::replace(&mut guard.blocker, NoneBlocked) {
- NoneBlocked => true,
- BlockedSender(token) => {
- guard.blocker = BlockedSender(token);
- true
- }
- BlockedReceiver(token) => { drop(token); false }
- }
+ abort_selection(&mut guard)
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
- let (len, _) = self.bytes.size_hint();
+ let len = self.bytes.len();
(len.saturating_add(3) / 4, Some(len))
}
}
#[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32;
#[doc(inline)]
-#[unstable(feature = "pthread_t", issue = "29791")]
+#[stable(feature = "pthread_t", since = "1.8.0")]
pub use sys::platform::raw::pthread_t;
#[doc(inline)]
#[stable(feature = "raw_ext", since = "1.1.0")]
}
#[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))]
- pub fn set_cloexec(&self) {
+ pub fn set_cloexec(&self) -> io::Result<()> {
unsafe {
- let ret = libc::ioctl(self.fd, libc::FIOCLEX);
- debug_assert_eq!(ret, 0);
+ cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
+ Ok(())
}
}
#[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))]
- pub fn set_cloexec(&self) {
+ pub fn set_cloexec(&self) -> io::Result<()> {
unsafe {
- let previous = libc::fcntl(self.fd, libc::F_GETFD);
- let ret = libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC);
- debug_assert_eq!(ret, 0);
+ let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
+ cvt(libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC))?;
+ Ok(())
}
}
- pub fn set_nonblocking(&self, nonblocking: bool) {
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
- let previous = libc::fcntl(self.fd, libc::F_GETFL);
- debug_assert!(previous != -1);
+ let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
let new = if nonblocking {
previous | libc::O_NONBLOCK
} else {
previous & !libc::O_NONBLOCK
};
- let ret = libc::fcntl(self.fd, libc::F_SETFL, new);
- debug_assert!(ret != -1);
+ cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
+ Ok(())
}
}
let make_filedesc = |fd| {
let fd = FileDesc::new(fd);
- fd.set_cloexec();
- fd
+ fd.set_cloexec()?;
+ Ok(fd)
};
static TRY_CLOEXEC: AtomicBool =
AtomicBool::new(!cfg!(target_os = "android"));
// though it reported doing so on F_DUPFD_CLOEXEC.
Ok(fd) => {
return Ok(if cfg!(target_os = "linux") {
- make_filedesc(fd)
+ make_filedesc(fd)?
} else {
FileDesc::new(fd)
})
Err(e) => return Err(e),
}
}
- cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc)
+ cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
}
}
// The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc
// that we support, so we only do this on Linux currently.
if cfg!(target_os = "linux") {
- fd.set_cloexec();
+ fd.set_cloexec()?;
}
Ok(File(fd))
let fd = cvt(libc::socket(fam, ty, 0))?;
let fd = FileDesc::new(fd);
- fd.set_cloexec();
+ fd.set_cloexec()?;
Ok(Socket(fd))
}
}
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
let a = FileDesc::new(fds[0]);
- a.set_cloexec();
let b = FileDesc::new(fds[1]);
- b.set_cloexec();
+ a.set_cloexec()?;
+ b.set_cloexec()?;
Ok((Socket(a), Socket(b)))
}
}
libc::accept(self.0.raw(), storage, len)
})?;
let fd = FileDesc::new(fd);
- fd.set_cloexec();
+ fd.set_cloexec()?;
Ok(Socket(fd))
}
}
}
if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } {
- Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1])))
+ let fd0 = FileDesc::new(fds[0]);
+ let fd1 = FileDesc::new(fds[1]);
+ Ok((AnonPipe::from_fd(fd0)?, AnonPipe::from_fd(fd1)?))
} else {
Err(io::Error::last_os_error())
}
}
impl AnonPipe {
- pub fn from_fd(fd: libc::c_int) -> AnonPipe {
- let fd = FileDesc::new(fd);
- fd.set_cloexec();
- AnonPipe(fd)
+ pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> {
+ fd.set_cloexec()?;
+ Ok(AnonPipe(fd))
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
// in the `select` loop below, and we wouldn't want one to block the other!
let p1 = p1.into_fd();
let p2 = p2.into_fd();
- p1.set_nonblocking(true);
- p2.set_nonblocking(true);
+ p1.set_nonblocking(true)?;
+ p2.set_nonblocking(true)?;
let max = cmp::max(p1.raw(), p2.raw());
loop {
}
};
if read(&p1, v1)? {
- p2.set_nonblocking(false);
+ p2.set_nonblocking(false)?;
return p2.read_to_end(v2).map(|_| ());
}
if read(&p2, v2)? {
- p1.set_nonblocking(false);
+ p1.set_nonblocking(false)?;
return p1.read_to_end(v1).map(|_| ());
}
}
/// Declare a new thread local storage key of type `std::thread::LocalKey`.
///
+/// # Syntax
+///
+/// The macro wraps any number of static declarations and makes them thread local.
+/// Each static may be public or private, and attributes are allowed. Example:
+///
+/// ```
+/// use std::cell::RefCell;
+/// thread_local! {
+/// pub static FOO: RefCell<u32> = RefCell::new(1);
+///
+/// #[allow(unused)]
+/// static BAR: RefCell<f32> = RefCell::new(1.0);
+/// }
+/// # fn main() {}
+/// ```
+///
/// See [LocalKey documentation](thread/struct.LocalKey.html) for more
/// information.
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[allow_internal_unstable]
macro_rules! thread_local {
- (static $name:ident: $t:ty = $init:expr) => (
- static $name: $crate::thread::LocalKey<$t> =
+ // rule 0: empty (base case for the recursion)
+ () => {};
+
+ // rule 1: process multiple declarations where the first one is private
+ ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
+ thread_local!($(#[$attr])* static $name: $t = $init); // go to rule 2
+ thread_local!($($rest)*);
+ );
+
+ // rule 2: handle a single private declaration
+ ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => (
+ $(#[$attr])* static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init);
);
- (pub static $name:ident: $t:ty = $init:expr) => (
- pub static $name: $crate::thread::LocalKey<$t> =
+
+ // rule 3: handle multiple declarations where the first one is public
+ ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
+ thread_local!($(#[$attr])* pub static $name: $t = $init); // go to rule 4
+ thread_local!($($rest)*);
+ );
+
+ // rule 4: handle a single public declaration
+ ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr) => (
+ $(#[$attr])* pub static $name: $crate::thread::LocalKey<$t> =
__thread_local_inner!($t, $init);
);
}
impl Thread {
// Used only internally to construct a thread object without spawning
fn new(name: Option<String>) -> Thread {
- let cname = name.map(|n| CString::new(n).unwrap_or_else(|_| {
- panic!("thread name may not contain interior null bytes")
- }));
+ let cname = name.map(|n| {
+ CString::new(n).expect("thread name may not contain interior null bytes")
+ });
Thread {
inner: Arc::new(Inner {
name: cname,
use ast;
use ast::{Name, PatKind};
+use attr::HasAttrs;
use codemap;
use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
use errors::DiagnosticBuilder;
ImplItem(P<ast::ImplItem>),
}
-impl Annotatable {
- pub fn attrs(&self) -> &[ast::Attribute] {
+impl HasAttrs for Annotatable {
+ fn attrs(&self) -> &[ast::Attribute] {
match *self {
- Annotatable::Item(ref i) => &i.attrs,
- Annotatable::TraitItem(ref ti) => &ti.attrs,
- Annotatable::ImplItem(ref ii) => &ii.attrs,
+ Annotatable::Item(ref item) => &item.attrs,
+ Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
+ Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
}
}
- pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
+ fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
match self {
- Annotatable::Item(i) => Annotatable::Item(i.map(|i| ast::Item {
- attrs: attrs,
- ..i
- })),
- Annotatable::TraitItem(i) => Annotatable::TraitItem(i.map(|ti| {
- ast::TraitItem { attrs: attrs, ..ti }
- })),
- Annotatable::ImplItem(i) => Annotatable::ImplItem(i.map(|ii| {
- ast::ImplItem { attrs: attrs, ..ii }
- })),
+ Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
+ Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
+ Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)),
}
}
+}
+
+impl Annotatable {
+ pub fn attrs(&self) -> &[ast::Attribute] {
+ HasAttrs::attrs(self)
+ }
+ pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
+ self.map_attrs(|_| attrs)
+ }
pub fn expect_item(self) -> P<ast::Item> {
match self {
}
}
-// A more flexible ItemKind::Modifier (ItemKind::Modifier should go away, eventually, FIXME).
-// meta_item is the annotation, item is the item being modified, parent_item
-// is the impl or trait item is declared in if item is part of such a thing.
+// `meta_item` is the annotation, and `item` is the item being modified.
// FIXME Decorators should follow the same pattern too.
pub trait MultiItemModifier {
fn expand(&self,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable)
- -> Annotatable;
+ -> Vec<Annotatable>;
}
-impl<F> MultiItemModifier for F
- where F: Fn(&mut ExtCtxt,
- Span,
- &ast::MetaItem,
- Annotatable) -> Annotatable
+impl<F, T> MultiItemModifier for F
+ where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T,
+ T: Into<Vec<Annotatable>>,
{
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable)
- -> Annotatable {
- (*self)(ecx, span, meta_item, item)
+ -> Vec<Annotatable> {
+ (*self)(ecx, span, meta_item, item).into()
+ }
+}
+
+impl Into<Vec<Annotatable>> for Annotatable {
+ fn into(self) -> Vec<Annotatable> {
+ vec![self]
}
}
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
use ast::TokenTree;
use ast;
+use attr::HasAttrs;
use ext::mtwt;
use ext::build::AstBuilder;
use attr;
impl<'a> Folder for IdentRenamer<'a> {
fn fold_ident(&mut self, id: Ident) -> Ident {
- Ident::new(id.name, mtwt::apply_renames(self.renames, id.ctxt))
+ mtwt::apply_renames(self.renames, id)
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
pat.map(|ast::Pat {id, node, span}| match node {
PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
- let new_ident = Ident::new(ident.name,
- mtwt::apply_renames(self.renames, ident.ctxt));
+ let new_ident = mtwt::apply_renames(self.renames, ident);
let new_node =
PatKind::Ident(binding_mode,
Spanned{span: sp, node: new_ident},
}
}
-fn expand_annotatable(a: Annotatable,
- fld: &mut MacroExpander)
- -> SmallVector<Annotatable> {
- let a = expand_item_multi_modifier(a, fld);
-
+fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
let new_items: SmallVector<Annotatable> = match a {
Annotatable::Item(it) => match it.node {
ast::ItemKind::Mac(..) => {
new_items
}
-// Partition a set of attributes into one kind of attribute, and other kinds.
-macro_rules! partition {
- ($fn_name: ident, $variant: ident) => {
- #[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used.
- fn $fn_name(attrs: &[ast::Attribute],
- fld: &MacroExpander)
- -> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
- attrs.iter().cloned().partition(|attr| {
- match fld.cx.syntax_env.find(intern(&attr.name())) {
- Some(rc) => match *rc {
- $variant(..) => true,
- _ => false
- },
- _ => false
- }
- })
- }
- }
-}
-
-partition!(multi_modifiers, MultiModifier);
-
-
fn expand_decorators(a: Annotatable,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
}
}
-fn expand_item_multi_modifier(mut it: Annotatable,
- fld: &mut MacroExpander)
- -> Annotatable {
- let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);
-
- // Update the attrs, leave everything else alone. Is this mutation really a good idea?
- it = it.fold_attrs(other_attrs);
-
- if modifiers.is_empty() {
- return it
- }
-
- for attr in &modifiers {
- let mname = intern(&attr.name());
-
- match fld.cx.syntax_env.find(mname) {
- Some(rc) => match *rc {
- MultiModifier(ref mac) => {
- attr::mark_used(attr);
- fld.cx.bt_push(ExpnInfo {
- call_site: attr.span,
- callee: NameAndSpan {
- format: MacroAttribute(mname),
- span: Some(attr.span),
- // attributes can do whatever they like,
- // for now
- allow_internal_unstable: true,
- }
- });
- it = mac.expand(fld.cx, attr.span, &attr.node.value, it);
- fld.cx.bt_pop();
+fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
+ let mut multi_modifier = None;
+ item = item.map_attrs(|mut attrs| {
+ for i in 0..attrs.len() {
+ if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
+ if let MultiModifier(..) = *extension {
+ multi_modifier = Some((attrs.remove(i), extension));
+ break;
}
- _ => unreachable!()
- },
- _ => unreachable!()
+ }
}
- }
+ attrs
+ });
- // Expansion may have added new ItemKind::Modifiers.
- expand_item_multi_modifier(it, fld)
+ match multi_modifier {
+ None => expand_multi_modified(item, fld),
+ Some((attr, extension)) => match *extension {
+ MultiModifier(ref mac) => {
+ attr::mark_used(&attr);
+ fld.cx.bt_push(ExpnInfo {
+ call_site: attr.span,
+ callee: NameAndSpan {
+ format: MacroAttribute(intern(&attr.name())),
+ span: Some(attr.span),
+ // attributes can do whatever they like, for now
+ allow_internal_unstable: true,
+ }
+ });
+ let modified = mac.expand(fld.cx, attr.span, &attr.node.value, item);
+ fld.cx.bt_pop();
+ modified.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
+ }
+ _ => unreachable!(),
+ }
+ }
}
fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
use ext::mtwt;
use fold::Folder;
use parse;
- use parse::token::{self, keywords};
+ use parse::token;
use util::parser_testing::{string_to_parser};
use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
use visit;
);
}
- // renaming tests expand a crate and then check that the bindings match
- // the right varrefs. The specification of the test case includes the
- // text of the crate, and also an array of arrays. Each element in the
- // outer array corresponds to a binding in the traversal of the AST
- // induced by visit. Each of these arrays contains a list of indexes,
- // interpreted as the varrefs in the varref traversal that this binding
- // should match. So, for instance, in a program with two bindings and
- // three varrefs, the array [[1, 2], [0]] would indicate that the first
- // binding should match the second two varrefs, and the second binding
- // should match the first varref.
- //
- // Put differently; this is a sparse representation of a boolean matrix
- // indicating which bindings capture which identifiers.
- //
- // Note also that this matrix is dependent on the implicit ordering of
- // the bindings and the varrefs discovered by the name-finder and the path-finder.
- //
- // The comparisons are done post-mtwt-resolve, so we're comparing renamed
- // names; differences in marks don't matter any more.
- //
- // oog... I also want tests that check "bound-identifier-=?". That is,
- // not just "do these have the same name", but "do they have the same
- // name *and* the same marks"? Understanding this is really pretty painful.
- // in principle, you might want to control this boolean on a per-varref basis,
- // but that would make things even harder to understand, and might not be
- // necessary for thorough testing.
- type RenamingTest = (&'static str, Vec<Vec<usize>>, bool);
-
- #[test]
- fn automatic_renaming () {
- let tests: Vec<RenamingTest> =
- vec!(// b & c should get new names throughout, in the expr too:
- ("fn a() -> i32 { let b = 13; let c = b; b+c }",
- vec!(vec!(0,1),vec!(2)), false),
- // both x's should be renamed (how is this causing a bug?)
- ("fn main () {let x: i32 = 13;x;}",
- vec!(vec!(0)), false),
- // the use of b after the + should be renamed, the other one not:
- ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
- vec!(vec!(1)), false),
- // the b before the plus should not be renamed (requires marks)
- ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
- vec!(vec!(1)), false),
- // the marks going in and out of letty should cancel, allowing that $x to
- // capture the one following the semicolon.
- // this was an awesome test case, and caught a *lot* of bugs.
- ("macro_rules! letty(($x:ident) => (let $x = 15;));
- macro_rules! user(($x:ident) => ({letty!($x); $x}));
- fn main() -> i32 {user!(z)}",
- vec!(vec!(0)), false)
- );
- for (idx,s) in tests.iter().enumerate() {
- run_renaming_test(s,idx);
- }
- }
-
- // no longer a fixme #8062: this test exposes a *potential* bug; our system does
- // not behave exactly like MTWT, but a conversation with Matthew Flatt
- // suggests that this can only occur in the presence of local-expand, which
- // we have no plans to support. ... unless it's needed for item hygiene....
- #[ignore]
- #[test]
- fn issue_8062(){
- run_renaming_test(
- &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
- vec!(vec!(0)), true), 0)
- }
-
- // FIXME #6994:
- // the z flows into and out of two macros (g & f) along one path, and one
- // (just g) along the other, so the result of the whole thing should
- // be "let z_123 = 3; z_123"
- #[ignore]
- #[test]
- fn issue_6994(){
- run_renaming_test(
- &("macro_rules! g (($x:ident) =>
- ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
- fn a(){g!(z)}",
- vec!(vec!(0)),false),
- 0)
- }
-
- // match variable hygiene. Should expand into
- // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
- #[test]
- fn issue_9384(){
- run_renaming_test(
- &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
- fn z() {match 8 {x => bad_macro!(x)}}",
- // NB: the third "binding" is the repeat of the second one.
- vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
- true),
- 0)
- }
-
- // interpolated nodes weren't getting labeled.
- // should expand into
- // fn main(){let g1_1 = 13; g1_1}}
- #[test]
- fn pat_expand_issue_15221(){
- run_renaming_test(
- &("macro_rules! inner ( ($e:pat ) => ($e));
- macro_rules! outer ( ($e:pat ) => (inner!($e)));
- fn main() { let outer!(g) = 13; g;}",
- vec!(vec!(0)),
- true),
- 0)
- }
-
// create a really evil test case where a $x appears inside a binding of $x
// but *shouldn't* bind because it was inserted by a different macro....
// can't write this test case until we have macro-generating macros.
- // method arg hygiene
- // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
- #[test]
- fn method_arg_hygiene(){
- run_renaming_test(
- &("macro_rules! inject_x (()=>(x));
- macro_rules! inject_self (()=>(self));
- struct A;
- impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
- vec!(vec!(0),vec!(3)),
- true),
- 0)
- }
-
- // ooh, got another bite?
- // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
- #[test]
- fn method_arg_hygiene_2(){
- run_renaming_test(
- &("struct A;
- macro_rules! add_method (($T:ty) =>
- (impl $T { fn thingy(&self) {self;} }));
- add_method!(A);",
- vec!(vec!(0)),
- true),
- 0)
- }
-
- // item fn hygiene
- // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
- #[test]
- fn issue_9383(){
- run_renaming_test(
- &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
- fn q(x: i32) { bad_macro!(x); }",
- vec!(vec!(1),vec!(0)),true),
- 0)
- }
-
- // closure arg hygiene (ExprKind::Closure)
- // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
- #[test]
- fn closure_arg_hygiene(){
- run_renaming_test(
- &("macro_rules! inject_x (()=>(x));
- fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
- vec!(vec!(1)),
- true),
- 0)
- }
-
- // macro_rules in method position. Sadly, unimplemented.
- #[test]
- fn macro_in_method_posn(){
- expand_crate_str(
- "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
- struct A;
- impl A{ my_method!(); }
- fn f(){A.thirteen;}".to_string());
- }
-
- // another nested macro
- // expands to impl Entries {fn size_hint(&self_1) {self_1;}
- #[test]
- fn item_macro_workaround(){
- run_renaming_test(
- &("macro_rules! item { ($i:item) => {$i}}
- struct Entries;
- macro_rules! iterator_impl {
- () => { item!( impl Entries { fn size_hint(&self) { self;}});}}
- iterator_impl! { }",
- vec!(vec!(0)), true),
- 0)
- }
-
- // run one of the renaming tests
- fn run_renaming_test(t: &RenamingTest, test_idx: usize) {
- let invalid_name = keywords::Invalid.name();
- let (teststr, bound_connections, bound_ident_check) = match *t {
- (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
- };
- let cr = expand_crate_str(teststr.to_string());
- let bindings = crate_bindings(&cr);
- let varrefs = crate_varrefs(&cr);
-
- // must be one check clause for each binding:
- assert_eq!(bindings.len(),bound_connections.len());
- for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
- let binding_name = mtwt::resolve(bindings[binding_idx]);
- let binding_marks = mtwt::marksof(bindings[binding_idx].ctxt, invalid_name);
- // shouldmatch can't name varrefs that don't exist:
- assert!((shouldmatch.is_empty()) ||
- (varrefs.len() > *shouldmatch.iter().max().unwrap()));
- for (idx,varref) in varrefs.iter().enumerate() {
- let print_hygiene_debug_info = || {
- // good lord, you can't make a path with 0 segments, can you?
- let final_varref_ident = match varref.segments.last() {
- Some(pathsegment) => pathsegment.identifier,
- None => panic!("varref with 0 path segments?")
- };
- let varref_name = mtwt::resolve(final_varref_ident);
- let varref_idents : Vec<ast::Ident>
- = varref.segments.iter().map(|s| s.identifier)
- .collect();
- println!("varref #{}: {:?}, resolves to {}",idx, varref_idents, varref_name);
- println!("varref's first segment's string: \"{}\"", final_varref_ident);
- println!("binding #{}: {}, resolves to {}",
- binding_idx, bindings[binding_idx], binding_name);
- mtwt::with_sctable(|x| mtwt::display_sctable(x));
- };
- if shouldmatch.contains(&idx) {
- // it should be a path of length 1, and it should
- // be free-identifier=? or bound-identifier=? to the given binding
- assert_eq!(varref.segments.len(),1);
- let varref_name = mtwt::resolve(varref.segments[0].identifier);
- let varref_marks = mtwt::marksof(varref.segments[0]
- .identifier
- .ctxt,
- invalid_name);
- if !(varref_name==binding_name) {
- println!("uh oh, should match but doesn't:");
- print_hygiene_debug_info();
- }
- assert_eq!(varref_name,binding_name);
- if bound_ident_check {
- // we're checking bound-identifier=?, and the marks
- // should be the same, too:
- assert_eq!(varref_marks,binding_marks.clone());
- }
- } else {
- let varref_name = mtwt::resolve(varref.segments[0].identifier);
- let fail = (varref.segments.len() == 1)
- && (varref_name == binding_name);
- // temp debugging:
- if fail {
- println!("failure on test {}",test_idx);
- println!("text of test case: \"{}\"", teststr);
- println!("");
- println!("uh oh, matches but shouldn't:");
- print_hygiene_debug_info();
- }
- assert!(!fail);
- }
- }
- }
- }
-
#[test]
fn fmt_in_macro_used_inside_module_macro() {
let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
/// The SCTable contains a table of SyntaxContext_'s. It
/// represents a flattened tree structure, to avoid having
/// managed pointers everywhere (that caused an ICE).
-/// the mark_memo and rename_memo fields are side-tables
+/// the `marks` and `renames` fields are side-tables
/// that ensure that adding the same mark to the same context
-/// gives you back the same context as before. This shouldn't
-/// change the semantics--everything here is immutable--but
-/// it should cut down on memory use *a lot*; applying a mark
-/// to a tree containing 50 identifiers would otherwise generate
-/// 50 new contexts
+/// gives you back the same context as before. This should cut
+/// down on memory use *a lot*; applying a mark to a tree containing
+/// 50 identifiers would otherwise generate 50 new contexts.
pub struct SCTable {
table: RefCell<Vec<SyntaxContext_>>,
- mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
- // The pair (Name,SyntaxContext) is actually one Ident, but it needs to be hashed and
- // compared as pair (name, ctxt) and not as an Ident
- rename_memo: RefCell<HashMap<(SyntaxContext,(Name,SyntaxContext),Name),SyntaxContext>>,
+ marks: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
+ renames: RefCell<HashMap<Name,SyntaxContext>>,
}
#[derive(PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy, Clone)]
pub enum SyntaxContext_ {
EmptyCtxt,
Mark (Mrk,SyntaxContext),
- /// flattening the name and syntaxcontext into the rename...
- /// HIDDEN INVARIANTS:
- /// 1) the first name in a Rename node
- /// can only be a programmer-supplied name.
- /// 2) Every Rename node with a given Name in the
- /// "to" slot must have the same name and context
- /// in the "from" slot. In essence, they're all
- /// pointers to a single "rename" event node.
- Rename (Ident,Name,SyntaxContext),
+ Rename (Name),
/// actually, IllegalCtxt may not be necessary.
IllegalCtxt
}
/// Extend a syntax context with a given mark and sctable (explicit memoization)
fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
- let key = (ctxt, m);
- *table.mark_memo.borrow_mut().entry(key).or_insert_with(|| {
- SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt)))
- })
+ let ctxts = &mut *table.table.borrow_mut();
+ match ctxts[ctxt.0 as usize] {
+ // Applying the same mark twice is a no-op.
+ Mark(outer_mark, prev_ctxt) if outer_mark == m => return prev_ctxt,
+ _ => *table.marks.borrow_mut().entry((ctxt, m)).or_insert_with(|| {
+ SyntaxContext(idx_push(ctxts, Mark(m, ctxt)))
+ }),
+ }
}
/// Extend a syntax context with a given rename
-pub fn apply_rename(id: Ident, to:Name,
- ctxt: SyntaxContext) -> SyntaxContext {
- with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
+pub fn apply_rename(from: Ident, to: Name, ident: Ident) -> Ident {
+ with_sctable(|table| apply_rename_internal(from, to, ident, table))
}
/// Extend a syntax context with a given rename and sctable (explicit memoization)
-fn apply_rename_internal(id: Ident,
- to: Name,
- ctxt: SyntaxContext,
- table: &SCTable) -> SyntaxContext {
- let key = (ctxt, (id.name, id.ctxt), to);
-
- *table.rename_memo.borrow_mut().entry(key).or_insert_with(|| {
- SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt)))
- })
+fn apply_rename_internal(from: Ident, to: Name, ident: Ident, table: &SCTable) -> Ident {
+ if (ident.name, ident.ctxt) != (from.name, from.ctxt) {
+ return ident;
+ }
+ let ctxt = *table.renames.borrow_mut().entry(to).or_insert_with(|| {
+ SyntaxContext(idx_push(&mut *table.table.borrow_mut(), Rename(to)))
+ });
+ Ident { ctxt: ctxt, ..ident }
}
/// Apply a list of renamings to a context
// if these rename lists get long, it would make sense
// to consider memoizing this fold. This may come up
// when we add hygiene to item names.
-pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
- renames.iter().fold(ctxt, |ctxt, &(from, to)| {
- apply_rename(from, to, ctxt)
+pub fn apply_renames(renames: &RenameList, ident: Ident) -> Ident {
+ renames.iter().fold(ident, |ident, &(from, to)| {
+ apply_rename(from, to, ident)
})
}
fn new_sctable_internal() -> SCTable {
SCTable {
table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
- mark_memo: RefCell::new(HashMap::new()),
- rename_memo: RefCell::new(HashMap::new()),
+ marks: RefCell::new(HashMap::new()),
+ renames: RefCell::new(HashMap::new()),
}
}
pub fn clear_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = Vec::new();
- *table.mark_memo.borrow_mut() = HashMap::new();
- *table.rename_memo.borrow_mut() = HashMap::new();
+ *table.marks.borrow_mut() = HashMap::new();
+ *table.renames.borrow_mut() = HashMap::new();
});
- with_resolve_table_mut(|table| *table = HashMap::new());
}
/// Reset the tables to their initial state
pub fn reset_tables() {
with_sctable(|table| {
*table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
- *table.mark_memo.borrow_mut() = HashMap::new();
- *table.rename_memo.borrow_mut() = HashMap::new();
+ *table.marks.borrow_mut() = HashMap::new();
+ *table.renames.borrow_mut() = HashMap::new();
});
- with_resolve_table_mut(|table| *table = HashMap::new());
}
/// Add a value to the end of a vec, return its index
/// Resolve a syntax object to a name, per MTWT.
pub fn resolve(id: Ident) -> Name {
with_sctable(|sctable| {
- with_resolve_table_mut(|resolve_table| {
- resolve_internal(id, sctable, resolve_table)
- })
+ resolve_internal(id, sctable)
})
}
-type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
-
-// okay, I admit, putting this in TLS is not so nice:
-// fetch the SCTable from TLS, create one if it doesn't yet exist.
-fn with_resolve_table_mut<T, F>(op: F) -> T where
- F: FnOnce(&mut ResolveTable) -> T,
-{
- thread_local!(static RESOLVE_TABLE_KEY: RefCell<ResolveTable> = {
- RefCell::new(HashMap::new())
- });
-
- RESOLVE_TABLE_KEY.with(move |slot| op(&mut *slot.borrow_mut()))
-}
-
/// Resolve a syntax object to a name, per MTWT.
/// adding memoization to resolve 500+ seconds in resolve for librustc (!)
-fn resolve_internal(id: Ident,
- table: &SCTable,
- resolve_table: &mut ResolveTable) -> Name {
- let key = (id.name, id.ctxt);
-
- match resolve_table.get(&key) {
- Some(&name) => return name,
- None => {}
- }
-
- let resolved = {
- let result = (*table.table.borrow())[id.ctxt.0 as usize];
- match result {
- EmptyCtxt => id.name,
- // ignore marks here:
- Mark(_,subctxt) =>
- resolve_internal(Ident::new(id.name, subctxt),
- table, resolve_table),
- // do the rename if necessary:
- Rename(Ident{name, ctxt}, toname, subctxt) => {
- let resolvedfrom =
- resolve_internal(Ident::new(name, ctxt),
- table, resolve_table);
- let resolvedthis =
- resolve_internal(Ident::new(id.name, subctxt),
- table, resolve_table);
- if (resolvedthis == resolvedfrom)
- && (marksof_internal(ctxt, resolvedthis, table)
- == marksof_internal(subctxt, resolvedthis, table)) {
- toname
- } else {
- resolvedthis
- }
- }
- IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
- }
- };
- resolve_table.insert(key, resolved);
- resolved
-}
-
-/// Compute the marks associated with a syntax context.
-pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
- with_sctable(|table| marksof_internal(ctxt, stopname, table))
-}
-
-// the internal function for computing marks
-// it's not clear to me whether it's better to use a [] mutable
-// vector or a cons-list for this.
-fn marksof_internal(ctxt: SyntaxContext,
- stopname: Name,
- table: &SCTable) -> Vec<Mrk> {
- let mut result = Vec::new();
- let mut loopvar = ctxt;
- loop {
- let table_entry = (*table.table.borrow())[loopvar.0 as usize];
- match table_entry {
- EmptyCtxt => {
- return result;
- },
- Mark(mark, tl) => {
- xor_push(&mut result, mark);
- loopvar = tl;
- },
- Rename(_,name,tl) => {
- // see MTWT for details on the purpose of the stopname.
- // short version: it prevents duplication of effort.
- if name == stopname {
- return result;
- } else {
- loopvar = tl;
- }
- }
- IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
- }
+fn resolve_internal(id: Ident, table: &SCTable) -> Name {
+ match table.table.borrow()[id.ctxt.0 as usize] {
+ EmptyCtxt => id.name,
+ // ignore marks here:
+ Mark(_, subctxt) => resolve_internal(Ident::new(id.name, subctxt), table),
+ Rename(name) => name,
+ IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
}
}
})
}
-/// Push a name... unless it matches the one on top, in which
-/// case pop and discard (so two of the same marks cancel)
-fn xor_push(marks: &mut Vec<Mrk>, mark: Mrk) {
- if (!marks.is_empty()) && (*marks.last().unwrap() == mark) {
- marks.pop().unwrap();
- } else {
- marks.push(mark);
- }
-}
-
#[cfg(test)]
mod tests {
- use self::TestSC::*;
use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
- use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal};
- use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal};
- use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
- use std::collections::HashMap;
-
- #[test]
- fn xorpush_test () {
- let mut s = Vec::new();
- xor_push(&mut s, 14);
- assert_eq!(s.clone(), [14]);
- xor_push(&mut s, 14);
- assert_eq!(s.clone(), []);
- xor_push(&mut s, 14);
- assert_eq!(s.clone(), [14]);
- xor_push(&mut s, 15);
- assert_eq!(s.clone(), [14, 15]);
- xor_push(&mut s, 16);
- assert_eq!(s.clone(), [14, 15, 16]);
- xor_push(&mut s, 16);
- assert_eq!(s.clone(), [14, 15]);
- xor_push(&mut s, 15);
- assert_eq!(s.clone(), [14]);
- }
+ use super::{resolve, apply_mark_internal, new_sctable_internal};
+ use super::{SCTable, Mark};
fn id(n: u32, s: SyntaxContext) -> Ident {
Ident::new(Name(n), s)
}
- // because of the SCTable, I now need a tidy way of
- // creating syntax objects. Sigh.
- #[derive(Clone, PartialEq, Debug)]
- enum TestSC {
- M(Mrk),
- R(Ident,Name)
- }
-
- // unfold a vector of TestSC values into a SCTable,
- // returning the resulting index
- fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
- -> SyntaxContext {
- tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
- {match *tsc {
- M(mrk) => apply_mark_internal(mrk,tail,table),
- R(ident,name) => apply_rename_internal(ident,name,tail,table)}})
- }
-
- // gather a SyntaxContext back into a vector of TestSCs
- fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
- let mut result = Vec::new();
- loop {
- let table = table.table.borrow();
- match (*table)[sc.0 as usize] {
- EmptyCtxt => {return result;},
- Mark(mrk,tail) => {
- result.push(M(mrk));
- sc = tail;
- continue;
- },
- Rename(id,name,tail) => {
- result.push(R(id,name));
- sc = tail;
- continue;
- }
- IllegalCtxt => panic!("expected resolvable context, got IllegalCtxt")
- }
- }
- }
-
- #[test]
- fn test_unfold_refold(){
- let mut t = new_sctable_internal();
-
- let test_sc = vec!(M(3),R(id(101,EMPTY_CTXT),Name(14)),M(9));
- assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),SyntaxContext(4));
- {
- let table = t.table.borrow();
- assert!((*table)[2] == Mark(9,EMPTY_CTXT));
- assert!((*table)[3] == Rename(id(101,EMPTY_CTXT),Name(14),SyntaxContext(2)));
- assert!((*table)[4] == Mark(3,SyntaxContext(3)));
- }
- assert_eq!(refold_test_sc(SyntaxContext(4),&t),test_sc);
- }
-
// extend a syntax context with a sequence of marks given
// in a vector. v[0] will be the outermost mark.
fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
}
}
- #[test]
- fn test_marksof () {
- let stopname = Name(242);
- let name1 = Name(243);
- let mut t = new_sctable_internal();
- assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
- // FIXME #5074: ANF'd to dodge nested calls
- { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
- assert_eq! (marksof_internal (ans,stopname,&t), [4, 98]);}
- // does xoring work?
- { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
- assert_eq! (marksof_internal (ans,stopname,&t), [16]);}
- // does nested xoring work?
- { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
- assert_eq! (marksof_internal (ans, stopname,&t), [16]);}
- // rename where stop doesn't match:
- { let chain = vec!(M(9),
- R(id(name1.0,
- apply_mark_internal (4, EMPTY_CTXT,&mut t)),
- Name(100101102)),
- M(14));
- let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
- assert_eq! (marksof_internal (ans, stopname, &t), [9, 14]);}
- // rename where stop does match
- { let name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t);
- let chain = vec!(M(9),
- R(id(name1.0, name1sc),
- stopname),
- M(14));
- let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
- assert_eq! (marksof_internal (ans, stopname, &t), [9]); }
- }
-
-
- #[test]
- fn resolve_tests () {
- let a = 40;
- let mut t = new_sctable_internal();
- let mut rt = HashMap::new();
- // - ctxt is MT
- assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),Name(a));
- // - simple ignored marks
- { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
- // - orthogonal rename where names don't match
- { let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),Name(51)),M(12)),EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(a));}
- // - rename where names do match, but marks don't
- { let sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t);
- let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),
- M(1),
- M(2)),
- EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(a));}
- // - rename where names and marks match
- { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
- let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50)),M(1),M(2)),EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
- // - rename where names and marks match by literal sharing
- { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
- let sc = unfold_test_sc(vec!(R(id(a,sc1),Name(50))),sc1,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(50)); }
- // - two renames of the same var.. can only happen if you use
- // local-expand to prevent the inner binding from being renamed
- // during the rename-pass caused by the first:
- println!("about to run bad test");
- { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),Name(50)),
- R(id(a,EMPTY_CTXT),Name(51))),
- EMPTY_CTXT,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), Name(51)); }
- // the simplest double-rename:
- { let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),Name(50),EMPTY_CTXT,&mut t);
- let a50_to_a51 = apply_rename_internal(id(a,a_to_a50),Name(51),a_to_a50,&mut t);
- assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),Name(51));
- // mark on the outside doesn't stop rename:
- let sc = apply_mark_internal(9,a50_to_a51,&mut t);
- assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),Name(51));
- // but mark on the inside does:
- let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),Name(51)),
- M(9)),
- a_to_a50,
- &mut t);
- assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),Name(50));}
- }
-
#[test]
fn mtwt_resolve_test(){
let a = 40;
assert_eq!(resolve(id(a,EMPTY_CTXT)),Name(a));
}
-
#[test]
fn hashing_tests () {
let mut t = new_sctable_internal();
assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),SyntaxContext(2));
// I'm assuming that the rename table will behave the same....
}
-
- #[test]
- fn resolve_table_hashing_tests() {
- let mut t = new_sctable_internal();
- let mut rt = HashMap::new();
- assert_eq!(rt.len(),0);
- resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
- assert_eq!(rt.len(),1);
- resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
- assert_eq!(rt.len(),2);
- resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
- assert_eq!(rt.len(),2);
- }
-
- #[test]
- fn new_resolves_test() {
- let renames = vec!((Ident::with_empty_ctxt(Name(23)),Name(24)),
- (Ident::with_empty_ctxt(Name(29)),Name(29)));
- let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT);
- assert_eq!(resolve(Ident::new(Name(23),new_ctxt1)),Name(24));
- assert_eq!(resolve(Ident::new(Name(29),new_ctxt1)),Name(29));
- }
}
fn expand_into_foo_multi(cx: &mut ExtCtxt,
sp: Span,
attr: &MetaItem,
- it: Annotatable) -> Annotatable {
+ it: Annotatable) -> Vec<Annotatable> {
match it {
- Annotatable::Item(it) => {
+ Annotatable::Item(it) => vec![
Annotatable::Item(P(Item {
attrs: it.attrs.clone(),
..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
- }))
- }
- Annotatable::ImplItem(it) => {
+ })),
+ Annotatable::Item(quote_item!(cx, enum Foo3 { Bar }).unwrap()),
+ ],
+ Annotatable::ImplItem(it) => vec![
quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Impl(_, _, _, _, _, mut items) => {
_ => unreachable!("impl parsed to something other than impl")
}
})
- }
- Annotatable::TraitItem(it) => {
+ ],
+ Annotatable::TraitItem(it) => vec![
quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Trait(_, _, _, mut items) => {
_ => unreachable!("trait parsed to something other than trait")
}
})
- }
+ ],
}
}
assert_eq!(Foo2::Bar2, Foo2::Bar2);
test(None::<Foo2>);
+ let _ = Foo3::Bar;
+
let x = 10i32;
assert_eq!(x.foo(), 42);
let x = 10u8;
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+fn f() {
+ let x = 0;
+ macro_rules! foo { () => {
+ assert_eq!(x, 0);
+ } }
+
+ let x = 1;
+ foo!();
+}
+
+fn g() {
+ let x = 0;
+ macro_rules! m { ($x:ident) => {
+ macro_rules! m2 { () => { ($x, x) } }
+ let x = 1;
+ macro_rules! m3 { () => { ($x, x) } }
+ } }
+
+ let x = 2;
+ m!(x);
+
+ let x = 3;
+ assert_eq!(m2!(), (2, 0));
+ assert_eq!(m3!(), (2, 1));
+
+ let x = 4;
+ m!(x);
+ assert_eq!(m2!(), (4, 0));
+ assert_eq!(m3!(), (4, 1));
+}
+
+mod foo {
+ macro_rules! m {
+ ($f:ident : |$x:ident| $e:expr) => {
+ pub fn $f() -> (i32, i32) {
+ let x = 0;
+ let $x = 1;
+ (x, $e)
+ }
+ }
+ }
+
+ m!(f: |x| x + 10);
+}
+
+fn interpolated_pattern() {
+ let x = 0;
+ macro_rules! m {
+ ($p:pat, $e:expr) => {
+ let $p = 1;
+ assert_eq!((x, $e), (0, 1));
+ }
+ }
+
+ m!(x, x);
+}
+
+fn patterns_in_macro_generated_macros() {
+ let x = 0;
+ macro_rules! m {
+ ($a:expr, $b:expr) => {
+ assert_eq!(x, 0);
+ let x = $a;
+ macro_rules! n {
+ () => {
+ (x, $b)
+ }
+ }
+ }
+ }
+
+ let x = 1;
+ m!(2, x);
+
+ let x = 3;
+ assert_eq!(n!(), (2, 1));
+}
+
+fn match_hygiene() {
+ let x = 0;
+
+ macro_rules! m {
+ ($p:pat, $e:expr) => {
+ for result in &[Ok(1), Err(1)] {
+ match *result {
+ $p => { assert_eq!(($e, x), (1, 0)); }
+ Err(x) => { assert_eq!(($e, x), (2, 1)); }
+ }
+ }
+ }
+ }
+
+ let x = 2;
+ m!(Ok(x), x);
+}
+
+fn main() {
+ f();
+ g();
+ assert_eq!(foo::f(), (0, 11));
+ interpolated_pattern();
+ patterns_in_macro_generated_macros();
+ match_hygiene();
+}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(missing_docs)]
+//! this tests the syntax of `thread_local!`
+
+thread_local! {
+ // no docs
+ #[allow(unused)]
+ static FOO: i32 = 42;
+ /// docs
+ pub static BAR: String = String::from("bar");
+}
+thread_local!(static BAZ: u32 = 0);
+
+fn main() {}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub struct Foo;
+
+pub trait Bar {
+ #[doc(hidden)]
+ fn bar() {}
+}
+
+impl Bar for Foo {
+ fn bar() {}
+}