A new user interface for you! Read more...

File elixir-master.patch of Package elixir

diff --git a/.travis.yml b/.travis.yml
index 8bcd94869..960c611ae 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,6 +11,7 @@ env:
     - OTP_RELEASE=OTP-20.3
     - OTP_RELEASE=OTP-21.0
     - OTP_RELEASE=OTP-21.1
+    - OTP_RELEASE=OTP-21.2
     - OTP_RELEASE=maint
     - OTP_RELEASE=master
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f8dc31662..7868c0ab8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,179 +1,45 @@
-# Changelog for Elixir v1.8
+# Changelog for Elixir v1.9
 
-Elixir v1.8 comes with many improvements at the infrastructure level, improving compilation time, speeding up common patterns, and adding features around introspection of the system.
-
-## Custom struct inspections
-
-Elixir now provides a derivable implementation of the `Inspect` protocol. In a nutshell, this means it is really easy to filter data from your data structures whenever they are inspected. For example, imagine you have a user struct with security and privacy sensitive information:
-
-```elixir
-defmodule User do
-  defstruct [:id, :name, :age, :email, :encrypted_password]
-end
-```
-
-By default, if you inspect a user via `inspect(user)`, it will include all fields. This can cause fields such as `:email` and `:encrypted_password` to appear in logs, error reports, etc. You could always define a custom implementation of the `Inspect` protocol for such cases but Elixir v1.8 makes it simpler by allowing you to derive the `Inspect` protocol:
-
-```elixir
-defmodule User do
-  @derive {Inspect, only: [:id, :name, :age]}
-  defstruct [:id, :name, :age, :email, :encrypted_password]
-end
-```
-
-Now all user structs will be printed with all remaining fields collapsed:
-
-    #User<id: 1, name: "Jane", age: 33, ...>
-
-You can also pass `@derive {Inspect, except: [...]}` in case you want to keep all fields by default and exclude only some.
-
-## Time zone database support
-
-In Elixir v1.3, Elixir added four types, known as Calendar types, to work with dates and times: `Time`, `Date`, `NaiveDateTime` (without time zone) and `DateTime` (with time zone). Over the last releases we have added many enhancements to the Calendar types but the `DateTime` module always evolved at a slower pace since Elixir did not provide support for a time zone database.
-
-Elixir v1.8 now defines a `Calendar.TimeZoneDatabase` behaviour, allowing developers to bring in their own time zone databases. By defining an explicit contract for time zone behaviours, Elixir can now extend the `DateTime` API, adding functions such as `DateTime.shift_zone/3`. By default, Elixir ships with a time zone database called `Calendar.UTCOnlyTimeZoneDatabase` that only handles UTC.
-
-Other Calendar related improvements include the addition of `Date.day_of_year/1`, `Date.quarter_of_year/1`, `Date.year_of_era/1`, and `Date.day_of_era/1`.
-
-## Faster compilation and other performance improvements
-
-Due to improvements to the compiler made over the last year, Elixir v1.8 should compile code about 5% faster on average. This is yet another release where we have been able to reduce compilation times and provide a more joyful development experience to everyone.
-
-The compiler also emits more efficient code for range checks in guards (such as `x in y..z`), for charlists with interpolation (such as `'foo #{bar} baz'`), and when working with records via the `Record` module.
-
-Finally, EEx templates got their own share of optimizations, emitting more compact code that runs faster.
-
-## Improved instrumentation and ownership with `$callers`
-
-The `Task` module is one of the most common ways to spawn light-weight processes to perform work concurrently. Whenever you spawn a new process, Elixir annotates the parent of that process through the `$ancestors` key. This information can be used by instrumentation tools to track the relationship between events occurring within multiple processes. However, many times, tracking only the `$ancestors` is not enough.
-
-For example, we recommend developers to always start tasks under a supervisor. This provides more visibility and allows us to control how those tasks are terminated when a node shuts down. In your code, this can be done by invoking something like: `Task.Supervisor.start_child(MySupervisor, task_specification)`. This means that, although your code is the one who invokes the task, the actual parent of the task would be the supervisor, as the supervisor is the one spawning it. We would list the supervisor as one of the `$ancestors` for the task, but the relationship between your code and the task is lost.
-
-In Elixir v1.8, we now track the relationship between your code and the task via the `$callers` key in the process dictionary, which aligns well with the existing `$ancestors` key. Therefore, assuming the `Task.Supervisor` call above, we have:
-
-    [your code] -- calls --> [supervisor] ---- spawns --> [task]
-
-which means we store the following relationships:
-
-    [your code]              [supervisor] <-- ancestor -- [task]
-         ^                                                  |
-         |--------------------- caller ---------------------|
-
-When a task is spawned directly from your code, without a supervisor, then the process running your code will be listed under both `$ancestors` and `$callers`.
-
-This small feature is very powerful. It allows instrumentation and monitoring tools to better track and relate the events happening in your system. This feature can also be used by tools like the "Ecto Sandbox". The "Ecto Sandbox" allows developers to run tests concurrently against the database, by using transactions and an ownership mechanism where each process explicitly gets a connection assigned to it. Without `$callers`, every time you spawned a task that queries the database, the task would not know its caller, and therefore it would be unable to know which connection was assigned to it. This often meant features that relies on tasks could not be tested concurrently. With `$callers`, figuring out this relationship is trivial and you have more tests using the full power of your machine.
-
-## v1.8.0 (2018-01-14)
+## v1.9.0-dev
 
 ### 1. Enhancements
 
 #### EEx
 
-  * [EEx] Optimize the default template engine to compile and execute more efficiently
+  * [EEx] Allow more complex mixed expression when tokenizing
 
 #### Elixir
 
-  * [Calendar] Add `Calendar.TimeZoneDatabase` and a `Calendar.UTCOnlyTimeZoneDatabase` implementation
-  * [Calendar] Add callbacks `day_of_year/3`, `quarter_of_year/3`, `year_of_era/1`, and `day_of_era/3`
-  * [Code.Formatter] Preserve user's choice of new line after most operators
-  * [Date] Add `Date.day_of_year/1`, `Date.quarter_of_year/1`, `Date.year_of_era/1`, and `Date.day_of_era/1`
-  * [DateTime] Add `DateTime.from_naive/3`, `DateTime.now/1`, and `DateTime.shift_zone/3`
-  * [File] Allow `:raw` option in `File.exists?/2`, `File.regular?/2`, and `File.dir?/2`
-  * [File] Allow POSIX time as an integer in `File.touch/2` and `File.touch!/2`
-  * [Inspect] Allow `Inspect` protocol to be derivable with the `:only`/`:except` options
-  * [Kernel] Do not propagate counters to variables in quote inside another quote
-  * [Kernel] Warn on ambiguous use of `::` and `|` in typespecs
-  * [Kernel] Add `:delegate_to` `@doc` metadata tag when using `defdelegate`
-  * [Kernel] Improve compile-time building of ranges via the `..` operator
-  * [Kernel] Compile charlist interpolation more efficiently
-  * [Kernel] Add `floor/1` and `ceil/1` guards
-  * [Kernel.SpecialForms] Add `:reduce` option to `for` comprehensions
-  * [List] Add `List.myers_difference/3` and `List.improper?/1`
-  * [Macro] Add `Macro.struct!/2` for proper struct resolution during compile time
-  * [Map] Optimize and merge nested maps `put` and `merge` operations
-  * [Range] Add `Range.disjoint?/2`
-  * [Record] Reduce memory allocation when updating multiple fields in a record
-  * [Registry] Allow associating a value on `:via` tuple
-  * [String] Add `String.bag_distance/2`
-  * [Task] Add `$callers` tracking to `Task` - this makes it easier to find which process spawned a task and use it for tracking ownership and monitoring
+  * [CLI] Add support for `--boot`, `--boot-var`, `--erl-config`, `--pipe-to`, `--rpc-eval`, and `--vm-args` options
+  * [Protocol] Improve Protocol.UndefinedError messages to also include the type that was attempted to dispatch on
+  * [System] Add `System.restart/0` and `System.pid/0`
 
 #### ExUnit
 
-  * [ExUnit] Add `ExUnit.after_suite/1` callback
-  * [ExUnit.Assertions] Show last N messages (instead of first N) from mailbox on `assert_receive` fail
-
-#### IEx
-
-  * [IEx.Helpers] Add `port/1` and `port/2`
-  * [IEx.Server] Expose `IEx.Server.run/1` for custom IEx sessions with the ability to broker pry sessions
+  * [ExUnit.DocTest] No longer wrap doctest errors in custom exceptions. They ended-up hiding more information than showing
 
-#### Mix
+#### Logger
 
-  * [Mix] Add `Mix.target/0` and `Mix.target/1` to control dependency management per target
-  * [Mix.Project] Add `:depth` and `:parents` options to `deps_paths/1`
-  * [mix archive.install] Add a timeout when installing archives
-  * [mix compile] Include optional dependencies in `:extra_applications`
-  * [mix escript.install] Add a timeout when installing escripts
-  * [mix format] Warn when the same file may be formatted by multiple `.formatter.exs`
-  * [mix test] Allow setting the maximum number of failures via `--max-failures`
-  * [mix test] Print a message instead of raising on unmatched tests inside umbrella projects
+  * [Logger] Use a descentralized mode computation for Logger which allows overloads to be detect more quickly
 
 ### 2. Bug fixes
 
 #### Elixir
 
-  * [Calendar] Allow printing dates with more than 9999 years
-  * [Exception] Exclude deprecated functions in "did you mean?" hints
-  * [Float] Handle subnormal floats in `Float.ratio/1`
-  * [Kernel] Remove `Guard test tuple_size(...) can never succeed` Dialyzer warning on `try`
-  * [Kernel] Expand operands in `size*unit` bitstring modifier instead of expecting `size` and `unit` to be literal integers
-  * [Kernel] Do not deadlock on circular struct dependencies in typespecs
-  * [Kernel] Raise proper error message when passing flags to the Erlang compiler that Elixir cannot handle
-  * [Kernel] Do not leak variables in `cond` clauses with a single matching at compile-time clause
-  * [NaiveDateTime] Do not accept leap seconds in builder and parsing functions
-  * [String] Fix ZWJ handling in Unicode grapheme clusters
-  * [StringIO] Handle non-printable args in StringIO gracefully
-
-#### IEx
-
-  * [IEx.Helpers] Use typespec info (instead of docs chunk) and properly format callbacks in `b/1`
-
-#### Logger
-
-  * [Logger] Allow Logger backends to be dynamically removed when an application is shutting down
-
-#### Mix
-
-  * [mix compile] Ensure changes in deps propagate to all umbrella children - this fix a long standing issue where updating a dependency would not recompile all projects accordingly, requiring a complete removal of `_build`
-  * [mix compile] Avoid time drift when checking and updating compiler manifest files
-  * [mix compile.app] Respect the `:only` option between umbrella siblings
-  * [mix compile.protocols] Reconsolidate protocols if local dependencies are stale
-  * [mix deps] Properly mark dependencies with different `:system_env` as diverged
-  * [mix new] Use `--module` value when setting up filenames
+  * [Kernel] Properly merge and handle docs for callbacks with multiple clauses
 
 ### 3. Soft-deprecations (no warnings emitted)
 
-None.
-
 ### 4. Hard-deprecations
 
 #### Elixir
 
-  * [Enum] Passing a non-empty list to `Enum.into/2` was inconsistent with maps and is deprecated in favor of `Kernel.++/2` or `Keyword.merge/2`
-  * [Inspect.Algebra] `surround/3` is deprecated in favor of `Inspect.Algebra.concat/2` and `Inspect.Algebra.nest/2`
-  * [Inspect.Algebra] `surround_many/6` is deprecated in favor of `container_doc/6`
-  * [Kernel] Using `@since` will now emit a unused attribute warning. Use `@doc since: "1.7.2"` instead
-  * [Kernel] Passing a non-empty list as `:into` in `for` comprehensions was inconsistent with maps and is deprecated in favor of `Kernel.++/2` or `Keyword.merge/2`
-  * [Kernel.ParallelCompiler] `files/2` is deprecated in favor of `compile/2`
-  * [Kernel.ParallelCompiler] `files_to_path/2` is deprecated in favor of `compile_to_path/2`
-  * [Kernel.ParallelRequire] `files/2` is deprecated in favor of `Kernel.ParallelCompiler.require/2`
-  * [System] `:seconds`, `:milliseconds`, etc. as time units is deprecated in favor of `:second`, `:millisecond`, etc.
-  * [System] `System.cwd/0` and `System.cwd!/0` are deprecated in favor of `File.cwd/0` and `File.cwd!/0`
+  * [CLI] Deprecate `--detached` option, use `--erl "-detached"` instead
 
 #### Mix
 
-  * [mix compile.erlang] Returning `{:ok, contents}` or `:error` as the callback in `Mix.Compilers.Erlang.compile/6` is deprecated in favor of returning `{:ok, contents, warnings}` or `{:error, errors, warnings}`
+  * [Mix.Project] Deprecate `Mix.Project.load_paths/1` in favor of `Mix.Project.compile_path/1`
 
-## v1.7
+## v1.8
 
-The CHANGELOG for v1.7 releases can be found [in the v1.7 branch](https://github.com/elixir-lang/elixir/blob/v1.7/CHANGELOG.md).
+The CHANGELOG for v1.8 releases can be found [in the v1.8 branch](https://github.com/elixir-lang/elixir/blob/v1.8/CHANGELOG.md).
diff --git a/Makefile b/Makefile
index 8367c76b3..727f959b3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 PREFIX ?= /usr/local
 SHARE_PREFIX ?= $(PREFIX)/share
 MAN_PREFIX ?= $(SHARE_PREFIX)/man
-CANONICAL := v1.8/ # master/ or vMAJOR.MINOR/
+CANONICAL := master/ # master/ or vMAJOR.MINOR/
 ELIXIRC := bin/elixirc --verbose --ignore-module-conflict --warnings-as-errors
 ERLC := erlc -I lib/elixir/include +warnings_as_errors
 ERL := erl -I lib/elixir/include -noshell -pa lib/elixir/ebin
diff --git a/VERSION b/VERSION
index afa2b3515..1b19bcbc6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.8.0
\ No newline at end of file
+1.9.0-dev
\ No newline at end of file
diff --git a/bin/elixir b/bin/elixir
index e4f7b7a07..6feee5fdb 100755
--- a/bin/elixir
+++ b/bin/elixir
@@ -1,31 +1,59 @@
 #!/bin/sh
+set -e
+
 if [ $# -eq 0 ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
-  echo "Usage: `basename $0` [options] [.exs file] [data]
-
-  -e COMMAND                  Evaluates the given command (*)
-  -r FILE                     Requires the given files/patterns (*)
-  -S SCRIPT                   Finds and executes the given script in PATH
-  -pr FILE                    Requires the given files/patterns in parallel (*)
-  -pa PATH                    Prepends the given path to Erlang code path (*)
-  -pz PATH                    Appends the given path to Erlang code path (*)
-
-  --app APP                   Starts the given app and its dependencies (*)
-  --cookie COOKIE             Sets a cookie for this distributed node
-  --detached                  Starts the Erlang VM detached from console
-  --erl SWITCHES              Switches to be passed down to Erlang (*)
-  --help, -h                  Prints this message and exits
-  --hidden                    Makes a hidden node
-  --logger-otp-reports BOOL   Enables or disables OTP reporting
-  --logger-sasl-reports BOOL  Enables or disables SASL reporting
-  --name NAME                 Makes and assigns a name to the distributed node
-  --no-halt                   Does not halt the Erlang VM after execution
-  --sname NAME                Makes and assigns a short name to the distributed node
-  --version, -v               Prints Elixir version and exits
-  --werl                      Uses Erlang's Windows shell GUI (Windows only)
-
-** Options marked with (*) can be given more than once
-** Options given after the .exs file or -- are passed down to the executed code
-** Options can be passed to the Erlang runtime using ELIXIR_ERL_OPTIONS or --erl" >&2
+  echo "Usage: $(basename $0) [options] [.exs file] [data]
+
+## General options
+
+  -e \"COMMAND\"                 Evaluates the given command (*)
+  -h, --help                   Prints this message and exits
+  -r \"FILE\"                    Requires the given files/patterns (*)
+  -S SCRIPT                    Finds and executes the given script in \$PATH
+  -pr \"FILE\"                   Requires the given files/patterns in parallel (*)
+  -pa \"PATH\"                   Prepends the given path to Erlang code path (*)
+  -pz \"PATH\"                   Appends the given path to Erlang code path (*)
+  -v, --version                Prints Elixir version and exits
+
+  --app APP                    Starts the given app and its dependencies (*)
+  --erl \"SWITCHES\"             Switches to be passed down to Erlang (*)
+  --eval \"COMMAND\"             Evaluates the given command, same as -e (*)
+  --logger-otp-reports BOOL    Enables or disables OTP reporting
+  --logger-sasl-reports BOOL   Enables or disables SASL reporting
+  --no-halt                    Does not halt the Erlang VM after execution
+  --werl                       Uses Erlang's Windows shell GUI (Windows only)
+
+Options given after the .exs file or -- are passed down to the executed code.
+Options can be passed to the Erlang runtime using \$ELIXIR_ERL_OPTIONS or --erl.
+
+## Distribution options
+
+The following options are related to node distribution.
+
+  --cookie COOKIE              Sets a cookie for this distributed node
+  --hidden                     Makes a hidden node
+  --name NAME                  Makes and assigns a name to the distributed node
+  --rpc-eval NODE \"COMMAND\"    Evaluates the given command on the given remote node (*)
+  --sname NAME                 Makes and assigns a short name to the distributed node
+
+## Release options
+
+The following options are generally used under releases.
+
+  --boot \"FILE\"                Uses the given FILE.boot to start the system
+  --boot-var VAR \"VALUE\"       Makes \$VAR available as VALUE to FILE.boot (*)
+  --erl-config \"FILE\"          Loads configuration in FILE.config written in Erlang (*)
+  --pipe-to \"PIPEDIR\" \"LOGDIR\" Starts the Erlang VM as a named PIPEDIR and LOGDIR
+  --vm-args \"FILE\"             Passes the contents in file as arguments to the VM
+
+--pipe-to starts Elixir detached from console (Unix-like only).
+It will attempt to create PIPEDIR and LOGDIR if they don't exist.
+See run_erl to learn more. To reattach, run: to_erl PIPEDIR.
+
+--pipe-to automatically sets \$HEART_COMMAND if none is set.
+See the -heart mode of the Erlang VM for more information.
+
+** Options marked with (*) can be given more than once." >&2
   exit 1
 fi
 
@@ -35,7 +63,7 @@ readlink_f () {
   if [ -h "$filename" ]; then
     readlink_f "$(readlink "$filename")"
   else
-    echo "`pwd -P`/$filename"
+    echo "$(pwd -P)/$filename"
   fi
 }
 
@@ -43,81 +71,129 @@ MODE="elixir"
 ERL_EXEC="erl"
 ERL=""
 I=1
+LENGTH=$#
 
-while [ $I -le $# ]; do
+while [ $I -le $LENGTH ]; do
   S=1
-  eval "PEEK=\${$I}"
-  case "$PEEK" in
+  case "$1" in
     +iex)
+        set -- "$@" "$1"
         MODE="iex"
         ;;
     +elixirc)
+        set -- "$@" "$1"
         MODE="elixirc"
         ;;
-    -v|--compile|--no-halt)
+    -v|--no-halt)
+        set -- "$@" "$1"
         ;;
-    -e|-r|-pr|-pa|-pz|--remsh|--app)
+    -e|-r|-pr|-pa|-pz|--app|--eval|--remsh)
         S=2
+        set -- "$@" "$1" "$2"
+        ;;
+    --rpc-eval)
+        S=3
+        set -- "$@" "$1" "$2" "$3"
         ;;
-    --detached|--hidden)
-        ERL="$ERL `echo $PEEK | cut -c 2-`"
+    --detached)
+        echo "warning: the --detached option is deprecated" >&2
+        ERL="$ERL -detached"
+        ;;
+    --hidden)
+        ERL="$ERL -hidden"
         ;;
     --cookie)
-        I=$(expr $I + 1)
-        eval "VAL=\${$I}"
-        ERL="$ERL -setcookie "$VAL""
+        S=2
+        ERL="$ERL -setcookie "$2""
         ;;
     --sname|--name)
-        I=$(expr $I + 1)
-        eval "VAL=\${$I}"
-        ERL="$ERL `echo $PEEK | cut -c 2-` "$VAL""
+        S=2
+        ERL="$ERL $(echo $1 | cut -c 2-) "$2""
         ;;
     --logger-otp-reports)
-        I=$(expr $I + 1)
-        eval "VAL=\${$I}"
-        if [ "$VAL" = 'true' ] || [ "$VAL" = 'false' ]; then
-            ERL="$ERL -logger handle_otp_reports "$VAL""
+        S=2
+        if [ "$2" = 'true' ] || [ "$2" = 'false' ]; then
+          ERL="$ERL -logger handle_otp_reports "$2""
         fi
         ;;
     --logger-sasl-reports)
-        I=$(expr $I + 1)
-        eval "VAL=\${$I}"
-        if [ "$VAL" = 'true' ] || [ "$VAL" = 'false' ]; then
-            ERL="$ERL -logger handle_sasl_reports "$VAL""
+        S=2
+        if [ "$2" = 'true' ] || [ "$2" = 'false' ]; then
+          ERL="$ERL -logger handle_sasl_reports "$2""
         fi
         ;;
     --erl)
-        I=$(expr $I + 1)
-        eval "VAL=\${$I}"
-        ERL="$ERL "$VAL""
+        S=2
+        ERL="$ERL "$2""
+        ;;
+    --erl-config)
+        S=2
+        ERL="$ERL -config "$2""
+        ;;
+    --vm-args)
+        S=2
+        ERL="$ERL -args_file "$2""
+        ;;
+    --boot)
+        S=2
+        ERL="$ERL -boot "$2""
+        ;;
+    --boot-var)
+        S=3
+        ERL="$ERL -boot_var "$2" "$3""
+        ;;
+    --pipe-to)
+        S=3
+        RUN_ERL_PIPE="$2"
+        if [[ "$RUN_ERL_PIPE" == "-"* ]]; then
+          echo "--pipe-to : PIPEDIR cannot be a switch" >&2
+          exit 1
+        fi
+
+        RUN_ERL_LOG="$3"
+        if [[ "$RUN_ERL_LOG" == "-"* ]]; then
+          echo "--pipe-to : LOGDIR cannot be a switch" >&2
+          exit 1
+        fi
         ;;
     --werl)
         USE_WERL=true
         ;;
     *)
+        while [ $I -le $LENGTH ]; do
+          I=$(($I + 1))
+          set -- "$@" "$1"
+          shift
+        done
         break
         ;;
   esac
-  I=$(expr $I + $S)
+
+  I=$(($I + $S))
+  shift $S
 done
 
 SELF=$(readlink_f "$0")
 SCRIPT_PATH=$(dirname "$SELF")
+ERTS_BIN=""
 
 if [ "$OSTYPE" = "cygwin" ]; then SCRIPT_PATH=$(cygpath -m "$SCRIPT_PATH"); fi
 if [ "$MODE" != "iex" ]; then ERL="-noshell -s elixir start_cli $ERL"; fi
 
-# Check for terminal support
 if [ "$OS" != "Windows_NT" ]; then
   if test -t 1 -a -t 2; then ERL="-elixir ansi_enabled true $ERL"; fi
+else
+  if [ $USE_WERL ]; then ERL_EXEC="werl"; fi
 fi
 
-if [ "$OS" = "Windows_NT" ] && [ $USE_WERL ]; then
-    ERL_EXEC="werl"
-fi
+set -- "$ERTS_BIN$ERL_EXEC" -pa "$SCRIPT_PATH"/../lib/*/ebin $ELIXIR_ERL_OPTIONS $ERL -extra "$@"
 
-if [ -z "$ERL_PATH" ]; then
-  ERL_PATH="$ERL_EXEC"
+if [ -n "$RUN_ERL_PIPE" ]; then
+  mkdir -p "$RUN_ERL_PIPE"
+  mkdir -p "$RUN_ERL_LOG"
+  ERL_EXEC="run_erl"
+  set -- "$ERTS_BIN$ERL_EXEC" -daemon "$RUN_ERL_PIPE/" "$RUN_ERL_LOG/" "$(printf "%q " "$@")"
+  export HEART_COMMAND=${HEART_COMMAND:-$(printf "%q " "$@")}
 fi
 
-exec "$ERL_PATH" -pa "$SCRIPT_PATH"/../lib/*/ebin $ELIXIR_ERL_OPTIONS $ERL -extra "$@"
+exec "$@"
diff --git a/bin/elixir.bat b/bin/elixir.bat
index 16eb70ae4..68dd0a5e6 100644
--- a/bin/elixir.bat
+++ b/bin/elixir.bat
@@ -1,5 +1,5 @@
 @if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)
-setlocal
+setlocal enabledelayedexpansion
 if    ""%1""==""""       goto documentation
 if /I ""%1""==""--help"" goto documentation
 if /I ""%1""==""-h""     goto documentation
@@ -10,105 +10,157 @@ goto parseopts
 :documentation
 echo Usage: %~nx0 [options] [.exs file] [data]
 echo.
-echo   -e COMMAND                  Evaluates the given command (*)
-echo   -r FILE                     Requires the given files/patterns (*)
-echo   -S SCRIPT                   Finds and executes the given script in PATH
-echo   -pr FILE                    Requires the given files/patterns in parallel (*)
-echo   -pa PATH                    Prepends the given path to Erlang code path (*)
-echo   -pz PATH                    Appends the given path to Erlang code path (*)
+echo ## General options
+echo.
+echo   -e "COMMAND"                 Evaluates the given command (*)
+echo   -h, --help                   Prints this message and exits
+echo   -r "FILE"                    Requires the given files/patterns (*)
+echo   -S SCRIPT                    Finds and executes the given script in $PATH
+echo   -pr "FILE"                   Requires the given files/patterns in parallel (*)
+echo   -pa "PATH"                   Prepends the given path to Erlang code path (*)
+echo   -pz "PATH"                   Appends the given path to Erlang code path (*)
+echo   -v, --version                Prints Elixir version and exits
+echo.
+echo   --app APP                    Starts the given app and its dependencies (*)
+echo   --erl "SWITCHES"             Switches to be passed down to Erlang (*)
+echo   --eval "COMMAND"             Evaluates the given command, same as -e (*)
+echo   --logger-otp-reports BOOL    Enables or disables OTP reporting
+echo   --logger-sasl-reports BOOL   Enables or disables SASL reporting
+echo   --no-halt                    Does not halt the Erlang VM after execution
+echo   --werl                       Uses Erlang's Windows shell GUI (Windows only)
+echo.
+echo Options given after the .exs file or -- are passed down to the executed code.
+echo Options can be passed to the Erlang runtime using $ELIXIR_ERL_OPTIONS or --erl.
+echo.
+echo ## Distribution options
+echo.
+echo The following options are related to node distribution.
 echo.
-echo   --app APP                   Starts the given app and its dependencies (*)
-echo   --cookie COOKIE             Sets a cookie for this distributed node
-echo   --detached                  Starts the Erlang VM detached from console
-echo   --erl SWITCHES              Switches to be passed down to Erlang (*)
-echo   --help, -h                  Prints this message and exits
-echo   --hidden                    Makes a hidden node
-echo   --logger-otp-reports BOOL   Enables or disables OTP reporting
-echo   --logger-sasl-reports BOOL  Enables or disables SASL reporting
-echo   --name NAME                 Makes and assigns a name to the distributed node
-echo   --no-halt                   Does not halt the Erlang VM after execution
-echo   --sname NAME                Makes and assigns a short name to the distributed node
-echo   --version, -v               Prints Elixir version and exits
-echo   --werl                      Uses Erlang's Windows shell GUI
+echo   --cookie COOKIE              Sets a cookie for this distributed node
+echo   --hidden                     Makes a hidden node
+echo   --name NAME                  Makes and assigns a name to the distributed node
+echo   --rpc-eval NODE "COMMAND"    Evaluates the given command on the given remote node (*)
+echo   --sname NAME                 Makes and assigns a short name to the distributed node
 echo.
-echo ** Options marked with (*) can be given more than once
-echo ** Options given after the .exs file or -- are passed down to the executed code
-echo ** Options can be passed to the Erlang runtime using ELIXIR_ERL_OPTIONS or --erl
+echo ## Release options
+echo.
+echo The following options are generally used under releases.
+echo.
+echo   --boot "FILE"                Uses the given FILE.boot to start the system
+echo   --boot-var VAR "VALUE"       Makes $VAR available as VALUE to FILE.boot (*)
+echo   --erl-config "FILE"          Loads configuration in FILE.config written in Erlang (*)
+echo   --vm-args "FILE"             Passes the contents in file as arguments to the VM
+echo.
+echo --pipe-to is not supported on Windows. If set, Elixir won't boot.
+echo.
+echo ** Options marked with (*) can be given more than once.
 goto end
 
 :parseopts
 
+rem Parameters for Elixir
+set parsElixir=
+
 rem Parameters for Erlang
 set parsErlang=
 
-rem Make sure we keep a copy of all parameters
-set allPars=%*
-
-rem Get the original path name from the batch file
-set originPath=%~dp0
-
 rem Optional parameters before the "-extra" parameter
 set beforeExtra=
 
-rem Option which determines whether or not to use werl vs erl
+rem Option which determines whether the loop is over
+set endLoop=0
+
+rem Option which determines whether to use werl or erl
 set useWerl=0
 
 rem Designates which mode / Elixir component to run as
 set runMode="elixir"
 
+rem Designates the path to the current script
+set SCRIPT_PATH=%~dp0
+
+rem Designates the path to the ERTS system
+set ERTS_BIN=""
+
 rem Recursive loop called for each parameter that parses the cmd line parameters
 :startloop
-set par="%1"
-shift
-if "%par%"=="" (
-  rem if no parameters defined
+set "par=%~1"
+if "!par!"=="" (
+  rem skip if no parameter
   goto expand_erl_libs
 )
-if "%par%"=="""" (
-  rem if no parameters defined - special case for parameter that is already quoted
-  goto expand_erl_libs
+shift
+set par="!par:"=\"!"
+if !endLoop! == 1 (
+  set parsElixir=!parsElixir! !par!
+  goto startloop
 )
 rem ******* EXECUTION OPTIONS **********************
-if "%par%"==""--werl"" (set useWerl=1)
-if "%par%"==""+iex"" (set runMode="iex")
+if !par!=="--werl"   (set useWerl=1 && goto startloop)
+if !par!=="+iex"     (set parsElixir=!parsElixir! +iex && set runMode="iex" && goto startloop)
+if !par!=="+elixirc" (set parsElixir=!parsElixir! +elixirc && set runMode="elixirc" && goto startloop)
+rem ******* EVAL PARAMETERS ************************
+if ""==!par:-e=! (
+  set "VAR=%~1"
+  set parsElixir=!parsElixir! -e "!VAR:"=\"!"
+  shift
+  goto startloop
+)
+if ""==!par:--eval=! (
+  set "VAR=%~1"
+  set parsElixir=!parsElixir! --eval "!VAR:"=\"!"
+  shift
+  goto startloop
+)
+if ""==!par:--rpc-eval=! (
+  set "VAR=%~2"
+  set parsElixir=!parsElixir! --rpc-eval %1 "!VAR:"=\"!"
+  shift
+  shift
+  goto startloop
+)
 rem ******* ELIXIR PARAMETERS **********************
-rem Note: we don't have to do anything with options that don't take an argument
-if """"=="%par:-e=%"      (shift)
-if """"=="%par:-r=%"      (shift)
-if """"=="%par:-pr=%"     (shift)
-if """"=="%par:-pa=%"     (shift)
-if """"=="%par:-pz=%"     (shift)
-if """"=="%par:--app=%"   (shift)
-if """"=="%par:--remsh=%" (shift)
+if ""==!par:-r=!          (set parsElixir=!parsElixir! -r %1 && shift && goto startloop)
+if ""==!par:-pr=!         (set parsElixir=!parsElixir! -pr %1 && shift && goto startloop)
+if ""==!par:-pa=!         (set parsElixir=!parsElixir! -pa %1 && shift && goto startloop)
+if ""==!par:-pz=!         (set parsElixir=!parsElixir! -pz %1 && shift && goto startloop)
+if ""==!par:-v=!          (set parsElixir=!parsElixir! -v && goto startloop)
+if ""==!par:--app=!       (set parsElixir=!parsElixir! --app %1 && shift && goto startloop)
+if ""==!par:--no-halt=!   (set parsElixir=!parsElixir! --no-halt && goto startloop)
+if ""==!par:--remsh=!     (set parsElixir=!parsElixir! --remsh %1 && shift && goto startloop)
 rem ******* ERLANG PARAMETERS **********************
-if """"=="%par:--detached=%"            (set parsErlang=%parsErlang% -detached)
-if """"=="%par:--hidden=%"              (set parsErlang=%parsErlang% -hidden)
-if """"=="%par:--cookie=%"              (set parsErlang=%parsErlang% -setcookie %1 && shift)
-if """"=="%par:--sname=%"               (set parsErlang=%parsErlang% -sname %1 && shift)
-if """"=="%par:--name=%"                (set parsErlang=%parsErlang% -name %1 && shift)
-if """"=="%par:--logger-otp-reports=%"  (set parsErlang=%parsErlang% -logger handle_otp_reports %1 && shift)
-if """"=="%par:--logger-sasl-reports=%" (set parsErlang=%parsErlang% -logger handle_sasl_reports %1 && shift)
-if """"=="%par:--erl=%"                 (set "beforeExtra=%beforeExtra% %~1" && shift)
-goto:startloop
+if ""==!par:--boot=!                (set parsErlang=!parsErlang! -boot %1 && shift && goto startloop)
+if ""==!par:--boot-var=!            (set parsErlang=!parsErlang! -boot_var %1 %2 && shift && shift && goto startloop)
+if ""==!par:--cookie=!              (set parsErlang=!parsErlang! -setcookie %1 && shift && goto startloop)
+if ""==!par:--hidden=!              (set parsErlang=!parsErlang! -hidden && goto startloop)
+if ""==!par:--detached=!            (set parsErlang=!parsErlang! -detached && echo warning: the --detached option is deprecated && goto startloop)
+if ""==!par:--erl-config=!          (set parsErlang=!parsErlang! -config %1 && shift && goto startloop)
+if ""==!par:--logger-otp-reports=!  (set parsErlang=!parsErlang! -logger handle_otp_reports %1 && shift && goto startloop)
+if ""==!par:--logger-sasl-reports=! (set parsErlang=!parsErlang! -logger handle_sasl_reports %1 && shift && goto startloop)
+if ""==!par:--name=!                (set parsErlang=!parsErlang! -name %1 && shift && goto startloop)
+if ""==!par:--sname=!               (set parsErlang=!parsErlang! -sname %1 && shift && goto startloop)
+if ""==!par:--vm-args=!             (set parsErlang=!parsErlang! -args_file %1 && shift && goto startloop)
+if ""==!par:--erl=!                 (set "beforeExtra=!beforeExtra! %~1" && shift && goto startloop)
+if ""==!par:--pipe-to=!             (echo --pipe-to : Option is not supported on Windows && goto end)
+set endLoop=1
+set parsElixir=!parsElixir! !par!
+goto startloop
 
-rem ******* assume all pre-params are parsed ********************
 :expand_erl_libs
-rem ******* expand all ebin paths as Windows does not support the ..\*\ebin wildcard ********************
-setlocal enabledelayedexpansion
+rem expand all ebin paths as Windows does not support the ..\*\ebin wildcard
 set ext_libs=
-for  /d %%d in ("%originPath%..\lib\*.") do (
+for  /d %%d in ("!SCRIPT_PATH!..\lib\*.") do (
   set ext_libs=!ext_libs! -pa "%%~fd\ebin"
 )
-setlocal disabledelayedexpansion
 
 :run
-if not %runMode% == "iex" (
-  set beforeExtra=-noshell -s elixir start_cli %beforeExtra%
+if not !runMode! == "iex" (
+  set beforeExtra=-noshell -s elixir start_cli !beforeExtra!
 )
-if %useWerl% equ 1 (
-  start werl.exe %ext_libs% %ELIXIR_ERL_OPTIONS% %parsErlang% %beforeExtra% -extra %*
+if !useWerl! equ 1 (
+  start !ERTS_BIN!werl.exe !ext_libs! !ELIXIR_ERL_OPTIONS! !parsErlang! !beforeExtra! -extra !parsElixir!
 ) else (
-  erl.exe %ext_libs% %ELIXIR_ERL_OPTIONS% %parsErlang% %beforeExtra% -extra %*
+  !ERTS_BIN!erl.exe !ext_libs! !ELIXIR_ERL_OPTIONS! !parsErlang! !beforeExtra! -extra !parsElixir!
 )
 :end
-endlocal
+endlocal
\ No newline at end of file
diff --git a/bin/elixirc b/bin/elixirc
index d677f6ef9..9bad4380b 100755
--- a/bin/elixirc
+++ b/bin/elixirc
@@ -1,20 +1,20 @@
 #!/bin/sh
 if [ $# -eq 0 ] || [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
-  echo "Usage: `basename $0` [elixir switches] [compiler switches] [.ex files]
+  echo "Usage: $(basename $0) [elixir switches] [compiler switches] [.ex files]
 
+  -h, --help                Prints this message and exits
   -o                        The directory to output compiled files
+  -v, --version             Prints Elixir version and exits
 
-  --help, -h                Prints this message and exits
   --ignore-module-conflict  Does not emit warnings if a module was previously defined
   --no-debug-info           Does not attach debug info to compiled modules
   --no-docs                 Does not attach documentation to compiled modules
   --verbose                 Prints compilation status
-  --version, -v             Prints Elixir version and exits
   --warnings-as-errors      Treats warnings as errors and return non-zero exit code
 
-** Options given after -- are passed down to the executed code
-** Options can be passed to the Erlang runtime using ELIXIR_ERL_OPTIONS
-** Options can be passed to the Erlang compiler using ERL_COMPILER_OPTIONS" >&2
+Options given after -- are passed down to the executed code.
+Options can be passed to the Erlang runtime using \$ELIXIR_ERL_OPTIONS.
+Options can be passed to the Erlang compiler using \$ERL_COMPILER_OPTIONS." >&2
   exit 1
 fi
 
@@ -24,7 +24,7 @@ readlink_f () {
   if [ -h "$filename" ]; then
     readlink_f "$(readlink "$filename")"
   else
-    echo "`pwd -P`/$filename"
+    echo "$(pwd -P)/$filename"
   fi
 }
 
diff --git a/bin/iex b/bin/iex
index 700d7bc24..3016b0436 100755
--- a/bin/iex
+++ b/bin/iex
@@ -1,35 +1,14 @@
 #!/bin/sh
 if [ $# -gt 0 ] && ([ "$1" = "--help" ] || [ "$1" = "-h" ]); then
-  echo "Usage: `basename $0` [options] [.exs file] [data]
+  echo "Usage: $(basename $0) [options] [.exs file] [data]
 
-  -e COMMAND                  Evaluates the given command (*)
-  -r FILE                     Requires the given files/patterns (*)
-  -S SCRIPT                   Finds and executes the given script in PATH
-  -pr FILE                    Requires the given files/patterns in parallel (*)
-  -pa PATH                    Prepends the given path to Erlang code path (*)
-  -pz PATH                    Appends the given path to Erlang code path (*)
+The following options are exclusive to IEx:
 
-  --app APP                   Starts the given app and its dependencies (*)
-  --cookie COOKIE             Sets a cookie for this distributed node
-  --detached                  Starts the Erlang VM detached from console
-  --erl SWITCHES              Switches to be passed down to Erlang (*)
-  --help, -h                  Prints this message and exits
-  --hidden                    Makes a hidden node
-  --logger-otp-reports BOOL   Enables or disables OTP reporting
-  --logger-sasl-reports BOOL  Enables or disables SASL reporting
-  --name NAME                 Makes and assigns a name to the distributed node
-  --no-halt                   Does not halt the Erlang VM after execution
-  --sname NAME                Makes and assigns a short name to the distributed node
-  --version, -v               Prints IEx version and exits
-  --werl                      Uses Erlang's Windows shell GUI (Windows only)
+  --dot-iex \"PATH\"    Overrides default .iex.exs file and uses path instead;
+                      path can be empty, then no file will be loaded
+  --remsh NAME        Connects to a node using a remote shell
 
-  --dot-iex PATH              Overrides default .iex.exs file and uses path instead;
-                              path can be empty, then no file will be loaded
-  --remsh NAME                Connects to a node using a remote shell
-
-** Options marked with (*) can be given more than once
-** Options given after the .exs file or -- are passed down to the executed code
-** Options can be passed to the VM using ELIXIR_ERL_OPTIONS or --erl" >&2
+The remaining options are the same as in the \"elixir\" executable. Run \"elixir --help\" to see them" >&2
   exit 1
 fi
 
@@ -39,7 +18,7 @@ readlink_f () {
   if [ -h "$filename" ]; then
     readlink_f "$(readlink "$filename")"
   else
-    echo "`pwd -P`/$filename"
+    echo "$(pwd -P)/$filename"
   fi
 }
 
diff --git a/bin/iex.bat b/bin/iex.bat
index 5ee7722af..8893b997f 100644
--- a/bin/iex.bat
+++ b/bin/iex.bat
@@ -9,34 +9,13 @@ goto run
 :documentation
 echo Usage: %~nx0 [options] [.exs file] [data]
 echo.
-echo   -e COMMAND                  Evaluates the given command (*)
-echo   -r FILE                     Requires the given files/patterns (*)
-echo   -S SCRIPT                   Finds and executes the given script in PATH
-echo   -pr FILE                    Requires the given files/patterns in parallel (*)
-echo   -pa PATH                    Prepends the given path to Erlang code path (*)
-echo   -pz PATH                    Appends the given path to Erlang code path (*)
+echo The following options are exclusive to IEx:
 echo.
-echo   --app APP                   Starts the given app and its dependencies (*)
-echo   --cookie COOKIE             Sets a cookie for this distributed node
-echo   --detached                  Starts the Erlang VM detached from console
-echo   --erl SWITCHES              Switches to be passed down to Erlang (*)
-echo   --help, -h                  Prints this message and exits
-echo   --hidden                    Makes a hidden node
-echo   --logger-otp-reports BOOL   Enables or disables OTP reporting
-echo   --logger-sasl-reports BOOL  Enables or disables SASL reporting
-echo   --name NAME                 Makes and assigns a name to the distributed node
-echo   --no-halt                   Does not halt the Erlang VM after execution
-echo   --sname NAME                Makes and assigns a short name to the distributed node
-echo   --version, -v               Prints IEx version and exits
-echo   --werl                      Uses Erlang's Windows shell GUI (Windows only)
+echo   --dot-iex "PATH"    Overrides default .iex.exs file and uses path instead;
+echo                       path can be empty, then no file will be loaded
+echo   --remsh NAME        Connects to a node using a remote shell
 echo.
-echo   --dot-iex PATH              Overrides default .iex.exs file and uses path instead;
-echo                               path can be empty, then no file will be loaded
-echo   --remsh NAME                Connects to a node using a remote shell
-echo.
-echo ** Options marked with (*) can be given more than once
-echo ** Options given after the .exs file or -- are passed down to the executed code
-echo ** Options can be passed to the Erlang VM using ELIXIR_ERL_OPTIONS or --erl
+echo The remaining options are the same as in the "elixir" executable. Run "elixir --help" to see them
 goto end
 
 :run
diff --git a/lib/eex/lib/eex.ex b/lib/eex/lib/eex.ex
index ddf28024f..da1f64438 100644
--- a/lib/eex/lib/eex.ex
+++ b/lib/eex/lib/eex.ex
@@ -105,7 +105,7 @@ defmodule EEx do
 
       iex> defmodule Sample do
       ...>   require EEx
-      ...>   EEx.function_from_string :def, :sample, "<%= a + b %>", [:a, :b]
+      ...>   EEx.function_from_string(:def, :sample, "<%= a + b %>", [:a, :b])
       ...> end
       iex> Sample.sample(1, 2)
       "3"
@@ -141,11 +141,12 @@ defmodule EEx do
       # sample.ex
       defmodule Sample do
         require EEx
-        EEx.function_from_file :def, :sample, "sample.eex", [:a, :b]
+        EEx.function_from_file(:def, :sample, "sample.eex", [:a, :b])
       end
 
       # iex
-      Sample.sample(1, 2) #=> "3"
+      Sample.sample(1, 2)
+      #=> "3"
 
   """
   defmacro function_from_file(kind, name, file, args \\ [], options \\ []) do
@@ -207,7 +208,8 @@ defmodule EEx do
       foo <%= bar %>
 
       # iex
-      EEx.eval_file "sample.eex", [bar: "baz"] #=> "foo baz"
+      EEx.eval_file("sample.eex", bar: "baz")
+      #=> "foo baz"
 
   """
   @spec eval_file(String.t(), keyword, keyword) :: any
diff --git a/lib/eex/lib/eex/compiler.ex b/lib/eex/lib/eex/compiler.ex
index b6780b21e..83bc42c23 100644
--- a/lib/eex/lib/eex/compiler.ex
+++ b/lib/eex/lib/eex/compiler.ex
@@ -78,7 +78,7 @@ defmodule EEx.Compiler do
       "unexpected beginning of EEx tag \"<%#{modifier}\" on \"<%#{modifier}#{chars}%>\", " <>
         "please remove \"#{modifier}\" accordingly"
 
-    :elixir_errors.warn(line, state.file, message)
+    :elixir_errors.erl_warn(line, state.file, message)
     generate_buffer([{:middle_expr, line, '', chars} | t], buffer, scope, state)
     # TODO: Make this an error on Elixir v2.0 since it accidentally worked previously.
     # raise EEx.SyntaxError, message: message, file: state.file, line: line
@@ -103,7 +103,7 @@ defmodule EEx.Compiler do
       "unexpected beginning of EEx tag \"<%#{modifier}\" on end of " <>
         "expression \"<%#{modifier}#{chars}%>\", please remove \"#{modifier}\" accordingly"
 
-    :elixir_errors.warn(line, state.file, message)
+    :elixir_errors.erl_warn(line, state.file, message)
     generate_buffer([{:end_expr, line, '', chars} | t], buffer, scope, state)
     # TODO: Make this an error on Elixir v2.0 since it accidentally worked previously.
     # raise EEx.SyntaxError, message: message, file: state.file, line: line
diff --git a/lib/eex/lib/eex/engine.ex b/lib/eex/lib/eex/engine.ex
index 95b8d042c..6c8197871 100644
--- a/lib/eex/lib/eex/engine.ex
+++ b/lib/eex/lib/eex/engine.ex
@@ -127,7 +127,7 @@ defmodule EEx.Engine do
   end
 
   @doc false
-  # TODO: Raise on 2.0
+  # TODO: Raise on v2.0
   @spec fetch_assign!(Access.t(), Access.key()) :: term | nil
   def fetch_assign!(assigns, key) do
     case Access.fetch(assigns, key) do
diff --git a/lib/eex/lib/eex/smart_engine.ex b/lib/eex/lib/eex/smart_engine.ex
index 6b5c398d1..32acda012 100644
--- a/lib/eex/lib/eex/smart_engine.ex
+++ b/lib/eex/lib/eex/smart_engine.ex
@@ -24,11 +24,12 @@ defmodule EEx.SmartEngine do
       # sample.ex
       defmodule Sample do
         require EEx
-        EEx.function_from_file :def, :sample, "sample.eex", [:assigns]
+        EEx.function_from_file(:def, :sample, "sample.eex", [:assigns])
       end
 
       # iex
-      Sample.sample(a: 1, b: 2) #=> "3"
+      Sample.sample(a: 1, b: 2)
+      #=> "3"
 
   """
 
diff --git a/lib/eex/lib/eex/tokenizer.ex b/lib/eex/lib/eex/tokenizer.ex
index febdfbeac..d95dff416 100644
--- a/lib/eex/lib/eex/tokenizer.ex
+++ b/lib/eex/lib/eex/tokenizer.ex
@@ -8,6 +8,9 @@ defmodule EEx.Tokenizer do
           {:text, content}
           | {:expr | :start_expr | :middle_expr | :end_expr, line, marker, content}
 
+  @spaces [?\s, ?\t]
+  @closing_brackets ')]}'
+
   @doc """
   Tokenizes the given charlist or binary.
 
@@ -110,25 +113,28 @@ defmodule EEx.Tokenizer do
   #
   # Start tokens finish with "do" and "fn ->"
   # Middle tokens are marked with "->" or keywords
-  # End tokens contain only the end word and optionally ")"
+  # End tokens contain only the end word and optionally
+  # combinations of ")", "]" and "}".
 
-  defp token_name([h | t]) when h in [?\s, ?\t, ?)] do
+  defp token_name([h | t]) when h in @spaces do
     token_name(t)
   end
 
-  defp token_name('od' ++ [h | _]) when h in [?\s, ?\t, ?)] do
-    :start_expr
+  defp token_name('od' ++ [h | rest]) when h in @spaces or h in @closing_brackets do
+    case tokenize_rest(rest) do
+      {:ok, [{:end, _} | _]} -> :middle_expr
+      _ -> :start_expr
+    end
   end
 
   defp token_name('>-' ++ rest) do
-    rest = Enum.reverse(rest)
-
-    # Tokenize the remaining passing check_terminators as
-    # false, which relax the tokenizer to not error on
-    # unmatched pairs. Then, we check if there is a "fn"
-    # token and, if so, it is not followed by an "end"
-    # token. If this is the case, we are on a start expr.
-    case :elixir_tokenizer.tokenize(rest, 1, file: "eex", check_terminators: false) do
+    case tokenize_rest(rest) do
+      {:ok, [{:end, _} | _]} ->
+        :middle_expr
+
+      # Check if there is a "fn" token and, if so, it is not
+      # followed by an "end" token. If this is the case, we
+      # are on a start expr.
       {:ok, tokens} ->
         tokens = Enum.reverse(tokens)
         fn_index = fn_index(tokens)
@@ -148,10 +154,19 @@ defmodule EEx.Tokenizer do
   defp token_name('retfa' ++ t), do: check_spaces(t, :middle_expr)
   defp token_name('hctac' ++ t), do: check_spaces(t, :middle_expr)
   defp token_name('eucser' ++ t), do: check_spaces(t, :middle_expr)
-  defp token_name('dne' ++ t), do: check_spaces(t, :end_expr)
 
-  defp token_name(_) do
-    :expr
+  defp token_name(rest) do
+    case Enum.drop_while(rest, &(&1 in @spaces or &1 in @closing_brackets)) do
+      'dne' ++ t -> check_spaces(t, :end_expr)
+      _ -> :expr
+    end
+  end
+
+  # Tokenize the remaining passing check_terminators as false,
+  # which relax the tokenizer to not error on unmatched pairs.
+  # If the tokens start with an "end" we have a middle expr.
+  defp tokenize_rest(rest) do
+    :elixir_tokenizer.tokenize(Enum.reverse(rest), 1, file: "eex", check_terminators: false)
   end
 
   defp fn_index(tokens) do
@@ -167,7 +182,7 @@ defmodule EEx.Tokenizer do
   end
 
   defp check_spaces(string, token) do
-    if Enum.all?(string, &(&1 in [?\s, ?\t])) do
+    if Enum.all?(string, &(&1 in @spaces)) do
       token
     else
       :expr
@@ -221,7 +236,7 @@ defmodule EEx.Tokenizer do
     end
   end
 
-  defp trim_whitespace([h | t]) when h == ?\s or h == ?\t do
+  defp trim_whitespace([h | t]) when h in @spaces do
     trim_whitespace(t)
   end
 
diff --git a/lib/eex/test/eex/smart_engine_test.exs b/lib/eex/test/eex/smart_engine_test.exs
index 82d0e3298..11120db79 100644
--- a/lib/eex/test/eex/smart_engine_test.exs
+++ b/lib/eex/test/eex/smart_engine_test.exs
@@ -29,16 +29,19 @@ defmodule EEx.SmartEngineTest do
     assert_eval("1\n2\n3\n", "<%= for x <- [1, 2, 3] do %><%= x %>\n<% end %>")
   end
 
-  test "preserves line numbers" do
-    result = EEx.compile_string("<%= @hello %>", engine: EEx.SmartEngine)
+  test "preserves line numbers in assignments" do
+    result = EEx.compile_string("foo\n<%= @hello %>", engine: EEx.SmartEngine)
 
     Macro.prewalk(result, fn
-      {_left, meta, _right} ->
-        assert Keyword.get(meta, :line, 0) in [0, 1]
+      {_left, meta, [_, :hello]} ->
+        assert Keyword.get(meta, :line) == 2
+        send(self(), :found)
 
-      _ ->
-        :ok
+      node ->
+        node
     end)
+
+    assert_received :found
   end
 
   defp assert_eval(expected, actual, binding \\ []) do
diff --git a/lib/eex/test/eex/tokenizer_test.exs b/lib/eex/test/eex/tokenizer_test.exs
index fa04980e9..d34f6ab18 100644
--- a/lib/eex/test/eex/tokenizer_test.exs
+++ b/lib/eex/test/eex/tokenizer_test.exs
@@ -122,6 +122,30 @@ defmodule EEx.TokenizerTest do
              {:ok, exprs}
   end
 
+  test "strings with multiple callbacks" do
+    exprs = [
+      {:start_expr, 1, '=', ' a fn -> '},
+      {:text, 'foo'},
+      {:middle_expr, 1, '', ' end, fn -> '},
+      {:text, 'bar'},
+      {:end_expr, 1, '', ' end '}
+    ]
+
+    assert T.tokenize('<%= a fn -> %>foo<% end, fn -> %>bar<% end %>', 1) == {:ok, exprs}
+  end
+
+  test "strings with callback followed by do block" do
+    exprs = [
+      {:start_expr, 1, '=', ' a fn -> '},
+      {:text, 'foo'},
+      {:middle_expr, 1, '', ' end do '},
+      {:text, 'bar'},
+      {:end_expr, 1, '', ' end '}
+    ]
+
+    assert T.tokenize('<%= a fn -> %>foo<% end do %>bar<% end %>', 1) == {:ok, exprs}
+  end
+
   test "strings with embedded keywords blocks" do
     exprs = [
       {:text, 'foo '},
diff --git a/lib/eex/test/eex_test.exs b/lib/eex/test/eex_test.exs
index 2130ac5f6..13ff91fb8 100644
--- a/lib/eex/test/eex_test.exs
+++ b/lib/eex/test/eex_test.exs
@@ -100,6 +100,12 @@ defmodule EExTest do
       assert_eval("foo ", "foo <%= if false do %>bar<% end %>")
     end
 
+    test "embedded code with do preceeded by bracket" do
+      assert_eval("foo bar", "foo <%= if {true}do %>bar<% end %>")
+      assert_eval("foo bar", "foo <%= if (true)do %>bar<% end %>")
+      assert_eval("foo bar", "foo <%= if [true]do %>bar<% end %>")
+    end
+
     test "embedded code with do end and expression" do
       assert_eval("foo bar", "foo <%= if true do %><%= :bar %><% end %>")
     end
@@ -132,7 +138,27 @@ defmodule EExTest do
       )
     end
 
-    test "embedded code with parentheses after end in end token" do
+    test "embedded code with end followed by bracket" do
+      assert_eval(
+        " 101  102  103 ",
+        "<%= Enum.map([1, 2, 3], fn x -> %> <%= 100 + x %> <% end) %>"
+      )
+
+      assert_eval(
+        " 101  102  103 ",
+        "<%= apply Enum, :map, [[1, 2, 3], fn x -> %> <%= 100 + x %> <% end] %>"
+      )
+
+      assert_eval(
+        " 101  102  103 ",
+        "<%= #{__MODULE__}.tuple_map {[1, 2, 3], fn x -> %> <%= 100 + x %> <% end} %>"
+      )
+
+      assert_eval(
+        " 101  102  103 ",
+        "<%= apply(Enum, :map, [[1, 2, 3], fn x -> %> <%= 100 + x %> <% end]) %>"
+      )
+
       assert_eval(
         " 101  102  103 ",
         "<%= Enum.map([1, 2, 3], (fn x -> %> <%= 100 + x %> <% end) ) %>"
@@ -217,6 +243,20 @@ defmodule EExTest do
     end
   end
 
+  describe "error messages" do
+    test "honor line numbers" do
+      assert_raise EEx.SyntaxError, "nofile:99: missing token '%>'", fn ->
+        EEx.compile_string("foo <%= bar", line: 99)
+      end
+    end
+
+    test "honor file names" do
+      assert_raise EEx.SyntaxError, "my_file.eex:1: missing token '%>'", fn ->
+        EEx.compile_string("foo <%= bar", file: "my_file.eex")
+      end
+    end
+  end
+
   describe "environment" do
     test "respects line numbers" do
       expected = """
@@ -342,6 +382,52 @@ defmodule EExTest do
       assert_eval(expected, string)
     end
 
+    test "inside multiple functions" do
+      expected = """
+
+      A 1
+
+      B 2
+
+      A 3
+
+      """
+
+      string = """
+      <%= #{__MODULE__}.switching_map [1, 2, 3], fn x -> %>
+      A <%= x %>
+      <% end, fn x -> %>
+      B <%= x %>
+      <% end %>
+      """
+
+      assert_eval(expected, string)
+    end
+
+    test "inside callback and do block" do
+      expected = """
+
+
+      A 1
+
+      B 2
+
+      A 3
+
+      """
+
+      string = """
+      <% require #{__MODULE__} %>
+      <%= #{__MODULE__}.switching_macro [1, 2, 3], fn x -> %>
+      A <%= x %>
+      <% end do %>
+      B <%= x %>
+      <% end %>
+      """
+
+      assert_eval(expected, string)
+    end
+
     test "inside cond" do
       expected = """
       foo
@@ -527,4 +613,27 @@ defmodule EExTest do
   defp assert_normalized_newline_equal(expected, actual) do
     assert String.replace(expected, "\r\n", "\n") == String.replace(actual, "\r\n", "\n")
   end
+
+  def tuple_map({list, callback}) do
+    Enum.map(list, callback)
+  end
+
+  def switching_map(list, a, b) do
+    list
+    |> Enum.with_index()
+    |> Enum.map(fn
+      {item, index} when rem(index, 2) == 0 -> a.(item)
+      {item, index} when rem(index, 2) == 1 -> b.(item)
+    end)
+  end
+
+  defmacro switching_macro(list, a, do: block) do
+    quote do
+      b = fn var!(x) ->
+        unquote(block)
+      end
+
+      unquote(__MODULE__).switching_map(unquote(list), unquote(a), b)
+    end
+  end
 end
diff --git a/lib/elixir/docs.exs b/lib/elixir/docs.exs
index 366971acd..00749fb7f 100644
--- a/lib/elixir/docs.exs
+++ b/lib/elixir/docs.exs
@@ -18,6 +18,7 @@
       Float,
       Function,
       Integer,
+      Module,
       NaiveDateTime,
       Record,
       Regex,
@@ -57,13 +58,6 @@
       Calendar.TimeZoneDatabase,
       Calendar.UTCOnlyTimeZoneDatabase
     ],
-    "Modules & Code": [
-      Code,
-      Kernel.ParallelCompiler,
-      Macro,
-      Macro.Env,
-      Module
-    ],
     "Processes & Applications": [
       Agent,
       Application,
@@ -86,6 +80,12 @@
       Protocol,
       String.Chars
     ],
+    "Code & Macros": [
+      Code,
+      Kernel.ParallelCompiler,
+      Macro,
+      Macro.Env
+    ],
     Deprecated: [
       Behaviour,
       Dict,
diff --git a/lib/elixir/lib/access.ex b/lib/elixir/lib/access.ex
index 6967f7d1b..99ea71f1b 100644
--- a/lib/elixir/lib/access.ex
+++ b/lib/elixir/lib/access.ex
@@ -2,46 +2,12 @@ defmodule Access do
   @moduledoc """
   Key-based access to data structures.
 
-  Elixir supports three main key-value constructs: keywords,
-  maps, and structs. It also supports two mechanisms to access those keys:
-  by brackets (via `data[key]`) and by dot-syntax (via `data.field`).
+  The `Access` module defines a behaviour for dynamically accessing
+  keys of any type in a data structure via the `data[key]` syntax.
 
-  In the next section we will briefly recap the key-value constructs and then
-  discuss the access mechanisms.
-
-  ## Key-value constructs
-
-  Elixir provides three main key-value constructs, summarized below:
-
-    * keyword lists - they are lists of two-element tuples where
-      the first element is an atom. Commonly written in the
-      `[key: value]` syntax, they support only atom keys. Keyword
-      lists are used almost exclusively to pass options to functions
-      and macros. They keep the user ordering and allow duplicate
-      keys. See the `Keyword` module.
-
-    * maps - they are the "go to" key-value data structure in Elixir.
-      They are capable of supporting billions of keys of any type. They are
-      written using the `%{key => value}` syntax and also support the
-      `%{key: value}` syntax when the keys are atoms. They do not
-      have any specified ordering and do not allow duplicate keys.
-      See the `Map` module.
-
-    * structs - they are named maps with a pre-determined set of keys.
-      They are defined with `defstruct/1` and written using the
-      `%StructName{key: value}` syntax.
-
-  ## Key-based accessors
-
-  Elixir provides two mechanisms to access data structures by key,
-  described next.
-
-  ### Bracket-based access
-
-  The `data[key]` syntax is used to access data structures with a
-  dynamic number of keys, such as keywords and maps. The key can
-  be of any type. The bracket-based access syntax returns `nil`
-  if the key does not exist:
+  `Access` supports keyword lists (`Keyword`) and maps (`Map`) out
+  of the box. The key can be of any type and it returns `nil` if
+  the key does not exist:
 
       iex> keywords = [a: 1, b: 2]
       iex> keywords[:a]
@@ -59,62 +25,41 @@ defmodule Access do
 
   This syntax is very convenient as it can be nested arbitrarily:
 
-      iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
-      iex> put_in(users["john"][:age], 28)
-      %{"john" => %{age: 28}, "meg" => %{age: 23}}
-
-  Furthermore, the bracket-based access syntax transparently ignores
-  `nil` values. When trying to access anything on a `nil` value, `nil`
-  is returned:
-
       iex> keywords = [a: 1, b: 2]
       iex> keywords[:c][:unknown]
       nil
 
+  This works because accessing anything on a `nil` value, returns
+  `nil` itself:
+
       iex> nil[:a]
       nil
 
-  Internally, `data[key]` translates  to `Access.get(term, key, nil)`.
-  Developers interested in implementing their own key-value data
-  structures can implement the `Access` behaviour to provide the
-  bracket-based access syntax. `Access` requires the key comparison
-  to be implemented using the `===/2` operator.
-
-  ### Dot-based syntax
-
-  The `data.field` syntax is used exclusively to access atom fields
-  in maps and structs. If the accessed field does not exist, an error is
-  raised. This is a deliberate decision: since all of the
-  fields in a struct are pre-determined, structs support only the
-  dot-based syntax and not the access one.
-
-  Imagine a struct named `User` with a `:name` field. The following would raise:
-
-      user = %User{name: "John"}
-      user[:name]
-      # ** (UndefinedFunctionError) undefined function User.fetch/2 (User does not implement the Access behaviour)
-
-  Instead we should use the `user.name` syntax to access fields:
+  The access syntax can also be used with the `Kernel.put_in/2`,
+  `Kernel.update_in/2` and `Kernel.get_and_update_in/2` macros
+  to allow values to be set in nested data structures:
 
-      user.name
-      #=> "John"
-
-  Differently from `user[:name]`, `user.name` is not extensible via
-  a behaviour and is restricted only to structs and atom keys in maps.
-
-  ### Summing up
-
-  The bracket-based syntax, `user[:name]`, is used by dynamic structures,
-  is extensible and returns nil on missing keys.
+      iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
+      iex> put_in(users["john"][:age], 28)
+      %{"john" => %{age: 28}, "meg" => %{age: 23}}
 
-  The dot-based syntax, `user.name`, is used exclusively to access atom
-  keys in maps and structs, and it raises on missing keys.
+  > Attention! While the access syntax is allowed in maps via
+  > `map[key]`, if your map is made of predefined atom keys,
+  > you should prefer to access those atom keys with `map.key`
+  > instead of `map[key]`, as `map.key` will raise if the key
+  > is missing. This is important because, if a map has a predefined
+  > set of keys and a key is missing, it is most likely a bug
+  > in your software or a typo on the key name. For this reason,
+  > because structs are predefined in nature, they only allow
+  > the `struct.key` syntax and they do not allow the `struct[key]`
+  > access syntax. See the `Map` module for more information.
 
   ## Nested data structures
 
   Both key-based access syntaxes can be used with the nested update
-  functions and macros in `Kernel`, such as `Kernel.get_in/2`, `Kernel.put_in/3`,
-  `Kernel.update_in/3`, `Kernel.pop_in/2`, and `Kernel.get_and_update_in/3`.
+  functions and macros in `Kernel`, such as `Kernel.get_in/2`,
+  `Kernel.put_in/3`, `Kernel.update_in/3`, `Kernel.pop_in/2`, and
+  `Kernel.get_and_update_in/3`.
 
   For example, to update a map inside another map:
 
@@ -126,9 +71,9 @@ defmodule Access do
   structures, like tuples and lists. These functions can be used
   in all the `Access`-related functions and macros in `Kernel`.
 
-  For instance, given a user map with the `:name` and `:languages` keys,
-  here is how to deeply traverse the map and convert all language names
-  to uppercase:
+  For instance, given a user map with the `:name` and `:languages`
+  keys, here is how to deeply traverse the map and convert all
+  language names to uppercase:
 
       iex> languages = [
       ...>   %{name: "elixir", type: :functional},
@@ -144,8 +89,8 @@ defmodule Access do
         ]
       }
 
-  See the functions `key/1`, `key!/1`, `elem/1`, and `all/0` for some of the
-  available accessors.
+  See the functions `key/1`, `key!/1`, `elem/1`, and `all/0` for
+  some of the available accessors.
   """
 
   @type container :: keyword | struct | map
diff --git a/lib/elixir/lib/agent.ex b/lib/elixir/lib/agent.ex
index ea3c80b7f..36e80179b 100644
--- a/lib/elixir/lib/agent.ex
+++ b/lib/elixir/lib/agent.ex
@@ -32,11 +32,19 @@ defmodule Agent do
   Usage would be:
 
       Counter.start_link(0)
+      #=> {:ok, #PID<0.123.0>}
 
-      Counter.value     #=> 0
-      Counter.increment #=> :ok
-      Counter.increment #=> :ok
-      Counter.value     #=> 2
+      Counter.value()
+      #=> 0
+
+      Counter.increment()
+      #=> :ok
+
+      Counter.increment()
+      #=> :ok
+
+      Counter.value()
+      #=> 2
 
   Thanks to the agent server process, the counter can be safely incremented
   concurrently.
diff --git a/lib/elixir/lib/application.ex b/lib/elixir/lib/application.ex
index 2cfd218a6..ec9ed6034 100644
--- a/lib/elixir/lib/application.ex
+++ b/lib/elixir/lib/application.ex
@@ -198,8 +198,8 @@ defmodule Application do
   will shut down every application in the opposite order they had been started.
 
   By default, a SIGTERM from the operating system will automatically translate to
-  `System.stop/0`. You can also have more explicit control over OS signals via the
-  `:os.set_signal/2` function.
+  `System.stop/0`. You can also have more explicit control over operating system
+  signals via the `:os.set_signal/2` function.
 
   ## Tooling
 
diff --git a/lib/elixir/lib/atom.ex b/lib/elixir/lib/atom.ex
index 0e9d72175..6f491d55b 100644
--- a/lib/elixir/lib/atom.ex
+++ b/lib/elixir/lib/atom.ex
@@ -37,7 +37,6 @@ defmodule Atom do
     :erlang.atom_to_list(atom)
   end
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Atom.to_charlist/1 instead"
   @spec to_char_list(atom) :: charlist
diff --git a/lib/elixir/lib/calendar/date.ex b/lib/elixir/lib/calendar/date.ex
index 690cc0a07..938edac44 100644
--- a/lib/elixir/lib/calendar/date.ex
+++ b/lib/elixir/lib/calendar/date.ex
@@ -745,10 +745,11 @@ defmodule Date do
 
   ## Examples
 
-    iex> Date.day_of_era(~D[0001-01-01])
-    {1, 1}
-    iex> Date.day_of_era(~D[0000-12-31])
-    {1, 0}
+      iex> Date.day_of_era(~D[0001-01-01])
+      {1, 1}
+
+      iex> Date.day_of_era(~D[0000-12-31])
+      {1, 0}
 
   """
   @doc since: "1.8.0"
diff --git a/lib/elixir/lib/calendar/datetime.ex b/lib/elixir/lib/calendar/datetime.ex
index dcc791b91..1744fd11f 100644
--- a/lib/elixir/lib/calendar/datetime.ex
+++ b/lib/elixir/lib/calendar/datetime.ex
@@ -480,9 +480,11 @@ defmodule DateTime do
 
   ## Examples
 
-      iex> {:ok, datetime} = DateTime.now("Europe/Copenhagen", FakeTimeZoneDatabase)
+      iex> {:ok, datetime} = DateTime.now("Etc/UTC")
       iex> datetime.time_zone
-      "Europe/Copenhagen"
+      "Etc/UTC"
+      iex> DateTime.now("Europe/Copenhagen")
+      {:error, :utc_only_time_zone_database}
       iex> DateTime.now("not a real time zone name", FakeTimeZoneDatabase)
       {:error, :time_zone_not_found}
 
@@ -553,18 +555,17 @@ defmodule DateTime do
 
   """
   @spec to_naive(Calendar.datetime()) :: NaiveDateTime.t()
-  def to_naive(datetime) do
-    %{
-      calendar: calendar,
-      year: year,
-      month: month,
-      day: day,
-      hour: hour,
-      minute: minute,
-      second: second,
-      microsecond: microsecond
-    } = datetime
-
+  def to_naive(%{
+        calendar: calendar,
+        year: year,
+        month: month,
+        day: day,
+        hour: hour,
+        minute: minute,
+        second: second,
+        microsecond: microsecond,
+        time_zone: _
+      }) do
     %NaiveDateTime{
       year: year,
       month: month,
@@ -593,8 +594,17 @@ defmodule DateTime do
 
   """
   @spec to_date(Calendar.datetime()) :: Date.t()
-  def to_date(datetime) do
-    %{year: year, month: month, day: day, calendar: calendar} = datetime
+  def to_date(%{
+        year: year,
+        month: month,
+        day: day,
+        calendar: calendar,
+        hour: _,
+        minute: _,
+        second: _,
+        microsecond: _,
+        time_zone: _
+      }) do
     %Date{year: year, month: month, day: day, calendar: calendar}
   end
 
@@ -614,10 +624,17 @@ defmodule DateTime do
 
   """
   @spec to_time(Calendar.datetime()) :: Time.t()
-  def to_time(datetime) do
-    %{hour: hour, minute: minute, second: second, microsecond: microsecond, calendar: calendar} =
-      datetime
-
+  def to_time(%{
+        year: _,
+        month: _,
+        day: _,
+        calendar: calendar,
+        hour: hour,
+        minute: minute,
+        second: second,
+        microsecond: microsecond,
+        time_zone: _
+      }) do
     %Time{
       hour: hour,
       minute: minute,
diff --git a/lib/elixir/lib/calendar/iso.ex b/lib/elixir/lib/calendar/iso.ex
index 401a3fdfe..c76da30bc 100644
--- a/lib/elixir/lib/calendar/iso.ex
+++ b/lib/elixir/lib/calendar/iso.ex
@@ -213,7 +213,7 @@ defmodule Calendar.ISO do
   end
 
   def date_to_iso_days(year, month, day) when year in -9999..9999 do
-    true = day <= days_in_month(year, month)
+    ensure_day_in_month!(year, month, day)
 
     days_in_previous_years(year) + days_before_month(month) + leap_day_offset(year, month) + day -
       1
@@ -366,7 +366,7 @@ defmodule Calendar.ISO do
   @impl true
   def day_of_year(year, month, day)
       when is_integer(year) and is_integer(month) and is_integer(day) do
-    true = day <= days_in_month(year, month)
+    ensure_day_in_month!(year, month, day)
     days_before_month(month) + leap_day_offset(year, month) + day
   end
 
@@ -482,6 +482,24 @@ defmodule Calendar.ISO do
     time_to_string(hour, minute, second, microsecond, :extended)
   end
 
+  @doc """
+  Converts the given time into a string in the given format.
+
+  ## Examples
+
+      iex> Calendar.ISO.time_to_string(2, 2, 2, {2, 6}, :basic)
+      "020202.000002"
+      iex> Calendar.ISO.time_to_string(2, 2, 2, {2, 6}, :extended)
+      "02:02:02.000002"
+
+  """
+  @spec time_to_string(
+          Calendar.hour(),
+          Calendar.minute(),
+          Calendar.second(),
+          Calendar.microsecond(),
+          :basic | :extended
+        ) :: String.t()
   def time_to_string(hour, minute, second, {_, 0}, format) do
     time_to_string_format(hour, minute, second, format)
   end
@@ -993,4 +1011,10 @@ defmodule Calendar.ISO do
 
     {hour, minute, second}
   end
+
+  defp ensure_day_in_month!(year, month, day) do
+    if day < 1 or day > days_in_month(year, month) do
+      raise ArgumentError, "invalid date: #{date_to_string(year, month, day)}"
+    end
+  end
 end
diff --git a/lib/elixir/lib/calendar/naive_datetime.ex b/lib/elixir/lib/calendar/naive_datetime.ex
index 5493b7a0f..86a853b04 100644
--- a/lib/elixir/lib/calendar/naive_datetime.ex
+++ b/lib/elixir/lib/calendar/naive_datetime.ex
@@ -392,10 +392,6 @@ defmodule NaiveDateTime do
 
   """
   @spec to_date(Calendar.naive_datetime()) :: Date.t()
-  def to_date(%NaiveDateTime{year: year, month: month, day: day, calendar: calendar}) do
-    %Date{year: year, month: month, day: day, calendar: calendar}
-  end
-
   def to_date(%{
         year: year,
         month: month,
@@ -422,24 +418,6 @@ defmodule NaiveDateTime do
 
   """
   @spec to_time(Calendar.naive_datetime()) :: Time.t()
-  def to_time(%NaiveDateTime{} = naive_datetime) do
-    %{
-      hour: hour,
-      minute: minute,
-      second: second,
-      microsecond: microsecond,
-      calendar: calendar
-    } = naive_datetime
-
-    %Time{
-      hour: hour,
-      minute: minute,
-      second: second,
-      microsecond: microsecond,
-      calendar: calendar
-    }
-  end
-
   def to_time(%{
         year: _,
         month: _,
diff --git a/lib/elixir/lib/code.ex b/lib/elixir/lib/code.ex
index c694d67ad..f745e6ffb 100644
--- a/lib/elixir/lib/code.ex
+++ b/lib/elixir/lib/code.ex
@@ -46,7 +46,7 @@ defmodule Code do
     :elixir_code_server.call(:required)
   end
 
-  # TODO: Deprecate me on 1.9
+  # TODO: Deprecate on v1.9
   @doc false
   def loaded_files do
     required_files()
@@ -78,7 +78,7 @@ defmodule Code do
     :elixir_code_server.cast({:unrequire_files, files})
   end
 
-  # TODO: Deprecate me on 1.9
+  # TODO: Deprecate on v1.9
   @doc false
   def unload_files(files) do
     unrequire_files(files)
@@ -707,7 +707,7 @@ defmodule Code do
     eval_string(File.read!(file), [], file: file, line: 1)
   end
 
-  # TODO: Deprecate me on 1.9
+  # TODO: Deprecate on v1.9
   @doc false
   def load_file(file, relative_to \\ nil) when is_binary(file) do
     file = find_file(file, relative_to)
@@ -753,7 +753,7 @@ defmodule Code do
   def require_file(file, relative_to \\ nil) when is_binary(file) do
     file = find_file(file, relative_to)
 
-    # TODO: Simply block until :required or :proceed once load_file is removed in 2.0
+    # TODO: Simply block until :required or :proceed once load_file is removed on v2.0
     case :elixir_code_server.call({:acquire, file}) do
       :required ->
         nil
@@ -1077,7 +1077,7 @@ defmodule Code do
           | {:error, :module_not_found | :chunk_not_found | {:invalid_chunk, binary}}
         when annotation: :erl_anno.anno(),
              beam_language: :elixir | :erlang | :lfe | :alpaca | atom(),
-             doc_content: %{binary => binary} | :none | :hidden,
+             doc_content: %{required(binary) => binary} | :none | :hidden,
              doc_element:
                {{kind :: atom, function_name :: atom, arity}, annotation, signature, doc_content,
                 metadata},
diff --git a/lib/elixir/lib/code/formatter.ex b/lib/elixir/lib/code/formatter.ex
index 7a588e76a..f3107462b 100644
--- a/lib/elixir/lib/code/formatter.ex
+++ b/lib/elixir/lib/code/formatter.ex
@@ -49,7 +49,7 @@ defmodule Code.Formatter do
     :<>
   ]
 
-  locals_without_parens = [
+  @locals_without_parens [
     # Special forms
     alias: 1,
     alias: 2,
@@ -138,8 +138,6 @@ defmodule Code.Formatter do
     import_config: 1
   ]
 
-  @locals_without_parens MapSet.new(locals_without_parens)
-
   @doc """
   Checks if two strings are equivalent.
   """
@@ -255,11 +253,7 @@ defmodule Code.Formatter do
       end
 
     locals_without_parens =
-      opts
-      |> Keyword.get(:locals_without_parens, [])
-      |> MapSet.new()
-      |> MapSet.union(@locals_without_parens)
-      |> MapSet.to_list()
+      Keyword.get(opts, :locals_without_parens, []) ++ @locals_without_parens
 
     %{
       locals_without_parens: locals_without_parens,
@@ -796,7 +790,7 @@ defmodule Code.Formatter do
   end
 
   # TODO: We can remove this workaround once we remove
-  # ?rearrange_uop from the parser in Elixir v2.0.
+  # ?rearrange_uop from the parser on v2.0.
   # (! left) in right
   # (not left) in right
   defp binary_operand_to_algebra(
@@ -1058,7 +1052,7 @@ defmodule Code.Formatter do
   end
 
   # We can only rename functions in the same module because
-  # introducing a new module may wrong due to aliases.
+  # introducing a new module may be wrong due to aliases.
   defp deprecated(Enum, :partition, 2), do: {"split_with", "~> 1.4"}
   defp deprecated(Code, :unload_files, 2), do: {"unrequire_files", "~> 1.7"}
   defp deprecated(Code, :loaded_files, 2), do: {"required_files", "~> 1.7"}
@@ -2021,7 +2015,7 @@ defmodule Code.Formatter do
   end
 
   # TODO: We can remove this workaround once we remove
-  # ?rearrange_uop from the parser in Elixir v2.0.
+  # ?rearrange_uop from the parser on v2.0.
   defp wrap_in_parens_if_operator(doc, {:__block__, _, [expr]}) do
     wrap_in_parens_if_operator(doc, expr)
   end
diff --git a/lib/elixir/lib/dict.ex b/lib/elixir/lib/dict.ex
index 1f2a7b109..3108ca08d 100644
--- a/lib/elixir/lib/dict.ex
+++ b/lib/elixir/lib/dict.ex
@@ -18,8 +18,6 @@ defmodule Dict do
   message =
     "Use the Map module for working with maps or the Keyword module for working with keyword lists"
 
-  # TODO: Remove by 2.0
-
   @deprecated message
   defmacro __using__(_) do
     # Use this import to guarantee proper code expansion
diff --git a/lib/elixir/lib/dynamic_supervisor.ex b/lib/elixir/lib/dynamic_supervisor.ex
index 6da8eca94..8a42a5cb8 100644
--- a/lib/elixir/lib/dynamic_supervisor.ex
+++ b/lib/elixir/lib/dynamic_supervisor.ex
@@ -47,12 +47,12 @@ defmodule DynamicSupervisor do
         # Automatically defines child_spec/1
         use DynamicSupervisor
 
-        def start_link(arg) do
-          DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
+        def start_link(init_arg) do
+          DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
         end
 
         @impl true
-        def init(_arg) do
+        def init(_init_arg) do
           DynamicSupervisor.init(strategy: :one_for_one)
         end
       end
@@ -78,20 +78,20 @@ defmodule DynamicSupervisor do
       defmodule MySupervisor do
         use Supervisor
 
-        def start_link(arg) do
-          Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
+        def start_link(init_arg) do
+          Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
         end
 
         def start_child(foo, bar, baz) do
-          # This will start child by calling MyWorker.start_link(initial_arg, foo, bar, baz)
+          # This will start child by calling MyWorker.start_link(init_arg, foo, bar, baz)
           Supervisor.start_child(__MODULE__, [foo, bar, baz])
         end
 
         @impl true
-        def init(initial_arg) do
+        def init(init_arg) do
           children = [
-            # Or the deprecated: worker(MyWorker, [initial_arg])
-            %{id: MyWorker, start: {MyWorker, :start_link, [initial_arg]}}
+            # Or the deprecated: worker(MyWorker, [init_arg])
+            %{id: MyWorker, start: {MyWorker, :start_link, [init_arg]}}
           ]
 
           Supervisor.init(children, strategy: :simple_one_for_one)
@@ -103,8 +103,8 @@ defmodule DynamicSupervisor do
       defmodule MySupervisor do
         use DynamicSupervisor
 
-        def start_link(arg) do
-          DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
+        def start_link(init_arg) do
+          DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
         end
 
         def start_child(foo, bar, baz) do
@@ -115,10 +115,10 @@ defmodule DynamicSupervisor do
         end
 
         @impl true
-        def init(initial_arg) do
+        def init(init_arg) do
           DynamicSupervisor.init(
             strategy: :one_for_one,
-            extra_arguments: [initial_arg]
+            extra_arguments: [init_arg]
           )
         end
       end
@@ -302,8 +302,8 @@ defmodule DynamicSupervisor do
 
   If the child process start function returns an error tuple or an erroneous
   value, or if it fails, the child specification is discarded and this function
-  returns `{:error, error}` where `error` is a term containing information about
-  the error and child specification.
+  returns `{:error, error}` where `error` is the error or erroneous value
+  returned from child process start function, or failure reason if it fails.
 
   If the supervisor already has N children in a way that N exceeds the amount
   of `:max_children` set on the supervisor initialization (see `init/1`), then
@@ -405,7 +405,7 @@ defmodule DynamicSupervisor do
 
     * `id` - it is always `:undefined` for dynamic supervisors
 
-    * `child` - the pid of the corresponding child process or the
+    * `child` - the PID of the corresponding child process or the
       atom `:restarting` if the process is about to be restarted
 
     * `type` - `:worker` or `:supervisor` as defined in the child
@@ -487,7 +487,7 @@ defmodule DynamicSupervisor do
 
     * `:strategy` - the restart strategy option. The only supported
       value is `:one_for_one` which means that no other child is
-      terminate if a child process terminates. You can learn more
+      terminated if a child process terminates. You can learn more
       about strategies in the `Supervisor` module docs.
 
     * `:max_restarts` - the maximum number of restarts allowed in
diff --git a/lib/elixir/lib/enum.ex b/lib/elixir/lib/enum.ex
index 7f9c87ebb..c27a40290 100644
--- a/lib/elixir/lib/enum.ex
+++ b/lib/elixir/lib/enum.ex
@@ -384,19 +384,16 @@ defmodule Enum do
     end
   end
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Enum.chunk_every/2 instead"
   def chunk(enumerable, count), do: chunk(enumerable, count, count, nil)
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Enum.chunk_every/3 instead"
   def chunk(enum, n, step) do
     chunk_every(enum, n, step, nil)
   end
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Enum.chunk_every/4 instead"
   def chunk(enumerable, count, step, leftover) do
@@ -799,7 +796,7 @@ defmodule Enum do
   end
 
   def empty?(enumerable) do
-    case backwards_compatible_slice(enumerable) do
+    case Enumerable.slice(enumerable) do
       {:ok, value, _} ->
         value == 0
 
@@ -908,7 +905,6 @@ defmodule Enum do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Enum.filter/2 + Enum.map/2 or for comprehensions instead"
   def filter_map(enumerable, filter, mapper) when is_list(enumerable) do
     for item <- enumerable, filter.(item), do: mapper.(item)
@@ -1122,7 +1118,6 @@ defmodule Enum do
     end)
   end
 
-  # TODO: Remove on 2.0
   def group_by(enumerable, dict, fun) do
     IO.warn(
       "Enum.group_by/3 with a map/dictionary as second element is deprecated. " <>
@@ -1781,7 +1776,6 @@ defmodule Enum do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Enum.split_with/2 instead"
   def partition(enumerable, fun) do
     split_with(enumerable, fun)
@@ -1830,7 +1824,7 @@ defmodule Enum do
 
   def random(enumerable) do
     result =
-      case backwards_compatible_slice(enumerable) do
+      case Enumerable.slice(enumerable) do
         {:ok, 0, _} ->
           []
 
@@ -2450,12 +2444,17 @@ defmodule Enum do
   end
 
   @doc """
-  Takes the first `amount` items from the `enumerable`.
+  Takes an `amount` of items from the beginning or the end of the `enumerable`.
 
-  If a negative `amount` is given, the `amount` of last values will be taken.
+  If a positive `amount` is given, it takes the `amount` items from the
+  beginning of the `enumerable`.
+
+  If a negative `amount` is given, the `amount` of items will be taken from the end.
   The `enumerable` will be enumerated once to retrieve the proper index and
   the remaining calculation is performed from the end.
 
+  If amount is `0`, it returns `[]`.
+
   ## Examples
 
       iex> Enum.take([1, 2, 3], 2)
@@ -2663,7 +2662,6 @@ defmodule Enum do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Enum.uniq_by/2 instead"
   def uniq(enumerable, fun) do
     uniq_by(enumerable, fun)
@@ -2865,19 +2863,6 @@ defmodule Enum do
     lower_limit + :rand.uniform(upper_limit - lower_limit + 1) - 1
   end
 
-  # TODO: Remove me on Elixir v1.9
-  defp backwards_compatible_slice(args) do
-    try do
-      Enumerable.slice(args)
-    catch
-      :error, :undef ->
-        case __STACKTRACE__ do
-          [{module, :slice, [^args], _} | _] -> {:error, module}
-          stack -> :erlang.raise(:error, :undef, stack)
-        end
-    end
-  end
-
   ## Implementations
 
   ## all?
@@ -3072,7 +3057,7 @@ defmodule Enum do
   end
 
   defp slice_any(enumerable, start, amount) do
-    case backwards_compatible_slice(enumerable) do
+    case Enumerable.slice(enumerable) do
       {:ok, count, _} when start >= count ->
         []
 
@@ -3105,7 +3090,7 @@ defmodule Enum do
   end
 
   defp slice_count_and_fun(enumerable) do
-    case backwards_compatible_slice(enumerable) do
+    case Enumerable.slice(enumerable) do
       {:ok, count, fun} when is_function(fun) ->
         {count, fun}
 
diff --git a/lib/elixir/lib/exception.ex b/lib/elixir/lib/exception.ex
index 85e2826d4..f271d521c 100644
--- a/lib/elixir/lib/exception.ex
+++ b/lib/elixir/lib/exception.ex
@@ -1148,9 +1148,22 @@ defmodule Protocol.UndefinedError do
 
   @impl true
   def message(%{protocol: protocol, value: value, description: description}) do
-    "protocol #{inspect(protocol)} not implemented for #{inspect(value)}" <>
-      maybe_description(description) <> maybe_available(protocol)
-  end
+    "protocol #{inspect(protocol)} not implemented for #{inspect(value)} of type " <>
+      value_type(value) <> maybe_description(description) <> maybe_available(protocol)
+  end
+
+  defp value_type(%{__struct__: struct}), do: "#{inspect(struct)} (a struct)"
+  defp value_type(value) when is_atom(value), do: "Atom"
+  defp value_type(value) when is_bitstring(value), do: "BitString"
+  defp value_type(value) when is_float(value), do: "Float"
+  defp value_type(value) when is_function(value), do: "Function"
+  defp value_type(value) when is_integer(value), do: "Integer"
+  defp value_type(value) when is_list(value), do: "List"
+  defp value_type(value) when is_map(value), do: "Map"
+  defp value_type(value) when is_pid(value), do: "PID"
+  defp value_type(value) when is_port(value), do: "Port"
+  defp value_type(value) when is_reference(value), do: "Reference"
+  defp value_type(value) when is_tuple(value), do: "Tuple"
 
   defp maybe_description(""), do: ""
   defp maybe_description(description), do: ", " <> description
@@ -1161,7 +1174,8 @@ defmodule Protocol.UndefinedError do
         ". There are no implementations for this protocol."
 
       {:consolidated, types} ->
-        ". This protocol is implemented for: #{Enum.map_join(types, ", ", &inspect/1)}"
+        ". This protocol is implemented for the following type(s): " <>
+          Enum.map_join(types, ", ", &inspect/1)
 
       :not_consolidated ->
         ""
@@ -1305,6 +1319,24 @@ defmodule File.CopyError do
   end
 end
 
+defmodule File.RenameError do
+  defexception [:reason, :source, :destination, on: "", action: ""]
+
+  @impl true
+  def message(exception) do
+    formatted = IO.iodata_to_binary(:file.format_error(exception.reason))
+
+    location =
+      case exception.on() do
+        "" -> ""
+        on -> ". #{on}"
+      end
+
+    "could not #{exception.action} from #{inspect(exception.source)} to " <>
+      "#{inspect(exception.destination)}#{location}: #{formatted}"
+  end
+end
+
 defmodule File.LinkError do
   defexception [:reason, :existing, :new, action: ""]
 
diff --git a/lib/elixir/lib/file.ex b/lib/elixir/lib/file.ex
index 13c900bc4..3c917f493 100644
--- a/lib/elixir/lib/file.ex
+++ b/lib/elixir/lib/file.ex
@@ -30,7 +30,7 @@ defmodule File do
   always treated as UTF-8. In particular, we expect that the
   shell and the operating system are configured to use UTF-8
   encoding. Binary filenames are considered raw and passed
-  to the OS as is.
+  to the operating system as is.
 
   ## API
 
@@ -373,6 +373,8 @@ defmodule File do
       machine
     * `:posix` - returns the time as integer seconds since epoch
 
+  Note: Since file times are stored in POSIX time format on most operating systems,
+  it is faster to retrieve file information with the `time: :posix` option.
   """
   @spec stat(Path.t(), stat_options) :: {:ok, File.Stat.t()} | {:error, posix}
   def stat(path, opts \\ []) do
@@ -424,6 +426,8 @@ defmodule File do
     * `:local` - returns a `{date, time}` tuple using the machine time
     * `:posix` - returns the time as integer seconds since epoch
 
+  Note: Since file times are stored in POSIX time format on most operating systems,
+  it is faster to retrieve file information with the `time: :posix` option.
   """
   @spec lstat(Path.t(), stat_options) :: {:ok, File.Stat.t()} | {:error, posix}
   def lstat(path, opts \\ []) do
@@ -712,8 +716,8 @@ defmodule File do
 
   Returns `:ok` in case of success, `{:error, reason}` otherwise.
 
-  Note: The command `mv` in Unix systems behaves differently depending
-  if `source` is a file and the `destination` is an existing directory.
+  Note: The command `mv` in Unix systems behaves differently depending on
+  whether `source` is a file and the `destination` is an existing directory.
   We have chosen to explicitly disallow this behaviour.
 
   ## Examples
@@ -731,30 +735,54 @@ defmodule File do
   end
 
   @doc """
-  Copies the contents in `source` to `destination` preserving its mode.
+  The same as `rename/2` but raises a `File.RenameError` exception if it fails.
+  Returns `:ok` otherwise.
+  """
+  @doc since: "1.9.0"
+  @spec rename!(Path.t(), Path.t()) :: :ok
+  def rename!(source, destination) do
+    case rename(source, destination) do
+      :ok ->
+        :ok
+
+      {:error, reason} ->
+        raise File.RenameError,
+          reason: reason,
+          action: "rename",
+          source: IO.chardata_to_string(source),
+          destination: IO.chardata_to_string(destination)
+    end
+  end
+
+  @doc """
+  Copies the contents in `source_file` to `destination_file` preserving its modes.
+
+  `source_file` and `destination_file` must be a file or a symbolic link to one,
+  or in the case of destination, a path to a non-existent file. If either one of
+  them is a directory, `{:error, :eisdir}` will be returned.
 
   If a file already exists in the destination, it invokes a
   callback which should return `true` if the existing file
   should be overwritten, `false` otherwise. The callback defaults to return `true`.
 
-  The function returns `:ok` in case of success, returns
-  `{:error, reason}` otherwise.
+  The function returns `:ok` in case of success. Otherwise, it returns
+  `{:error, reason}`.
 
   If you want to copy contents from an IO device to another device
   or do a straight copy from a source to a destination without
   preserving modes, check `copy/3` instead.
 
-  Note: The command `cp` in Unix systems behaves differently depending
-  if `destination` is an existing directory or not. We have chosen to
-  explicitly disallow this behaviour. If destination is a directory, an
-  error will be returned.
+  Note: The command `cp` in Unix systems behaves differently depending on
+  whether the destination is an existing directory or not. We have chosen to
+  explicitly disallow copying to a destination which is a directory,
+  and an error will be returned if tried.
   """
   @spec cp(Path.t(), Path.t(), (Path.t(), Path.t() -> boolean)) :: :ok | {:error, posix}
-  def cp(source, destination, callback \\ fn _, _ -> true end) do
-    source = IO.chardata_to_string(source)
-    destination = IO.chardata_to_string(destination)
+  def cp(source_file, destination_file, callback \\ fn _, _ -> true end) do
+    source_file = IO.chardata_to_string(source_file)
+    destination_file = IO.chardata_to_string(destination_file)
 
-    case do_cp_file(source, destination, callback, []) do
+    case do_cp_file(source_file, destination_file, callback, []) do
       {:error, reason, _} -> {:error, reason}
       _ -> :ok
     end
@@ -771,8 +799,8 @@ defmodule File do
   Returns `:ok` otherwise.
   """
   @spec cp!(Path.t(), Path.t(), (Path.t(), Path.t() -> boolean)) :: :ok
-  def cp!(source, destination, callback \\ fn _, _ -> true end) do
-    case cp(source, destination, callback) do
+  def cp!(source_file, destination_file, callback \\ fn _, _ -> true end) do
+    case cp(source_file, destination_file, callback) do
       :ok ->
         :ok
 
@@ -780,26 +808,29 @@ defmodule File do
         raise File.CopyError,
           reason: reason,
           action: "copy",
-          source: IO.chardata_to_string(source),
-          destination: IO.chardata_to_string(destination)
+          source: IO.chardata_to_string(source_file),
+          destination: IO.chardata_to_string(destination_file)
     end
   end
 
   @doc ~S"""
-  Copies the contents in source to destination.
+  Copies the contents in `source` to `destination` recursively, maintaining the
+  source directory structure and modes.
+
+  If `source` is a file or a symbolic link to it, `destination` must be a path
+  to an existent file, a symbolic link to one, or a path to a non-existent file.
+
+  If `source` is a directory, or a symbolic link to it, then `destination` must
+  be an existent `directory` or a symbolic link to one, or a path to a non-existent directory.
 
   If the source is a file, it copies `source` to
-  `destination`. If the source is a directory, it copies
-  the contents inside source into the destination.
+  `destination`. If the `source` is a directory, it copies
+  the contents inside source into the `destination` directory.
 
   If a file already exists in the destination, it invokes `callback`.
   `callback` must be a function that takes two arguments: `source` and `destination`.
   The callback should return `true` if the existing file should be overwritten and `false` otherwise.
 
-  If a directory already exists in the destination
-  where a file is meant to be (or vice versa), this
-  function will fail.
-
   This function may fail while copying files,
   in such cases, it will leave the destination
   directory in a dirty state, where file which have already been copied
@@ -809,9 +840,10 @@ defmodule File do
   success, `files_and_directories` lists all files and directories copied in no
   specific order. It returns `{:error, reason, file}` otherwise.
 
-  Note: The command `cp` in Unix systems behaves differently
-  depending if `destination` is an existing directory or not.
-  We have chosen to explicitly disallow this behaviour.
+  Note: The command `cp` in Unix systems behaves differently depending on
+  whether `destination` is an existing directory or not. We have chosen to
+  explicitly disallow this behaviour. If `source` is a `file` and `destination`
+  is a directory, `{:error, :eisdir}` will be returned.
 
   ## Examples
 
@@ -866,8 +898,6 @@ defmodule File do
     end
   end
 
-  # src may be a file or a directory, dest is definitely
-  # a directory. Returns nil unless an error is found.
   defp do_cp_r(src, dest, callback, acc) when is_list(acc) do
     case :elixir_utils.read_link_type(src) do
       {:ok, :regular} ->
@@ -1655,7 +1685,7 @@ defmodule File do
   end
 
   @doc """
-  Changes the group given by the group id `gid`
+  Changes the group given by the group ID `gid`
   for a given `file`. Returns `:ok` on success, or
   `{:error, reason}` on failure.
   """
@@ -1683,7 +1713,7 @@ defmodule File do
   end
 
   @doc """
-  Changes the owner given by the user id `uid`
+  Changes the owner given by the user ID `uid`
   for a given `file`. Returns `:ok` on success,
   or `{:error, reason}` on failure.
   """
@@ -1733,7 +1763,7 @@ defmodule File do
     [read_ahead: @read_ahead_size] ++ normalize_modes(rest, binary?)
   end
 
-  # TODO: Remove :char_list mode by 2.0
+  # TODO: Remove :char_list mode on v2.0
   defp normalize_modes([mode | rest], _binary?) when mode in [:charlist, :char_list] do
     if mode == :char_list do
       IO.warn("the :char_list mode is deprecated, use :charlist")
diff --git a/lib/elixir/lib/float.ex b/lib/elixir/lib/float.ex
index 35d644efa..34bc533fc 100644
--- a/lib/elixir/lib/float.ex
+++ b/lib/elixir/lib/float.ex
@@ -268,16 +268,14 @@ defmodule Float do
     raise ArgumentError, invalid_precision_message(precision)
   end
 
+  defp round(0.0, _precision, _rounding), do: 0.0
+
   defp round(float, precision, rounding) do
     <<sign::1, exp::11, significant::52-bitstring>> = <<float::float>>
     {num, count, _} = decompose(significant, 1)
     count = count - exp + 1023
 
     cond do
-      # There is no decimal precision on subnormal floats
-      count <= 0 or exp == 0 ->
-        float
-
       # Precision beyond 15 digits
       count >= 104 ->
         case rounding do
@@ -444,11 +442,11 @@ defmodule Float do
     {acc, last_count, last_power}
   end
 
+  @compile {:inline, sign: 2, shift_left: 2}
   defp sign(0, num), do: num
   defp sign(1, num), do: -num
 
-  defp shift_left(num, 0), do: num
-  defp shift_left(num, times), do: shift_left(num <<< 1, times - 1)
+  defp shift_left(num, times), do: num <<< times
 
   defp shift_right(num, 0), do: {num, 0}
   defp shift_right(1, times), do: {1, times}
@@ -495,19 +493,16 @@ defmodule Float do
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use Float.to_charlist/1 instead"
   def to_char_list(float), do: Float.to_charlist(float)
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use :erlang.float_to_list/2 instead"
   def to_char_list(float, options) do
     :erlang.float_to_list(float, expand_compact(options))
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use :erlang.float_to_binary/2 instead"
   def to_string(float, options) do
     :erlang.float_to_binary(float, expand_compact(options))
diff --git a/lib/elixir/lib/gen_event.ex b/lib/elixir/lib/gen_event.ex
index 0368572d9..6f695d8f1 100644
--- a/lib/elixir/lib/gen_event.ex
+++ b/lib/elixir/lib/gen_event.ex
@@ -1,6 +1,4 @@
 defmodule GenEvent do
-  # TODO: Remove by 2.0
-
   # Functions from this module are deprecated in elixir_dispatch.
 
   @moduledoc """
@@ -90,12 +88,10 @@ defmodule GenEvent do
   @deprecated message
   @doc false
   defmacro __using__(_) do
-    %{file: file, line: line} = __CALLER__
-
     deprecation_message =
       "the GenEvent module is deprecated, see its documentation for alternatives"
 
-    :elixir_errors.warn(line, file, deprecation_message)
+    IO.warn(deprecation_message, Macro.Env.stacktrace(__CALLER__))
 
     quote location: :keep do
       @behaviour :gen_event
diff --git a/lib/elixir/lib/gen_server.ex b/lib/elixir/lib/gen_server.ex
index f38773bc8..7c1dedb8a 100644
--- a/lib/elixir/lib/gen_server.ex
+++ b/lib/elixir/lib/gen_server.ex
@@ -135,7 +135,7 @@ defmodule GenServer do
 
       Supervisor.start_link(children, strategy: :one_for_all)
 
-  In both cases, `Stack.start_link/1` is alwaus invoked.
+  In both cases, `Stack.start_link/1` is always invoked.
 
   `use GenServer` also accepts a list of options which configures the
   child specification and therefore how it runs under a supervisor.
@@ -737,7 +737,7 @@ defmodule GenServer do
 
       defoverridable child_spec: 1
 
-      # TODO: Remove this on Elixir v2.0
+      # TODO: Remove this on v2.0
       @before_compile GenServer
 
       @doc false
@@ -819,7 +819,7 @@ defmodule GenServer do
       the arguments given to GenServer.start_link/3 to the server state.
       """
 
-      :elixir_errors.warn(env.line, env.file, message)
+      IO.warn(message, Macro.Env.stacktrace(env))
 
       quote do
         @doc false
diff --git a/lib/elixir/lib/hash_dict.ex b/lib/elixir/lib/hash_dict.ex
index e1de2e6f3..c113b2059 100644
--- a/lib/elixir/lib/hash_dict.ex
+++ b/lib/elixir/lib/hash_dict.ex
@@ -7,8 +7,6 @@ defmodule HashDict do
 
   @moduledoc deprecated: "Use Map instead"
 
-  # TODO: Remove by 2.0
-
   use Dict
 
   @node_bitmap 0b111
diff --git a/lib/elixir/lib/inspect.ex b/lib/elixir/lib/inspect.ex
index eff700a57..e9e413758 100644
--- a/lib/elixir/lib/inspect.ex
+++ b/lib/elixir/lib/inspect.ex
@@ -160,7 +160,7 @@ defimpl Inspect, for: List do
     color("[]", :list, opts)
   end
 
-  # TODO: Remove :char_list and :as_char_lists handling in 2.0
+  # TODO: Remove :char_list and :as_char_lists handling on v2.0
   def inspect(term, opts) do
     %Inspect.Opts{
       charlists: lists,
diff --git a/lib/elixir/lib/inspect/algebra.ex b/lib/elixir/lib/inspect/algebra.ex
index 9e34c7f38..279a3f518 100644
--- a/lib/elixir/lib/inspect/algebra.ex
+++ b/lib/elixir/lib/inspect/algebra.ex
@@ -48,15 +48,15 @@ defmodule Inspect.Opts do
       is useful when debugging failures and crashes for custom inspect
       implementations
 
-    * `:syntax_colors` - when set to a keyword list of colors the output will
-      be colorized. The keys are types and the values are the colors to use for
+    * `:syntax_colors` - when set to a keyword list of colors the output is
+      colorized. The keys are types and the values are the colors to use for
       each type (for example, `[number: :red, atom: :blue]`). Types can include
       `:number`, `:atom`, `regex`, `:tuple`, `:map`, `:list`, and `:reset`.
       Colors can be any `t:IO.ANSI.ansidata/0` as accepted by `IO.ANSI.format/1`.
 
   """
 
-  # TODO: Remove :char_lists key by 2.0
+  # TODO: Remove :char_lists key on v2.0
   defstruct structs: true,
             binaries: :infer,
             charlists: :infer,
@@ -71,7 +71,7 @@ defmodule Inspect.Opts do
 
   @type color_key :: atom
 
-  # TODO: Remove :char_lists key and :as_char_lists value by 2.0
+  # TODO: Remove :char_lists key and :as_char_lists value on v2.0
   @type t :: %__MODULE__{
           structs: boolean,
           binaries: :infer | :as_binaries | :as_strings,
@@ -410,14 +410,12 @@ defmodule Inspect.Algebra do
   defp simple?(:doc_nil), do: true
   defp simple?(other), do: is_binary(other)
 
-  # TODO: Remove on 2.0
   @doc false
   @deprecated "Use a combination of concat/2 and nest/2 instead"
   def surround(left, doc, right) when is_doc(left) and is_doc(doc) and is_doc(right) do
     concat(concat(left, nest(doc, 1)), right)
   end
 
-  # TODO: Remove on 2.0
   @doc false
   @deprecated "Use Inspect.Algebra.container_doc/6 instead"
   def surround_many(
@@ -882,10 +880,11 @@ defmodule Inspect.Algebra do
   #   * flat_no_break - represents a document with breaks as flat not allowed to enter in break mode
   #   * break_no_flat - represents a document with breaks as breaks not allowed to enter in flat mode
   #
-  @typep mode :: :flat | :flat_no_break | :break
+  @typep mode :: :flat | :flat_no_break | :break | :break_no_flat
 
   @spec fits?(width :: integer(), column :: integer(), break? :: boolean(), entries) :: boolean()
-        when entries: [{integer(), mode(), t()}] | {:tail, boolean(), entries}
+        when entries:
+               maybe_improper_list({integer(), mode(), t()}, {:tail, boolean(), entries} | [])
 
   # We need at least a break to consider the document does not fit since a
   # large document without breaks has no option but fitting its current line.
diff --git a/lib/elixir/lib/integer.ex b/lib/elixir/lib/integer.ex
index facf8e8fb..82b5a4c7f 100644
--- a/lib/elixir/lib/integer.ex
+++ b/lib/elixir/lib/integer.ex
@@ -409,12 +409,10 @@ defmodule Integer do
   defp gcd_positive(integer1, 0), do: integer1
   defp gcd_positive(integer1, integer2), do: gcd_positive(integer2, rem(integer1, integer2))
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Integer.to_charlist/1 instead"
   def to_char_list(integer), do: Integer.to_charlist(integer)
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Integer.to_charlist/2 instead"
   def to_char_list(integer, base), do: Integer.to_charlist(integer, base)
diff --git a/lib/elixir/lib/io.ex b/lib/elixir/lib/io.ex
index 5b7b8db3b..8d8b382c4 100644
--- a/lib/elixir/lib/io.ex
+++ b/lib/elixir/lib/io.ex
@@ -38,11 +38,7 @@ defmodule IO do
   @type nodata :: {:error, term} | :eof
   @type chardata :: String.t() | maybe_improper_list(char | chardata, String.t() | [])
 
-  defmacrop is_iodata(data) do
-    quote do
-      is_list(unquote(data)) or is_binary(unquote(data))
-    end
-  end
+  defguardp is_iodata(data) when is_list(data) or is_binary(data)
 
   @doc """
   Reads from the IO `device`.
@@ -214,7 +210,7 @@ defmodule IO do
   """
   @spec warn(chardata | String.Chars.t(), Exception.stacktrace()) :: :ok
   def warn(message, []) do
-    :elixir_errors.bare_warn(nil, nil, [to_chardata(message), ?\n])
+    :elixir_errors.io_warn(nil, nil, [to_chardata(message), ?\n])
   end
 
   def warn(message, [{_, _, _, opts} | _] = stacktrace) do
@@ -222,7 +218,7 @@ defmodule IO do
     message = [to_chardata(message), ?\n, "  ", formatted_trace, ?\n]
     line = opts[:line]
     file = opts[:file]
-    :elixir_errors.bare_warn(line, file && List.to_string(file), message)
+    :elixir_errors.io_warn(line, file && List.to_string(file), message)
   end
 
   @doc """
@@ -494,8 +490,8 @@ defmodule IO do
 
   """
   @spec iodata_to_binary(iodata) :: binary
-  def iodata_to_binary(item) do
-    :erlang.iolist_to_binary(item)
+  def iodata_to_binary(iodata) do
+    :erlang.iolist_to_binary(iodata)
   end
 
   @doc """
@@ -510,8 +506,8 @@ defmodule IO do
 
   """
   @spec iodata_length(iodata) :: non_neg_integer
-  def iodata_length(item) do
-    :erlang.iolist_size(item)
+  def iodata_length(iodata) do
+    :erlang.iolist_size(iodata)
   end
 
   @doc false
diff --git a/lib/elixir/lib/io/ansi.ex b/lib/elixir/lib/io/ansi.ex
index 2715e0520..2eb1f6f37 100644
--- a/lib/elixir/lib/io/ansi.ex
+++ b/lib/elixir/lib/io/ansi.ex
@@ -237,7 +237,7 @@ defmodule IO.ANSI do
   The named sequences are represented by atoms.
 
   An optional boolean parameter can be passed to enable or disable
-  emitting actual ANSI codes. When `false`, no ANSI codes will emitted.
+  emitting actual ANSI codes. When `false`, no ANSI codes will be emitted.
   By default checks if ANSI is enabled using the `enabled?/0` function.
 
   ## Examples
diff --git a/lib/elixir/lib/io/ansi/docs.ex b/lib/elixir/lib/io/ansi/docs.ex
index 458cffb5d..9ab608331 100644
--- a/lib/elixir/lib/io/ansi/docs.ex
+++ b/lib/elixir/lib/io/ansi/docs.ex
@@ -531,7 +531,7 @@ defmodule IO.ANSI.Docs do
   end
 
   defp remove_square_brackets_in_link(text) do
-    ~r{\[(.*?)\]\((.*?)\)}
+    ~r{\[([^\]]*?)\]\((.*?)\)}
     |> Regex.recompile!()
     |> Regex.replace(text, "\\1 (\\2)")
   end
diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex
index 8ba28ff19..e4317ba20 100644
--- a/lib/elixir/lib/kernel.ex
+++ b/lib/elixir/lib/kernel.ex
@@ -415,16 +415,16 @@ defmodule Kernel do
   reasons above, its exit is considered normal and the Operating
   System process will exit with status 0.
 
-  It is, however, possible to customize the Operating System exit
+  It is, however, possible to customize the operating system exit
   signal by invoking:
 
       exit({:shutdown, integer})
 
-  This will cause the OS process to exit with the status given by
+  This will cause the operating system process to exit with the status given by
   `integer` while signaling all linked Erlang processes to politely
   shut down.
 
-  Any other exit reason will cause the OS process to exit with
+  Any other exit reason will cause the operating system process to exit with
   status `1` and linked Erlang processes to crash.
   """
   @spec exit(term) :: no_return
@@ -837,6 +837,8 @@ defmodule Kernel do
   @doc """
   Rounds a number to the nearest integer.
 
+  If the number is equidistant to the two nearest integers, rounds away from zero.
+
   Allowed in guard tests. Inlined by the compiler.
 
   ## Examples
@@ -853,6 +855,12 @@ defmodule Kernel do
       iex> round(-9)
       -9
 
+      iex> round(2.5)
+      3
+
+      iex> round(-2.5)
+      -3
+
   """
   @doc guard: true
   @spec round(float) :: integer
@@ -2134,18 +2142,7 @@ defmodule Kernel do
 
   Uses the `Access` module to traverse the structures
   according to the given `keys`, unless the `key` is a
-  function.
-
-  If a key is a function, the function will be invoked
-  passing three arguments:
-
-    * the operation (`:get`)
-    * the data to be accessed
-    * a function to be invoked next
-
-  This means `get_in/2` can be extended to provide
-  custom lookups. The downside is that functions cannot be
-  stored as keys in the accessed data structures.
+  function, which is detailed in a later section.
 
   ## Examples
 
@@ -2160,9 +2157,21 @@ defmodule Kernel do
       iex> get_in(users, ["unknown", :age])
       nil
 
-  When one of the keys is a function that takes three arguments, the function
-  is invoked. In the example below, we use a function to get all the maps
-  inside a list:
+  ## Functions as keys
+
+  If a key is a function, the function will be invoked passing three
+  arguments:
+
+    * the operation (`:get`)
+    * the data to be accessed
+    * a function to be invoked next
+
+  This means `get_in/2` can be extended to provide custom lookups.
+  The downside is that functions cannot be stored as keys in the accessed
+  data structures.
+
+  In the example below, we use a function to get all the maps inside
+  a list:
 
       iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
       iex> all = fn :get, data, next -> Enum.map(data, next) end
@@ -2172,6 +2181,10 @@ defmodule Kernel do
   If the previous value before invoking the function is `nil`,
   the function *will* receive `nil` as a value and must handle it
   accordingly.
+
+  The `Access` module ships with many convenience accessor functions,
+  like the `all` anonymous function defined above. See `Access.all/0`,
+  `Access.key/2`, and others as examples.
   """
   @spec get_in(Access.t(), nonempty_list(term)) :: term
   def get_in(data, keys)
@@ -2246,19 +2259,8 @@ defmodule Kernel do
       should be removed from the structure and returned.
 
   This function uses the `Access` module to traverse the structures
-  according to the given `keys`, unless the `key` is a
-  function.
-
-  If a key is a function, the function will be invoked
-  passing three arguments:
-
-    * the operation (`:get_and_update`)
-    * the data to be accessed
-    * a function to be invoked next
-
-  This means `get_and_update_in/3` can be extended to provide
-  custom lookups. The downside is that functions cannot be stored
-  as keys in the accessed data structures.
+  according to the given `keys`, unless the `key` is a function,
+  which is detailed in a later section.
 
   ## Examples
 
@@ -2271,6 +2273,19 @@ defmodule Kernel do
       iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
       {27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
 
+  ## Functions as keys
+
+  If a key is a function, the function will be invoked passing three
+  arguments:
+
+    * the operation (`:get_and_update`)
+    * the data to be accessed
+    * a function to be invoked next
+
+  This means `get_and_update_in/3` can be extended to provide custom
+  lookups. The downside is that functions cannot be stored as keys
+  in the accessed data structures.
+
   When one of the keys is a function, the function is invoked.
   In the example below, we use a function to get and increment all
   ages inside a list:
@@ -2843,7 +2858,7 @@ defmodule Kernel do
 
       name == :behavior ->
         warn_message = "@behavior attribute is not supported, please use @behaviour instead"
-        :elixir_errors.warn(env.line, env.file, warn_message)
+        IO.warn(warn_message, Macro.Env.stacktrace(env))
 
       :lists.member(name, [:moduledoc, :typedoc, :doc]) ->
         arg = {env.line, arg}
@@ -3318,7 +3333,7 @@ defmodule Kernel do
             <<"piping into a unary operator is deprecated, please use the ",
               "qualified name. For example, Kernel.+(5), instead of +5">>
 
-          :elixir_errors.warn(__CALLER__.line, __CALLER__.file, message)
+          IO.warn(message, Macro.Env.stacktrace(__CALLER__))
 
         _ ->
           :ok
@@ -3718,7 +3733,8 @@ defmodule Kernel do
   In the example above, two modules - `Foo` and `Foo.Bar` - are created.
   When nesting, Elixir automatically creates an alias to the inner module,
   allowing the second module `Foo.Bar` to be accessed as `Bar` in the same
-  lexical scope where it's defined (the `Foo` module).
+  lexical scope where it's defined (the `Foo` module). This only happens
+  if the nested module is defined via an alias.
 
   If the `Foo.Bar` module is moved somewhere else, the references to `Bar` in
   the `Foo` module need to be updated to the fully-qualified name (`Foo.Bar`) or
@@ -3734,15 +3750,6 @@ defmodule Kernel do
         # code here can refer to "Foo.Bar" as just "Bar"
       end
 
-  ## Module names
-
-  A module name can be any atom, but Elixir provides a special syntax which is
-  usually used for module names. What is called a module name is an
-  _uppercase ASCII letter_ followed by any number of _lowercase or
-  uppercase ASCII letters_, _numbers_, or _underscores_.
-  This identifier is equivalent to an atom prefixed by `Elixir.`. So in the
-  `defmodule Foo` example `Foo` is equivalent to `:"Elixir.Foo"`
-
   ## Dynamic names
 
   Elixir module names can be dynamically generated. This is very
@@ -3754,8 +3761,8 @@ defmodule Kernel do
 
   Elixir will accept any module name as long as the expression passed as the
   first argument to `defmodule/2` evaluates to an atom.
-  Note that, when a dynamic name is used, Elixir won't nest the name under the
-  current module nor automatically set up an alias.
+  Note that, when a dynamic name is used, Elixir won't nest the name under
+  the current module nor automatically set up an alias.
 
   ## Reserved module names
 
@@ -3790,12 +3797,8 @@ defmodule Kernel do
       case boot? and is_atom(expanded) do
         true ->
           # Expand the module considering the current environment/nesting
-          full = expand_module(alias, expanded, env)
-
-          # Generate the alias for this module definition
-          {new, old} = module_nesting(env.module, full)
+          {full, old, new} = expand_module(alias, expanded, env)
           meta = [defined: full, context: env.module] ++ alias_meta(alias)
-
           {full, {:alias, meta, [old, [as: new, warn: false]]}}
 
         false ->
@@ -3835,21 +3838,29 @@ defmodule Kernel do
   defp alias_meta({:__aliases__, meta, _}), do: meta
   defp alias_meta(_), do: []
 
-  # defmodule :foo
-  defp expand_module(raw, _module, _env) when is_atom(raw), do: raw
-
   # defmodule Elixir.Alias
-  defp expand_module({:__aliases__, _, [:"Elixir" | t]}, module, _env) when t != [], do: module
+  defp expand_module({:__aliases__, _, [:"Elixir", _ | _]}, module, _env),
+    do: {module, module, nil}
 
   # defmodule Alias in root
-  defp expand_module({:__aliases__, _, _}, module, %{module: nil}), do: module
+  defp expand_module({:__aliases__, _, _}, module, %{module: nil}),
+    do: {module, module, nil}
 
   # defmodule Alias nested
-  defp expand_module({:__aliases__, _, t}, _module, env),
-    do: :elixir_aliases.concat([env.module | t])
+  defp expand_module({:__aliases__, _, [h | t]}, _module, env) when is_atom(h) do
+    module = :elixir_aliases.concat([env.module, h])
+    alias = String.to_atom("Elixir." <> Atom.to_string(h))
+
+    case t do
+      [] -> {module, module, alias}
+      _ -> {String.to_atom(Enum.join([module | t], ".")), module, alias}
+    end
+  end
 
   # defmodule _
-  defp expand_module(_raw, module, env), do: :elixir_aliases.concat([env.module, module])
+  defp expand_module(_raw, module, _env) do
+    {module, module, nil}
+  end
 
   # quote vars to be injected into the module definition
   defp module_vars([{key, kind} | vars], counter) do
@@ -3868,53 +3879,6 @@ defmodule Kernel do
     []
   end
 
-  # Gets two modules' names and returns an alias
-  # which can be passed down to the alias directive
-  # and it will create a proper shortcut representing
-  # the given nesting.
-  #
-  # Examples:
-  #
-  #     module_nesting(:"Elixir.Foo.Bar", :"Elixir.Foo.Bar.Baz.Bat")
-  #     {:"Elixir.Baz", :"Elixir.Foo.Bar.Baz"}
-  #
-  # In case there is no nesting/no module:
-  #
-  #     module_nesting(nil, :"Elixir.Foo.Bar.Baz.Bat")
-  #     {nil, :"Elixir.Foo.Bar.Baz.Bat"}
-  #
-  defp module_nesting(nil, full) do
-    {nil, full}
-  end
-
-  defp module_nesting(prefix, full) do
-    case split_module(prefix) do
-      [] -> {nil, full}
-      prefix -> module_nesting(prefix, split_module(full), [], full)
-    end
-  end
-
-  defp module_nesting([x | t1], [x | t2], acc, full) do
-    module_nesting(t1, t2, [x | acc], full)
-  end
-
-  defp module_nesting([], [h | _], acc, _full) do
-    as = String.to_atom(<<"Elixir.", h::binary>>)
-    alias = :elixir_aliases.concat(:lists.reverse([h | acc]))
-    {as, alias}
-  end
-
-  defp module_nesting(_, _, _acc, full) do
-    {nil, full}
-  end
-
-  defp split_module(atom) do
-    case :binary.split(Atom.to_string(atom), ".", [:global]) do
-      ["Elixir" | t] -> t
-      _ -> []
-    end
-  end
-
   @doc ~S"""
   Defines a function with the given name and body.
 
@@ -4385,7 +4349,7 @@ defmodule Kernel do
         end
       end
 
-      # TODO: Only call Kernel.struct! by 2.0
+      # TODO: Change the implementation on v2.0 to simply call Kernel.struct!/2
       @impl true
       def exception(args) when is_list(args) do
         struct = __struct__()
@@ -5020,20 +4984,18 @@ defmodule Kernel do
       target =
         Keyword.get(opts, :to) || raise ArgumentError, "expected to: to be given as argument"
 
-      # TODO: Raise on 2.0
-      %{file: file, line: line} = __ENV__
-
       if is_list(funs) do
-        message =
-          "passing a list to Kernel.defdelegate/2 is deprecated, " <>
-            "please define each delegate separately"
-
-        :elixir_errors.warn(line, file, message)
+        IO.warn(
+          "passing a list to Kernel.defdelegate/2 is deprecated, please define each delegate separately",
+          Macro.Env.stacktrace(__ENV__)
+        )
       end
 
-      # TODO: Remove on 2.0
       if Keyword.has_key?(opts, :append_first) do
-        :elixir_errors.warn(line, file, "Kernel.defdelegate/2 :append_first option is deprecated")
+        IO.warn(
+          "Kernel.defdelegate/2 :append_first option is deprecated",
+          Macro.Env.stacktrace(__ENV__)
+        )
       end
 
       for fun <- List.wrap(funs) do
@@ -5417,7 +5379,7 @@ defmodule Kernel do
   end
 
   @doc false
-  # TODO: Remove by 2.0 (also hard-coded in elixir_dispatch)
+  # TODO: Remove on v2.0 (also hard-coded in elixir_dispatch)
   @deprecated "Use Kernel.to_charlist/1 instead"
   defmacro to_char_list(arg) do
     quote(do: Kernel.to_charlist(unquote(arg)))
diff --git a/lib/elixir/lib/kernel/cli.ex b/lib/elixir/lib/kernel/cli.ex
index 7b5d70e34..eaa5881f2 100644
--- a/lib/elixir/lib/kernel/cli.ex
+++ b/lib/elixir/lib/kernel/cli.ex
@@ -88,6 +88,13 @@ defmodule Kernel.CLI do
     [iodata, ?\n, Exception.format_stacktrace(prune_stacktrace(stacktrace))]
   end
 
+  @doc false
+  def rpc_eval(expr) do
+    wrapper(fn -> :elixir.eval(to_charlist(expr), [], []) end)
+  catch
+    kind, reason -> {kind, reason, __STACKTRACE__}
+  end
+
   ## Helpers
 
   defp at_exit(res) do
@@ -232,25 +239,20 @@ defmodule Kernel.CLI do
     parse_shared(t, %{config | commands: [{:eval, h} | config.commands]})
   end
 
-  defp parse_shared(["-r", h | t], config) do
-    parse_shared(t, %{config | commands: [{:require, h} | config.commands]})
+  defp parse_shared(["--eval", h | t], config) do
+    parse_shared(t, %{config | commands: [{:eval, h} | config.commands]})
   end
 
-  defp parse_shared(["-pr", h | t], config) do
-    parse_shared(t, %{config | commands: [{:parallel_require, h} | config.commands]})
+  defp parse_shared(["--rpc-eval", node, h | t], config) do
+    parse_shared(t, %{config | commands: [{:rpc_eval, node, h} | config.commands]})
   end
 
-  @erl_arg_options ["--erl", "--sname", "--name", "--cookie"] ++
-                     ["--logger-otp-reports", "--logger-sasl-reports"]
-
-  @erl_boolean_options ["--detached", "--hidden", "--werl"]
-
-  defp parse_shared([erl, _ | t], config) when erl in @erl_arg_options do
-    parse_shared(t, config)
+  defp parse_shared(["-r", h | t], config) do
+    parse_shared(t, %{config | commands: [{:require, h} | config.commands]})
   end
 
-  defp parse_shared([erl | t], config) when erl in @erl_boolean_options do
-    parse_shared(t, config)
+  defp parse_shared(["-pr", h | t], config) do
+    parse_shared(t, %{config | commands: [{:parallel_require, h} | config.commands]})
   end
 
   defp parse_shared(list, config) do
@@ -290,7 +292,7 @@ defmodule Kernel.CLI do
         shared_option?(list, config, &parse_argv(&1, &2))
 
       _ ->
-        if Keyword.has_key?(config.commands, :eval) do
+        if List.keymember?(config.commands, :eval, 0) do
           {config, list}
         else
           {%{config | commands: [{:file, h} | config.commands]}, t}
@@ -395,6 +397,15 @@ defmodule Kernel.CLI do
     wrapper(fn -> Code.eval_string(expr, []) end)
   end
 
+  defp process_command({:rpc_eval, node, expr}, _config) when is_binary(expr) do
+    case :rpc.call(String.to_atom(node), __MODULE__, :rpc_eval, [expr]) do
+      :ok -> :ok
+      {:badrpc, {:EXIT, exit}} -> Process.exit(self(), exit)
+      {:badrpc, reason} -> {:error, "--rpc-eval : RPC failed with reason #{inspect(reason)}"}
+      {kind, error, stack} -> :erlang.raise(kind, error, stack)
+    end
+  end
+
   defp process_command({:app, app}, _config) when is_binary(app) do
     case Application.ensure_all_started(String.to_atom(app)) do
       {:error, {app, reason}} ->
diff --git a/lib/elixir/lib/kernel/parallel_compiler.ex b/lib/elixir/lib/kernel/parallel_compiler.ex
index 77485cde6..47e2b317a 100644
--- a/lib/elixir/lib/kernel/parallel_compiler.ex
+++ b/lib/elixir/lib/kernel/parallel_compiler.ex
@@ -107,7 +107,6 @@ defmodule Kernel.ParallelCompiler do
     spawn_workers(files, :require, options)
   end
 
-  # TODO: Remove on 2.0
   @doc false
   @deprecated "Use Kernel.ParallelCompiler.compile/2 instead"
   def files(files, options \\ []) when is_list(options) do
@@ -117,7 +116,6 @@ defmodule Kernel.ParallelCompiler do
     end
   end
 
-  # TODO: Remove on 2.0
   @doc false
   @deprecated "Use Kernel.ParallelCompiler.compile_to_path/2 instead"
   def files_to_path(files, path, options \\ []) when is_binary(path) and is_list(options) do
diff --git a/lib/elixir/lib/kernel/parallel_require.ex b/lib/elixir/lib/kernel/parallel_require.ex
index 38622b748..869d18d9b 100644
--- a/lib/elixir/lib/kernel/parallel_require.ex
+++ b/lib/elixir/lib/kernel/parallel_require.ex
@@ -1,5 +1,4 @@
 defmodule Kernel.ParallelRequire do
-  # TODO: Remove on 2.0
   @moduledoc false
 
   @deprecated "Use Kernel.ParallelCompiler.require/2 instead"
diff --git a/lib/elixir/lib/kernel/typespec.ex b/lib/elixir/lib/kernel/typespec.ex
index 59c5903b5..e80779a3d 100644
--- a/lib/elixir/lib/kernel/typespec.ex
+++ b/lib/elixir/lib/kernel/typespec.ex
@@ -1,5 +1,4 @@
 defmodule Kernel.Typespec do
-  # TODO: Remove deprecated code on 2.0 and move this module to Module.Typespec.
   @moduledoc false
 
   ## Deprecated API moved to Code.Typespec
@@ -76,12 +75,21 @@ defmodule Kernel.Typespec do
 
   def spec_to_callback(module, {name, arity} = signature)
       when is_atom(module) and is_atom(name) and arity in 0..255 do
-    {_set, bag} = :elixir_module.data_tables(module)
+    {set, bag} = :elixir_module.data_tables(module)
 
     filter = fn {:spec, expr, pos} ->
       if spec_to_signature(expr) == signature do
-        delete_typespec(bag, :spec, expr, pos)
-        store_typespec(bag, :callback, expr, pos)
+        kind = :callback
+        store_typespec(bag, kind, expr, pos)
+
+        case :ets.lookup(set, {:function, name, arity}) do
+          [{{:function, ^name, ^arity}, line, _, doc, doc_meta}] ->
+            store_doc(set, kind, name, arity, line, :doc, doc, doc_meta)
+
+          _ ->
+            nil
+        end
+
         true
       else
         false
@@ -109,8 +117,11 @@ defmodule Kernel.Typespec do
 
     case spec_to_signature(expr) do
       {name, arity} ->
-        {line, doc} = get_doc_info(set, :doc, line)
-        store_doc(set, kind, name, arity, line, :doc, doc, %{})
+        # store doc only once in case callback has multiple clauses
+        unless :ets.member(set, {kind, name, arity}) do
+          {line, doc} = get_doc_info(set, :doc, line)
+          store_doc(set, kind, name, arity, line, :doc, doc, %{})
+        end
 
       :error ->
         :error
@@ -131,7 +142,7 @@ defmodule Kernel.Typespec do
           warning =
             "type #{name}/#{arity} is private, @typedoc's are always discarded for private types"
 
-          :elixir_errors.warn(line, file, warning)
+          :elixir_errors.erl_warn(line, file, warning)
         end
 
       {name, arity} ->
@@ -169,11 +180,6 @@ defmodule Kernel.Typespec do
     :ok
   end
 
-  defp delete_typespec(bag, key, expr, pos) do
-    :ets.delete_object(bag, {{:accumulate, key}, {key, expr, pos}})
-    :ok
-  end
-
   defp store_doc(set, kind, name, arity, line, doc_kind, doc, spec_meta) do
     doc_meta = get_doc_meta(spec_meta, doc_kind, set)
     :ets.insert(set, {{kind, name, arity}, line, doc, doc_meta})
@@ -261,7 +267,7 @@ defmodule Kernel.Typespec do
     fun = fn {_kind, {name, arity} = type_pair, _line, _type, export} ->
       if not export and not :lists.member(type_pair, state.used_type_pairs) do
         %{^type_pair => {file, line}} = state.defined_type_pairs
-        :elixir_errors.warn(line, file, "type #{name}/#{arity} is unused")
+        :elixir_errors.erl_warn(line, file, "type #{name}/#{arity} is unused")
         false
       else
         true
@@ -312,7 +318,7 @@ defmodule Kernel.Typespec do
 
     if underspecified?(kind, arity, spec) do
       message = "@#{kind} type #{name}/#{arity} is underspecified and therefore meaningless"
-      :elixir_errors.warn(caller.line, caller.file, message)
+      :elixir_errors.erl_warn(caller.line, caller.file, message)
     end
 
     {{kind, {name, arity}, caller.line, type, export}, state}
@@ -382,7 +388,7 @@ defmodule Kernel.Typespec do
     {{kind, {name, arity}, caller.line, spec}, state}
   end
 
-  # TODO: Remove char_list type by 2.0
+  # TODO: Remove char_list type by v2.0
   defp built_in_type?(:char_list, 0), do: true
   defp built_in_type?(:charlist, 0), do: true
   defp built_in_type?(:as_boolean, 1), do: true
@@ -502,11 +508,6 @@ defmodule Kernel.Typespec do
 
   defp typespec({:%{}, meta, fields} = map, vars, caller, state) do
     fun = fn
-      {k, v}, state when is_atom(k) ->
-        {arg1, state} = typespec(k, vars, caller, state)
-        {arg2, state} = typespec(v, vars, caller, state)
-        {{:type, line(meta), :map_field_exact, [arg1, arg2]}, state}
-
       {{:required, meta2, [k]}, v}, state ->
         {arg1, state} = typespec(k, vars, caller, state)
         {arg2, state} = typespec(v, vars, caller, state)
@@ -518,15 +519,9 @@ defmodule Kernel.Typespec do
         {{:type, line(meta2), :map_field_assoc, [arg1, arg2]}, state}
 
       {k, v}, state ->
-        # TODO: Warn on Elixir v1.8 (since v1.6 is the first version to drop support for 18 and
-        # older)
-        # warning =
-        #   "invalid map specification. %{foo => bar} is deprecated in favor of " <>
-        #   "%{required(foo) => bar} and %{optional(foo) => bar}."
-        # :elixir_errors.warn(caller.line, caller.file, warning)
         {arg1, state} = typespec(k, vars, caller, state)
         {arg2, state} = typespec(v, vars, caller, state)
-        {{:type, line(meta), :map_field_assoc, [arg1, arg2]}, state}
+        {{:type, line(meta), :map_field_exact, [arg1, arg2]}, state}
 
       {:|, _, [_, _]}, _state ->
         error =
@@ -655,8 +650,8 @@ defmodule Kernel.Typespec do
           "invalid type annotation. Type annotations cannot be nested: " <>
             "#{Macro.to_string(ann_type)}"
 
-        # TODO: make this an error in elixir 2.0 and remove the code below
-        :elixir_errors.warn(caller.line, caller.file, message)
+        # TODO: Make this an error on v2.0 and remove the code below
+        :elixir_errors.erl_warn(caller.line, caller.file, message)
 
         # This may be generating an invalid typespec but we need to generate it
         # to avoid breaking existing code that was valid but only broke dialyzer
@@ -673,9 +668,9 @@ defmodule Kernel.Typespec do
       "invalid type annotation. When using the | operator to represent the union of types, " <>
         "make sure to wrap type annotations in parentheses: #{Macro.to_string(expr)}"
 
-    # TODO: make this an error in Elixir 2.0, and remove the code below and the
-    # :undefined_type_error_enabled? key from the state
-    :elixir_errors.warn(caller.line, caller.file, message)
+    # TODO: Make this an error on v2.0, and remove the code below and
+    # the :undefined_type_error_enabled? key from the state
+    :elixir_errors.erl_warn(caller.line, caller.file, message)
 
     # This may be generating an invalid typespec but we need to generate it
     # to avoid breaking existing code that was valid but only broke dialyzer
@@ -769,8 +764,7 @@ defmodule Kernel.Typespec do
         "For character lists, use charlist() type, for strings, String.t()\n" <>
         Exception.format_stacktrace(Macro.Env.stacktrace(caller))
 
-    :elixir_errors.warn(caller.line, caller.file, warning)
-
+    :elixir_errors.erl_warn(caller.line, caller.file, warning)
     {args, state} = :lists.mapfoldl(&typespec(&1, vars, caller, &2), state, args)
     {{:type, line(meta), :string, args}, state}
   end
@@ -781,17 +775,15 @@ defmodule Kernel.Typespec do
         "For non-empty character lists, use nonempty_charlist() type, for strings, String.t()\n" <>
         Exception.format_stacktrace(Macro.Env.stacktrace(caller))
 
-    :elixir_errors.warn(caller.line, caller.file, warning)
-
+    :elixir_errors.erl_warn(caller.line, caller.file, warning)
     {args, state} = :lists.mapfoldl(&typespec(&1, vars, caller, &2), state, args)
     {{:type, line(meta), :nonempty_string, args}, state}
   end
 
-  # TODO: Remove char_list type by 2.0
   defp typespec({type, _meta, []}, vars, caller, state) when type in [:charlist, :char_list] do
     if type == :char_list do
       warning = "the char_list() type is deprecated, use charlist()"
-      :elixir_errors.warn(caller.line, caller.file, warning)
+      :elixir_errors.erl_warn(caller.line, caller.file, warning)
     end
 
     typespec(quote(do: :elixir.charlist()), vars, caller, state)
diff --git a/lib/elixir/lib/kernel/utils.ex b/lib/elixir/lib/kernel/utils.ex
index 0e6a18935..39031bc80 100644
--- a/lib/elixir/lib/kernel/utils.ex
+++ b/lib/elixir/lib/kernel/utils.ex
@@ -25,7 +25,7 @@ defmodule Kernel.Utils do
   Callback for defdelegate.
   """
   def defdelegate(fun, opts) when is_list(opts) do
-    # TODO: Remove by 2.0
+    # TODO: Remove on v2.0
     append_first? = Keyword.get(opts, :append_first, false)
 
     {name, args} =
diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex
index af59f138c..2990bf37e 100644
--- a/lib/elixir/lib/keyword.ex
+++ b/lib/elixir/lib/keyword.ex
@@ -1048,7 +1048,6 @@ defmodule Keyword do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Kernel.length/1 instead"
   def size(keyword) do
     length(keyword)
diff --git a/lib/elixir/lib/list.ex b/lib/elixir/lib/list.ex
index 1ca50550f..8131bd5a6 100644
--- a/lib/elixir/lib/list.ex
+++ b/lib/elixir/lib/list.ex
@@ -70,14 +70,21 @@ defmodule List do
 
   ## Charlists
 
-  If a list is made of non-negative integers, it can also be called
-  a charlist. Elixir uses single quotes to define charlists:
+  If a list is made of non-negative integers, where each integer represents a
+  Unicode codepoint, the list can also be called a charlist. These integers
+  must:
+
+    * be within the range `0..0x10FFFF` (`0..1_114_111`);
+    * and be out of the range `0xD800..0xDFFF` (`55_296..57_343`), which is
+      reserved in Unicode for UTF-16 surrogate pairs.
+
+  Elixir uses single quotes to define charlists:
 
       iex> 'héllo'
       [104, 233, 108, 108, 111]
 
-  In particular, charlists may be printed back in single
-  quotes if they contain only ASCII-printable codepoints:
+  In particular, charlists will be printed back by default in single
+  quotes if they contain only printable ASCII characters:
 
       iex> 'abc'
       'abc'
@@ -96,17 +103,19 @@ defmodule List do
       #=>    {:logger, 'logger', '1.0.0'}
       #=>  ]
 
-  A list can be checked if it is made of printable ASCII
-  codepoints with `ascii_printable?/2`.
+  A list can be checked if it is made of only printable ASCII
+  characters with `ascii_printable?/2`.
+
+  Improper lists are never deemed as charlists.
   """
 
   @compile :inline_list_funcs
 
   @doc """
-  Deletes the given `item` from the `list`. Returns a new list without
-  the item.
+  Deletes the given `element` from the `list`. Returns a new list without
+  the element.
 
-  If the `item` occurs more than once in the `list`, just
+  If the `element` occurs more than once in the `list`, just
   the first occurrence is removed.
 
   ## Examples
@@ -119,10 +128,10 @@ defmodule List do
 
   """
   @spec delete(list, any) :: list
-  def delete(list, item)
-  def delete([item | list], item), do: list
-  def delete([other | list], item), do: [other | delete(list, item)]
-  def delete([], _item), do: []
+  def delete(list, element)
+  def delete([element | list], element), do: list
+  def delete([other | list], element), do: [other | delete(list, element)]
+  def delete([], _element), do: []
 
   @doc """
   Duplicates the given element `n` times in a list.
@@ -245,7 +254,7 @@ defmodule List do
 
   @doc """
   Receives a list of tuples and returns the first tuple
-  where the item at `position` in the tuple matches the
+  where the element at `position` in the tuple matches the
   given `key`.
 
   ## Examples
@@ -267,7 +276,7 @@ defmodule List do
 
   @doc """
   Receives a list of tuples and returns `true` if there is
-  a tuple where the item at `position` in the tuple matches
+  a tuple where the element at `position` in the tuple matches
   the given `key`.
 
   ## Examples
@@ -288,7 +297,7 @@ defmodule List do
   end
 
   @doc """
-  Receives a list of tuples and if the identified item by `key` at `position`
+  Receives a list of tuples and if the identified element by `key` at `position`
   exists, it is replaced with `new_tuple`.
 
   ## Examples
@@ -306,7 +315,7 @@ defmodule List do
   end
 
   @doc """
-  Receives a list of tuples and sorts the items
+  Receives a list of tuples and sorts the elements
   at `position` of the tuples. The sort is stable.
 
   ## Examples
@@ -324,10 +333,10 @@ defmodule List do
   end
 
   @doc """
-  Receives a `list` of tuples and replaces the item
+  Receives a `list` of tuples and replaces the element
   identified by `key` at `position` with `new_tuple`.
 
-  If the item does not exist, it is added to the end of the `list`.
+  If the element does not exist, it is added to the end of the `list`.
 
   ## Examples
 
@@ -345,7 +354,7 @@ defmodule List do
 
   @doc """
   Receives a `list` of tuples and deletes the first tuple
-  where the item at `position` matches the
+  where the element at `position` matches the
   given `key`. Returns the new list.
 
   ## Examples
@@ -387,7 +396,7 @@ defmodule List do
   @spec keytake([tuple], any, non_neg_integer) :: {tuple, [tuple]} | nil
   def keytake(list, key, position) do
     case :lists.keytake(key, position + 1, list) do
-      {:value, item, list} -> {item, list}
+      {:value, element, list} -> {element, list}
       false -> nil
     end
   end
@@ -500,39 +509,9 @@ defmodule List do
   end
 
   defp ascii_printable_guarded?([char | rest], counter)
-       when is_integer(char) and char >= 32 and char <= 126 do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\n | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\r | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\t | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\v | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\b | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\f | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\e | rest], counter) do
-    ascii_printable_guarded?(rest, decrement(counter))
-  end
-
-  defp ascii_printable_guarded?([?\a | rest], counter) do
+       # 7..13 is the range '\a\b\t\n\v\f\r'. 32..126 are ASCII printables.
+       when is_integer(char) and
+              ((char >= 7 and char <= 13) or char == ?\e or (char >= 32 and char <= 126)) do
     ascii_printable_guarded?(rest, decrement(counter))
   end
 
@@ -548,11 +527,11 @@ defmodule List do
 
   ## Examples
 
-     iex> List.improper?([1, 2 | 3])
-     true
+      iex> List.improper?([1, 2 | 3])
+      true
 
-     iex> List.improper?([1, 2, 3])
-     false
+      iex> List.improper?([1, 2, 3])
+      false
 
   """
   @doc since: "1.8.0"
diff --git a/lib/elixir/lib/list/chars.ex b/lib/elixir/lib/list/chars.ex
index 7fa07b016..e9813a123 100644
--- a/lib/elixir/lib/list/chars.ex
+++ b/lib/elixir/lib/list/chars.ex
@@ -17,7 +17,6 @@ defprotocol List.Chars do
   def to_charlist(term)
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use List.Chars.to_charlist/1 instead"
   Kernel.def to_char_list(term) do
     __MODULE__.to_charlist(term)
diff --git a/lib/elixir/lib/macro/env.ex b/lib/elixir/lib/macro/env.ex
index e85957818..b99995aa6 100644
--- a/lib/elixir/lib/macro/env.ex
+++ b/lib/elixir/lib/macro/env.ex
@@ -69,8 +69,8 @@ defmodule Macro.Env do
   @typep vars :: [variable]
   @typep var_type :: :term
   @typep var_version :: non_neg_integer
-  @typep unused_vars :: %{{variable, var_version} => non_neg_integer | false}
-  @typep current_vars :: %{variable => {var_version, var_type}}
+  @typep unused_vars :: %{optional({variable, var_version}) => non_neg_integer | false}
+  @typep current_vars :: %{optional(variable) => {var_version, var_type}}
   @typep prematch_vars :: current_vars | :warn | :raise | :pin | :apply
   @typep contextual_vars :: [atom]
 
diff --git a/lib/elixir/lib/map.ex b/lib/elixir/lib/map.ex
index d361d70cd..f42036348 100644
--- a/lib/elixir/lib/map.ex
+++ b/lib/elixir/lib/map.ex
@@ -409,8 +409,9 @@ defmodule Map do
   Gets the value for a specific `key` in `map`.
 
   If `key` is present in `map` with value `value`, then `value` is
-  returned. Otherwise, `default` is returned (which is `nil` unless
-  specified otherwise).
+  returned. Otherwise, `default` is returned.
+
+  If `default` is not provided, `nil` is used.
 
   ## Examples
 
@@ -892,7 +893,6 @@ defmodule Map do
   def equal?(term, other), do: :erlang.error({:badmap, term}, [term, other])
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Kernel.map_size/1 instead"
   def size(map) do
     map_size(map)
diff --git a/lib/elixir/lib/map_set.ex b/lib/elixir/lib/map_set.ex
index d0d906dd9..946868d5f 100644
--- a/lib/elixir/lib/map_set.ex
+++ b/lib/elixir/lib/map_set.ex
@@ -41,7 +41,7 @@ defmodule MapSet do
   @opaque t(value) :: %__MODULE__{map: %{optional(value) => []}}
   @type t :: t(term)
 
-  # TODO: Remove version key on Elixir 2.0
+  # TODO: Remove version key on v2.0
   defstruct map: %{}, version: 2
 
   @doc """
diff --git a/lib/elixir/lib/module.ex b/lib/elixir/lib/module.ex
index a5eac78f7..08a0f849f 100644
--- a/lib/elixir/lib/module.ex
+++ b/lib/elixir/lib/module.ex
@@ -174,7 +174,7 @@ defmodule Module do
 
   Accepts a string (often a heredoc) or `false` where `@doc false` will
   make the entity invisible to documentation extraction tools like
-  ExDoc. For example:
+  [`ExDoc`](https://hexdocs.pm/ex_doc/). For example:
 
       defmodule MyModule do
         @typedoc "This type"
@@ -197,7 +197,8 @@ defmodule Module do
 
   As can be seen in the example above, `@doc` and `@typedoc` also accept
   a keyword list that serves as a way to provide arbitrary metadata
-  about the entity. Tools like ExDoc and IEx may use this information to
+  about the entity. Tools like [`ExDoc`](https://hexdocs.pm/ex_doc/)
+  and `IEx` may use this information to
   display annotations. A common use case is `since` that may be used
   to annotate in which version the function was introduced.
 
@@ -211,6 +212,9 @@ defmodule Module do
   there are a few reserved keys that will be ignored and warned if used.
   Currently these are: `:opaque` and `:defaults`.
 
+  Once this module is compiled, this information becomes available via
+  the `Code.fetch_docs/1` function.
+
   ### `@dialyzer`
 
   Defines warnings to request or suppress when using a version of
@@ -269,12 +273,15 @@ defmodule Module do
 
   Accepts a string (often a heredoc) or `false` where `@moduledoc false`
   will make the module invisible to documentation extraction tools like
-  ExDoc.
+  [`ExDoc`](https://hexdocs.pm/ex_doc/).
 
   Similarly to `@doc` also accepts a keyword list to provide metadata
   about the module. For more details, see the documentation of `@doc`
   above.
 
+  Once this module is compiled, this information becomes available via
+  the `Code.fetch_docs/1` function.
+
   ### `@on_definition`
 
   A hook that will be invoked when each function or macro in the current
@@ -493,27 +500,37 @@ defmodule Module do
   @typep definition :: {atom, arity}
   @typep def_kind :: :def | :defp | :defmacro | :defmacrop
 
+  @extra_error_msg_defines? "Use Kernel.function_exported?/3 and Kernel.macro_exported?/3 " <>
+                              "to check for public functions and macros instead"
+
+  @extra_error_msg_definitions_in "Use the Module.__info__/1 callback to get public functions and macros instead"
+
   @doc """
-  Provides runtime information about functions and macros defined by the
-  module, etc.
+  Provides runtime information about functions, macros, and other information
+  defined by the module.
 
   Each module gets an `__info__/1` function when it's compiled. The function
-  takes one of the following atoms:
+  takes one of the following items:
 
-    * `:functions` - keyword list of public functions along with their arities
+    * `:attributes` - a keyword list with all persisted attributes
 
-    * `:macros` - keyword list of public macros along with their arities
+    * `:compile` - a list with compiler metadata
 
-    * `:module` - the module atom name
+    * `:functions` - a keyword list of public functions and their arities
 
-    * `:md5` - the MD5 of the module
+    * `:macros` - a keyword list of public macros and their arities
 
-    * `:compile` - a list with compiler metadata
+    * `:md5` - the MD5 of the module
 
-    * `:attributes` - a list with all persisted attributes
+    * `:module` - the module atom name
 
   """
-  @callback __info__(:functions | :macros | :module | :md5 | :compile | :attributes) :: term()
+  @callback __info__(:attributes) :: keyword()
+  @callback __info__(:compile) :: [term()]
+  @callback __info__(:functions) :: keyword()
+  @callback __info__(:macros) :: keyword()
+  @callback __info__(:md5) :: binary()
+  @callback __info__(:module) :: module()
 
   @doc """
   Checks if a module is open.
@@ -584,7 +601,7 @@ defmodule Module do
 
   def eval_quoted(module, quoted, binding, opts)
       when is_atom(module) and is_list(binding) and is_list(opts) do
-    assert_not_compiled!(:eval_quoted, module)
+    assert_not_compiled!(__ENV__.function, module)
     :elixir_def.reset_last(module)
 
     {value, binding, _env, _scope} =
@@ -900,7 +917,8 @@ defmodule Module do
   Use `defines?/3` to assert for a specific type.
 
   This function can only be used on modules that have not yet been compiled.
-  Use `Kernel.function_exported?/3` to check compiled modules.
+  Use `Kernel.function_exported?/3` and `Kernel.macro_exported?/3` to check for
+  public functions and macros respectively in compiled modules.
 
   ## Examples
 
@@ -914,7 +932,7 @@ defmodule Module do
   @spec defines?(module, definition) :: boolean
   def defines?(module, {name, arity} = tuple)
       when is_atom(module) and is_atom(name) and is_integer(arity) and arity >= 0 and arity <= 255 do
-    assert_not_compiled!(:defines?, module)
+    assert_not_compiled!(__ENV__.function, module, @extra_error_msg_defines?)
     {set, _bag} = data_tables_for(module)
     :ets.member(set, {:def, tuple})
   end
@@ -926,7 +944,8 @@ defmodule Module do
   `kind` can be any of `:def`, `:defp`, `:defmacro`, or `:defmacrop`.
 
   This function can only be used on modules that have not yet been compiled.
-  Use `Kernel.function_exported?/3` to check compiled modules.
+  Use `Kernel.function_exported?/3` and `Kernel.macro_exported?/3` to check for
+  public functions and macros respectively in compiled modules.
 
   ## Examples
 
@@ -941,7 +960,8 @@ defmodule Module do
   def defines?(module, {name, arity} = tuple, def_kind)
       when is_atom(module) and is_atom(name) and is_integer(arity) and arity >= 0 and arity <= 255 and
              def_kind in [:def, :defp, :defmacro, :defmacrop] do
-    assert_not_compiled!(:defines?, module)
+    assert_not_compiled!(__ENV__.function, module, @extra_error_msg_defines?)
+
     {set, _bag} = data_tables_for(module)
 
     case :ets.lookup(set, {:def, tuple}) do
@@ -962,9 +982,11 @@ defmodule Module do
   end
 
   @doc """
-  Converts the given spec to a callback.
+  Copies the given spec as a callback.
 
-  Returns `true` if there is such a spec and it was converted to a callback.
+  Returns `true` if there is such a spec and it was copied as a callback.
+  If the function associated to the spec has documentation defined prior to
+  invoking this function, the docs are copied too.
   """
   @doc since: "1.7.0"
   @spec spec_to_callback(module, definition) :: boolean
@@ -973,19 +995,27 @@ defmodule Module do
   end
 
   @doc """
-  Returns all functions defined in `module`.
+  Returns all functions and macros defined in `module`.
+
+  It returns a list with all defined functions and macros, public and private,
+  in the shape of `[{name, arity}, ...]`.
+
+  This function can only be used on modules that have not yet been compiled.
+  Use the `c:Module.__info__/1` callback to get the public functions and macros in
+  compiled modules.
 
   ## Examples
 
       defmodule Example do
         def version, do: 1
-        Module.definitions_in(__MODULE__) #=> [{:version, 0}]
+        defmacrop test(arg), do: arg
+        Module.definitions_in(__MODULE__) #=> [{:version, 0}, {:test, 1}]
       end
 
   """
   @spec definitions_in(module) :: [definition]
   def definitions_in(module) when is_atom(module) do
-    assert_not_compiled!(:definitions_in, module)
+    assert_not_compiled!(__ENV__.function, module, @extra_error_msg_definitions_in)
     {_, bag} = data_tables_for(module)
     bag_lookup_element(bag, :defs, 2)
   end
@@ -994,6 +1024,10 @@ defmodule Module do
   Returns all functions defined in `module`, according
   to its kind.
 
+  This function can only be used on modules that have not yet been compiled.
+  Use the `c:Module.__info__/1` callback to get the public functions and macros in
+  compiled modules.
+
   ## Examples
 
       defmodule Example do
@@ -1006,7 +1040,7 @@ defmodule Module do
   @spec definitions_in(module, def_kind) :: [definition]
   def definitions_in(module, def_kind)
       when is_atom(module) and def_kind in [:def, :defp, :defmacro, :defmacrop] do
-    assert_not_compiled!(:definitions_in, module)
+    assert_not_compiled!(__ENV__.function, module, @extra_error_msg_definitions_in)
     {set, _} = data_tables_for(module)
     :lists.concat(:ets.match(set, {{:def, :"$1"}, def_kind, :_, :_, :_, :_}))
   end
@@ -1020,7 +1054,7 @@ defmodule Module do
   """
   @spec make_overridable(module, [definition]) :: :ok
   def make_overridable(module, tuples) when is_atom(module) and is_list(tuples) do
-    assert_not_compiled!(:make_overridable, module)
+    assert_not_compiled!(__ENV__.function, module)
 
     func = fn
       {function_name, arity} = tuple
@@ -1164,6 +1198,11 @@ defmodule Module do
 
       Module.get_attribute(__MODULE__, :foo)
 
+  This function can only be used on modules that have not yet been compiled.
+  Use the `c:Module.__info__/1` callback to get all persisted attributes, or
+  `Code.fetch_docs/1` to retrieve all documentation related attributes in
+  compiled modules.
+
   ## Examples
 
       defmodule Foo do
@@ -1196,7 +1235,7 @@ defmodule Module do
   """
   @spec delete_attribute(module, atom) :: term
   def delete_attribute(module, key) when is_atom(module) and is_atom(key) do
-    assert_not_compiled!(:delete_attribute, module)
+    assert_not_compiled!(__ENV__.function, module)
     {set, bag} = data_tables_for(module)
 
     case :ets.lookup(set, key) do
@@ -1248,7 +1287,7 @@ defmodule Module do
   @spec register_attribute(module, atom, [{:accumulate, boolean}, {:persist, boolean}]) :: :ok
   def register_attribute(module, attribute, options)
       when is_atom(module) and is_atom(attribute) and is_list(options) do
-    assert_not_compiled!(:register_attribute, module)
+    assert_not_compiled!(__ENV__.function, module)
     {set, bag} = data_tables_for(module)
 
     if Keyword.get(options, :persist) do
@@ -1300,10 +1339,9 @@ defmodule Module do
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use @doc instead"
   def add_doc(module, line, kind, {name, arity}, signature \\ [], doc) do
-    assert_not_compiled!(:add_doc, module)
+    assert_not_compiled!(__ENV__.function, module)
 
     if kind in [:defp, :defmacrop, :typep] do
       if doc, do: {:error, :private_doc}, else: :ok
@@ -1338,7 +1376,7 @@ defmodule Module do
         "#{kind} #{name}/#{arity} is private, " <>
           "@doc attribute is always discarded for private functions/macros/types"
 
-      :elixir_errors.warn(line, env.file, message)
+      IO.warn(message, Macro.Env.stacktrace(%{env | line: line}))
     end
   end
 
@@ -1438,7 +1476,7 @@ defmodule Module do
 
     pending_callbacks =
       if impls != [] do
-        {non_implemented_callbacks, contexts} = check_impls(behaviours, callbacks, impls)
+        {non_implemented_callbacks, contexts} = check_impls(env, behaviours, callbacks, impls)
         warn_missing_impls(env, non_implemented_callbacks, contexts, all_definitions)
         non_implemented_callbacks
       else
@@ -1456,23 +1494,21 @@ defmodule Module do
           message =
             "@behaviour #{inspect(behaviour)} must be an atom (in module #{inspect(env.module)})"
 
-          :elixir_errors.warn(env.line, env.file, message)
+          IO.warn(message, Macro.Env.stacktrace(env))
           acc
 
         not Code.ensure_compiled?(behaviour) ->
           message =
             "@behaviour #{inspect(behaviour)} does not exist (in module #{inspect(env.module)})"
 
-          :elixir_errors.warn(env.line, env.file, message)
-
+          IO.warn(message, Macro.Env.stacktrace(env))
           acc
 
         not function_exported?(behaviour, :behaviour_info, 1) ->
           message =
             "module #{inspect(behaviour)} is not a behaviour (in module #{inspect(env.module)})"
 
-          :elixir_errors.warn(env.line, env.file, message)
-
+          IO.warn(message, Macro.Env.stacktrace(env))
           acc
 
         true ->
@@ -1493,7 +1529,7 @@ defmodule Module do
           "conflicting behaviours found. #{format_definition(kind, callback)} is required by " <>
             "#{inspect(conflict)} and #{inspect(behaviour)} (in module #{inspect(env.module)})"
 
-        :elixir_errors.warn(env.line, env.file, message)
+        IO.warn(message, Macro.Env.stacktrace(env))
 
       %{} ->
         :ok
@@ -1510,7 +1546,7 @@ defmodule Module do
             format_callback(callback, kind, behaviour) <>
               " is not implemented (in module #{inspect(env.module)})"
 
-          :elixir_errors.warn(env.line, env.file, message)
+          IO.warn(message, Macro.Env.stacktrace(env))
 
         {_, wrong_kind, _, _} when kind != wrong_kind ->
           message =
@@ -1518,7 +1554,7 @@ defmodule Module do
               " was implemented as \"#{wrong_kind}\" but should have been \"#{kind}\" " <>
               "(in module #{inspect(env.module)})"
 
-          :elixir_errors.warn(env.line, env.file, message)
+          IO.warn(message, Macro.Env.stacktrace(env))
 
         _ ->
           :ok
@@ -1540,7 +1576,7 @@ defmodule Module do
       module.__protocol__(:module) == module
   end
 
-  defp check_impls(behaviours, callbacks, impls) do
+  defp check_impls(env, behaviours, callbacks, impls) do
     acc = {callbacks, %{}}
 
     Enum.reduce(impls, acc, fn {fa, context, defaults, kind, line, file, value}, acc ->
@@ -1553,7 +1589,8 @@ defmodule Module do
           end)
 
         {:error, message} ->
-          :elixir_errors.warn(line, file, format_impl_warning(fa, kind, message))
+          formatted = format_impl_warning(fa, kind, message)
+          IO.warn(formatted, Macro.Env.stacktrace(%{env | line: line, file: file}))
           acc
       end
     end)
@@ -1669,7 +1706,7 @@ defmodule Module do
             "This either means you forgot to add the \"@impl true\" annotation before the " <>
             "definition or that you are accidentally overriding this callback"
 
-        :elixir_errors.warn(:elixir_utils.get_line(meta), env.file, message)
+        IO.warn(message, Macro.Env.stacktrace(%{env | line: :elixir_utils.get_line(meta)}))
       end
     end
 
@@ -1710,7 +1747,12 @@ defmodule Module do
   # Used internally by Kernel's @.
   # This function is private and must be used only internally.
   def get_attribute(module, key, line) when is_atom(key) do
-    assert_not_compiled!(:get_attribute, module)
+    assert_not_compiled!(
+      {:get_attribute, 2},
+      module,
+      "Use the Module.__info__/1 callback or Code.fetch_docs/1 instead"
+    )
+
     {set, bag} = data_tables_for(module)
 
     case :ets.lookup(set, key) do
@@ -1742,7 +1784,7 @@ defmodule Module do
   # Used internally by Kernel's @.
   # This function is private and must be used only internally.
   def put_attribute(module, key, value, line) when is_atom(key) do
-    assert_not_compiled!(:put_attribute, module)
+    assert_not_compiled!(__ENV__.function, module)
     {set, bag} = data_tables_for(module)
     value = preprocess_attribute(key, value)
     put_attribute(module, key, value, line, set, bag)
@@ -1981,9 +2023,19 @@ defmodule Module do
     :error, :badarg -> []
   end
 
-  defp assert_not_compiled!(fun, module) do
+  defp assert_not_compiled!(function_name_arity, module, extra_msg \\ "") do
     open?(module) ||
       raise ArgumentError,
-            "could not call #{fun} with argument #{inspect(module)} because the module is already compiled"
+            assert_not_compiled_message(function_name_arity, module, extra_msg)
+  end
+
+  defp assert_not_compiled_message({function_name, arity}, module, extra_msg) do
+    mfa = "Module.#{function_name}/#{arity}"
+
+    "could not call #{mfa} because the module #{inspect(module)} is already compiled" <>
+      case extra_msg do
+        "" -> ""
+        _ -> ". " <> extra_msg
+      end
   end
 end
diff --git a/lib/elixir/lib/option_parser.ex b/lib/elixir/lib/option_parser.ex
index 615acf2e6..1f6f3d8c2 100644
--- a/lib/elixir/lib/option_parser.ex
+++ b/lib/elixir/lib/option_parser.ex
@@ -77,9 +77,11 @@ defmodule OptionParser do
 
   Switches can be specified via one of two options:
 
-    * `:strict` - defines strict switches. Any switch in `argv` that is not
-      specified in the list is returned in the invalid options list.
-    * `:switches` - defines some switches and their types. This function
+    * `:strict` - defines strict switches and their types. Any switch
+      in `argv` that is not specified in the list is returned in the
+      invalid options list. This is the preferred way to parse options.
+
+    * `:switches` - defines switches and their types. This function
       still attempts to parse switches that are not in this list.
 
   Both these options accept a keyword list where the key is an atom
@@ -157,16 +159,17 @@ defmodule OptionParser do
       # The :option_parser_example atom is not used anywhere below
 
   However, the code below would work as long as `:option_parser_example` atom is
-  used at some point later (or earlier) **in the same module**:
+  used at some point later (or earlier) **in the same module**. For example:
 
       {opts, _, _} = OptionParser.parse(["--option-parser-example"], switches: [debug: :boolean])
+      # ... then somewhere in the same module you access it ...
       opts[:option_parser_example]
 
-  In other words, Elixir will do the correct thing and only parse options that are
-  used by the runtime, ignoring all others. If you would like to parse all switches,
-  regardless if they exist or not, you can force creation of atoms by passing
-  `allow_nonexistent_atoms: true` as option. Use this option with care. It is only
-  useful when you are building command-line applications that receive
+  In other words, Elixir will only parse options that are used by the runtime,
+  ignoring all others. If you would like to parse all switches, regardless if
+  they exist or not, you can force creation of atoms by passing
+  `allow_nonexistent_atoms: true` as option. Use this option with care. It is
+  only useful when you are building command-line applications that receive
   dynamically-named arguments and must be avoided in long-running systems.
 
   ## Aliases
@@ -443,7 +446,6 @@ defmodule OptionParser do
         option_key = config.aliases[key]
 
         if key && option_key do
-          # TODO: Remove this in Elixir v2.0
           IO.warn("multi-letter aliases are deprecated, got: #{inspect(key)}")
           next_tagged({:default, option_key}, value, original, rest, config)
         else
@@ -596,7 +598,6 @@ defmodule OptionParser do
           {strict, true}
 
         true ->
-          # TODO: Remove this in Elixir v2.0
           IO.warn("not passing the :switches or :strict option to OptionParser is deprecated")
           {[], false}
       end
diff --git a/lib/elixir/lib/port.ex b/lib/elixir/lib/port.ex
index c664d6fd4..96c5e3e28 100644
--- a/lib/elixir/lib/port.ex
+++ b/lib/elixir/lib/port.ex
@@ -89,7 +89,7 @@ defmodule Port do
       {#Port<0.1444>, {:data, "hello\n"}}
 
   `:spawn` will retrieve the program name from the argument and traverse your
-  OS `$PATH` environment variable looking for a matching program.
+  operating system `$PATH` environment variable looking for a matching program.
 
   Although the above is handy, it means it is impossible to invoke an executable
   that has whitespaces on its name or in any of its arguments. For those reasons,
@@ -117,7 +117,7 @@ defmodule Port do
   reimplementing core part of the Runtime System, such as the `:user` and
   `:shell` processes.
 
-  ## Zombie OS processes
+  ## Zombie operating system processes
 
   A port can be closed via the `close/1` function or by sending a `{pid, :close}`
   message. However, if the VM crashes, a long-running program started by the port
@@ -261,7 +261,7 @@ defmodule Port do
   where:
 
     * `ref` is a monitor reference returned by this function;
-    * `object` is either the `port` being monitored (when monitoring by port id)
+    * `object` is either the `port` being monitored (when monitoring by port ID)
     or `{name, node}` (when monitoring by a port name);
     * `reason` is the exit reason.
 
diff --git a/lib/elixir/lib/protocol.ex b/lib/elixir/lib/protocol.ex
index fd750116d..178c6e4ee 100644
--- a/lib/elixir/lib/protocol.ex
+++ b/lib/elixir/lib/protocol.ex
@@ -42,7 +42,7 @@ defmodule Protocol do
         impl_for!(term).unquote(name)(unquote_splicing(call_args))
       end
 
-      # Convert the spec to callback if possible,
+      # Copy spec as callback if possible,
       # otherwise generate a dummy callback
       Module.spec_to_callback(__MODULE__, {name, arity}) ||
         @callback unquote(name)(unquote_splicing(type_args)) :: term
@@ -676,7 +676,7 @@ defmodule Protocol do
           "implement protocols after compilation or during tests, check the " <>
           "\"Consolidation\" section in the documentation for Kernel.defprotocol/2"
 
-      :elixir_errors.warn(env.line, env.file, message)
+      IO.warn(message, Macro.Env.stacktrace(env))
     end
 
     :ok
diff --git a/lib/elixir/lib/range.ex b/lib/elixir/lib/range.ex
index 79c8eb442..3f95c957e 100644
--- a/lib/elixir/lib/range.ex
+++ b/lib/elixir/lib/range.ex
@@ -78,7 +78,7 @@ defmodule Range do
   """
   @doc since: "1.8.0"
   @spec disjoint?(t, t) :: boolean
-  def disjoint?(first1..last1, first2..last2) do
+  def disjoint?(first1..last1 = _range1, first2..last2 = _range2) do
     {first1, last1} = normalize(first1, last1)
     {first2, last2} = normalize(first2, last2)
     last2 < first1 or last1 < first2
@@ -88,7 +88,6 @@ defmodule Range do
   defp normalize(first, last) when first > last, do: {last, first}
   defp normalize(first, last), do: {first, last}
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Pattern match on first..last instead"
   def range?(term)
diff --git a/lib/elixir/lib/regex.ex b/lib/elixir/lib/regex.ex
index 563b2dab9..6b9310266 100644
--- a/lib/elixir/lib/regex.ex
+++ b/lib/elixir/lib/regex.ex
@@ -786,7 +786,6 @@ defmodule Regex do
 
   defp translate_options(<<?m, t::binary>>, acc), do: translate_options(t, [:multiline | acc])
 
-  # TODO: Remove on 2.0
   defp translate_options(<<?r, t::binary>>, acc) do
     IO.warn("the /r modifier in regular expressions is deprecated, please use /U instead")
     translate_options(t, [:ungreedy | acc])
diff --git a/lib/elixir/lib/registry.ex b/lib/elixir/lib/registry.ex
index 3d23f72f0..7b9f55326 100644
--- a/lib/elixir/lib/registry.ex
+++ b/lib/elixir/lib/registry.ex
@@ -78,14 +78,14 @@ defmodule Registry do
   Now, an entity interested in dispatching events for a given key may call
   `dispatch/3` passing in the key and a callback. This callback will be invoked
   with a list of all the values registered under the requested key, alongside
-  the pid of the process that registered each value, in the form of `{pid,
+  the PID of the process that registered each value, in the form of `{pid,
   value}` tuples. In our example, `value` will be the `{module, function}` tuple
   in the code above:
 
       Registry.dispatch(Registry.DispatcherTest, "hello", fn entries ->
         for {pid, {module, function}} <- entries, do: apply(module, function, [pid])
       end)
-      # Prints #PID<...> where the pid is for the process that called register/3 above
+      # Prints #PID<...> where the PID is for the process that called register/3 above
       #=> :ok
 
   Dispatching happens in the process that calls `dispatch/3` either serially or
@@ -161,7 +161,7 @@ defmodule Registry do
   in the function documentation.
 
   However, keep in mind those cases are typically not an issue. After all, a
-  process referenced by a pid may crash at any time, including between getting
+  process referenced by a PID may crash at any time, including between getting
   the value from the registry and sending it a message. Many parts of the standard
   library are designed to cope with that, such as `Process.monitor/1` which will
   deliver the `:DOWN` message immediately if the monitored process is already dead
@@ -423,8 +423,8 @@ defmodule Registry do
   for the given `registry`.
 
   The list of `entries` is a non-empty list of two-element tuples where
-  the first element is the pid and the second element is the value
-  associated to the pid. If there are no entries for the given key,
+  the first element is the PID and the second element is the value
+  associated to the PID. If there are no entries for the given key,
   the callback is never invoked.
 
   If the registry is partitioned, the callback is invoked multiple times
@@ -868,11 +868,11 @@ defmodule Registry do
   lookup.
 
   This function returns `{:ok, owner}` or `{:error, reason}`.
-  The `owner` is the pid in the registry partition responsible for
-  the pid. The owner is automatically linked to the caller.
+  The `owner` is the PID in the registry partition responsible for
+  the PID. The owner is automatically linked to the caller.
 
   If the registry has unique keys, it will return `{:ok, owner}` unless
-  the key is already associated to a pid, in which case it returns
+  the key is already associated to a PID, in which case it returns
   `{:error, {:already_registered, pid}}`.
 
   If the registry has duplicate keys, multiple registrations from the
@@ -1220,12 +1220,12 @@ defmodule Registry.Supervisor do
   end
 
   # Unique registries have their key partition hashed by key.
-  # This means that, if a pid partition crashes, it may have
+  # This means that, if a PID partition crashes, it may have
   # entries from all key partitions, so we need to crash all.
   defp strategy_for_kind(:unique), do: :one_for_all
 
   # Duplicate registries have both key and pid partitions hashed
-  # by pid. This means that, if a pid partition crashes, all of
+  # by pid. This means that, if a PID partition crashes, all of
   # its associated entries are in its sibling table, so we crash one.
   defp strategy_for_kind(:duplicate), do: :one_for_one
 end
diff --git a/lib/elixir/lib/set.ex b/lib/elixir/lib/set.ex
index e932847b7..b3c5f3c48 100644
--- a/lib/elixir/lib/set.ex
+++ b/lib/elixir/lib/set.ex
@@ -11,7 +11,6 @@ defmodule Set do
   @type values :: [value]
   @type t :: map
 
-  # TODO: Remove by 2.0
   message = "Use the MapSet module for working with sets"
 
   defmacrop target(set) do
diff --git a/lib/elixir/lib/stream.ex b/lib/elixir/lib/stream.ex
index fd3092996..29542449b 100644
--- a/lib/elixir/lib/stream.ex
+++ b/lib/elixir/lib/stream.ex
@@ -125,19 +125,16 @@ defmodule Stream do
 
   ## Transformers
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Stream.chunk_every/2 instead"
   def chunk(enum, n), do: chunk(enum, n, n, nil)
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Stream.chunk_every/3 instead"
   def chunk(enum, n, step) do
     chunk_every(enum, n, step, nil)
   end
 
-  # TODO: Remove by 2.0
   @doc false
   @deprecated "Use Stream.chunk_every/4 instead"
   def chunk(enum, n, step, leftover)
@@ -333,7 +330,7 @@ defmodule Stream do
       [1, 2, 3, 4, 5]
 
   """
-  @spec drop(Enumerable.t(), non_neg_integer) :: Enumerable.t()
+  @spec drop(Enumerable.t(), integer) :: Enumerable.t()
   def drop(enum, n) when is_integer(n) and n >= 0 do
     lazy(enum, n, fn f1 -> R.drop(f1) end)
   end
@@ -477,7 +474,6 @@ defmodule Stream do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Stream.filter/2 + Stream.map/2 instead"
   def filter_map(enum, filter, mapper) do
     lazy(enum, fn f1 -> R.filter_map(filter, mapper, f1) end)
@@ -993,7 +989,6 @@ defmodule Stream do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use Stream.uniq_by/2 instead"
   def uniq(enum, fun) do
     uniq_by(enum, fun)
diff --git a/lib/elixir/lib/string.ex b/lib/elixir/lib/string.ex
index 16adf7ac1..76be1543f 100644
--- a/lib/elixir/lib/string.ex
+++ b/lib/elixir/lib/string.ex
@@ -798,12 +798,10 @@ defmodule String do
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.trim_trailing/1 instead"
   defdelegate rstrip(binary), to: String.Break, as: :trim_trailing
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.trim_trailing/2 with a binary as second argument instead"
   def rstrip(string, char) when is_integer(char) do
     replace_trailing(string, <<char::utf8>>, "")
@@ -1013,26 +1011,22 @@ defmodule String do
   defp append_unless_empty(prefix, suffix), do: prefix <> suffix
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.trim_leading/1 instead"
   defdelegate lstrip(binary), to: String.Break, as: :trim_leading
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.trim_leading/2 with a binary as second argument instead"
   def lstrip(string, char) when is_integer(char) do
     replace_leading(string, <<char::utf8>>, "")
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.trim/1 instead"
   def strip(string) do
     trim(string)
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.trim/2 with a binary second argument instead"
   def strip(string, char) do
     trim(string, <<char::utf8>>)
@@ -1257,28 +1251,24 @@ defmodule String do
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.pad_leading/2 instead"
   def rjust(subject, len) do
     rjust(subject, len, ?\s)
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.pad_leading/3 with a binary padding instead"
   def rjust(subject, len, pad) when is_integer(pad) and is_integer(len) and len >= 0 do
     pad(:leading, subject, len, [<<pad::utf8>>])
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.pad_trailing/2 instead"
   def ljust(subject, len) do
     ljust(subject, len, ?\s)
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.pad_trailing/3 with a binary padding instead"
   def ljust(subject, len, pad) when is_integer(pad) and is_integer(len) and len >= 0 do
     pad(:trailing, subject, len, [<<pad::utf8>>])
@@ -1561,7 +1551,6 @@ defmodule String do
   def valid?(_), do: false
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Use String.valid?/1 instead"
   def valid_character?(string) do
     case string do
@@ -2466,7 +2455,6 @@ defmodule String do
   end
 
   @doc false
-  # TODO: Remove by 2.0
   @deprecated "Use String.to_charlist/1 instead"
   @spec to_char_list(t) :: charlist
   def to_char_list(string), do: String.to_charlist(string)
diff --git a/lib/elixir/lib/supervisor.ex b/lib/elixir/lib/supervisor.ex
index c320ca23d..c5da11d40 100644
--- a/lib/elixir/lib/supervisor.ex
+++ b/lib/elixir/lib/supervisor.ex
@@ -602,7 +602,7 @@ defmodule Supervisor do
   description of the available strategies.
   """
   @doc since: "1.5.0"
-  # TODO: Warn if simple_one_for_one strategy is used on Elixir v1.9
+  # TODO: Warn if simple_one_for_one strategy is used on Elixir v1.10
   @spec init([:supervisor.child_spec() | {module, term} | module], [init_option]) :: {:ok, tuple}
   def init(children, options) when is_list(children) and is_list(options) do
     unless strategy = options[:strategy] do
@@ -796,7 +796,7 @@ defmodule Supervisor do
   `child_spec` should be a valid child specification. The child process will
   be started as defined in the child specification.
 
-  If a child specification with the specified id already exists, `child_spec` is
+  If a child specification with the specified ID already exists, `child_spec` is
   discarded and this function returns an error with `:already_started` or
   `:already_present` if the corresponding child process is running or not,
   respectively.
@@ -820,7 +820,7 @@ defmodule Supervisor do
     call(supervisor, {:start_child, child_spec})
   end
 
-  # TODO: Deprecate this on Elixir v1.9. Remove and update typespec on v2.0.
+  # TODO: Deprecate this clause on Elixir v1.10
   def start_child(supervisor, args) when is_list(args) do
     call(supervisor, {:start_child, args})
   end
@@ -830,7 +830,7 @@ defmodule Supervisor do
   end
 
   @doc """
-  Terminates the given child identified by child id.
+  Terminates the given child identified by `child_id`.
 
   The process is terminated, if there's one. The child specification is
   kept unless the child is temporary.
@@ -840,14 +840,14 @@ defmodule Supervisor do
   Use `delete_child/2` to remove the child specification.
 
   If successful, this function returns `:ok`. If there is no child
-  specification for the given child id, this function returns
+  specification for the given child ID, this function returns
   `{:error, :not_found}`.
   """
   @spec terminate_child(supervisor, term()) :: :ok | {:error, error}
         when error: :not_found | :simple_one_for_one
   def terminate_child(supervisor, child_id)
 
-  # TODO: Deprecate this clause on Elixir v1.9
+  # TODO: Deprecate this clause on Elixir v1.10
   def terminate_child(supervisor, pid) when is_pid(pid) do
     call(supervisor, {:terminate_child, pid})
   end
diff --git a/lib/elixir/lib/supervisor/spec.ex b/lib/elixir/lib/supervisor/spec.ex
index 42678206a..60d9dc4f9 100644
--- a/lib/elixir/lib/supervisor/spec.ex
+++ b/lib/elixir/lib/supervisor/spec.ex
@@ -127,7 +127,7 @@ defmodule Supervisor.Spec do
   @typedoc "Supported module values"
   @type modules :: :dynamic | [module]
 
-  @typedoc "Supported id values"
+  @typedoc "Supported ID values"
   @type child_id :: term
 
   @typedoc "The supervisor specification"
@@ -196,7 +196,7 @@ defmodule Supervisor.Spec do
   defp assert_unique_ids([id | rest]) do
     if id in rest do
       raise ArgumentError,
-            "duplicated id #{inspect(id)} found in the supervisor specification, " <>
+            "duplicated ID #{inspect(id)} found in the supervisor specification, " <>
               "please explicitly pass the :id option when defining this worker/supervisor"
     else
       assert_unique_ids(rest)
diff --git a/lib/elixir/lib/system.ex b/lib/elixir/lib/system.ex
index 0c4b01648..79e92ad97 100644
--- a/lib/elixir/lib/system.ex
+++ b/lib/elixir/lib/system.ex
@@ -36,11 +36,11 @@ defmodule System do
 
   Generally speaking, the VM provides three time measurements:
 
-    * `os_time/0` - the time reported by the OS. This time may be
+    * `os_time/0` - the time reported by the operating system (OS). This time may be
       adjusted forwards or backwards in time with no limitation;
 
-    * `system_time/0` - the VM view of the `os_time/0`. The system time and OS
-      time may not match in case of time warps although the VM works towards
+    * `system_time/0` - the VM view of the `os_time/0`. The system time and operating
+      system time may not match in case of time warps although the VM works towards
       aligning them. This time is not monotonic (i.e., it may decrease)
       as its behaviour is configured [by the VM time warp
       mode](http://www.erlang.org/doc/apps/erts/time_correction.html#Time_Warp_Modes);
@@ -49,7 +49,7 @@ defmodule System do
       by the Erlang VM.
 
   The time functions in this module work in the `:native` unit
-  (unless specified otherwise), which is OS dependent. Most of
+  (unless specified otherwise), which is operating system dependent. Most of
   the time, all calculations are done in the `:native` unit, to
   avoid loss of precision, with `convert_time_unit/3` being
   invoked at the end to convert to a specific time unit like
@@ -129,8 +129,21 @@ defmodule System do
   defp revision, do: get_revision()
 
   # Get the date at compilation time.
+  # Follows https://reproducible-builds.org/specs/source-date-epoch/
   defmacrop get_date do
-    {{year, month, day}, {hour, minute, second}} = :calendar.universal_time()
+    unix_epoch =
+      if source_date_epoch = :os.getenv('SOURCE_DATE_EPOCH') do
+        try do
+          List.to_integer(source_date_epoch)
+        rescue
+          _ -> nil
+        end
+      end
+
+    unix_epoch = unix_epoch || :os.system_time(:second)
+
+    {{year, month, day}, {hour, minute, second}} =
+      :calendar.gregorian_seconds_to_datetime(unix_epoch + 62_167_219_200)
 
     "~4..0b-~2..0b-~2..0bT~2..0b:~2..0b:~2..0bZ"
     |> :io_lib.format([year, month, day, hour, minute, second])
@@ -165,9 +178,34 @@ defmodule System do
   @doc """
   Elixir build information.
 
-  Returns a keyword list with Elixir version, Git short revision hash and compilation date.
+  Returns a map with the Elixir version, short Git revision hash and compilation date.
+
+  Every value in the map is a string, and these are:
+
+    * `:build` - the Elixir version, short Git revision hash and
+      Erlang/OTP release it was compiled with
+    * `:date` - a string representation of the ISO8601 date and time it was built
+    * `:opt_release` - OTP release it was compiled with
+    * `:revision` - short Git revision hash
+    * `:version` - the Elixir version
+
+  One should not rely on the specific formats returned by each of those fields.
+  Instead one should use specialized functions, such as `version/0` to retrieve
+  the Elixir version and `otp_release/0` to retrieve the Erlang/OTP release.
+
+  ## Examples
+
+      iex> System.build_info()
+      %{
+        build: "1.9.0-dev (772a00a0c) (compiled with Erlang/OTP 21)",
+        date: "2018-12-24T01:09:21Z",
+        otp_release: "21",
+        revision: "772a00a0c",
+        version: "1.9.0-dev"
+      }
+
   """
-  @spec build_info() :: map
+  @spec build_info() :: %{required(atom) => String.t()}
   def build_info do
     %{
       build: build(),
@@ -215,7 +253,6 @@ defmodule System do
   Returns the current working directory or `nil` if one
   is not available.
   """
-  # TODO: Remove by 2.0
   @deprecated "Use File.cwd/0 instead"
   @spec cwd() :: String.t() | nil
   def cwd do
@@ -230,7 +267,6 @@ defmodule System do
 
   Returns the current working directory or raises `RuntimeError`.
   """
-  # TODO: Remove by 2.0
   @deprecated "Use File.cwd!/0 instead"
   @spec cwd!() :: String.t()
   def cwd! do
@@ -351,7 +387,7 @@ defmodule System do
   This function looks up an executable program given
   its name using the environment variable PATH on Unix
   and Windows. It also considers the proper executable
-  extension for each OS, so for Windows it will try to
+  extension for each operating system, so for Windows it will try to
   lookup files with `.com`, `.cmd` or similar extensions.
   """
   @spec find_executable(binary) :: binary | nil
@@ -458,7 +494,7 @@ defmodule System do
   latest exception. To retrieve the stacktrace of the current process,
   use `Process.info(self(), :current_stacktrace)` instead.
   """
-  # TODO: Fully deprecate it on Elixir v1.9.
+  # TODO: Fully deprecate it on Elixir v1.11 via @deprecated
   # It is currently partially deprecated in elixir_dispatch.erl
   def stacktrace do
     apply(:erlang, :get_stacktrace, [])
@@ -505,6 +541,38 @@ defmodule System do
     :erlang.halt(String.to_charlist(status))
   end
 
+  @doc """
+  Returns the operating system PID for the current Erlang runtime system instance.
+
+  Returns a string containing the (usually) numerical identifier for a process.
+  On UNIX, this is typically the return value of the `getpid()` system call.
+  On Windows, the process ID as returned by the `GetCurrentProcessId()` system
+  call is used.
+
+  ## Examples
+
+      System.pid()
+
+  """
+  @doc since: "1.9.0"
+  def pid do
+    List.to_string(:os.getpid())
+  end
+
+  @doc """
+  Restarts all applications in the Erlang runtime system.
+
+  All applications are taken down smoothly, all code is unloaded, and all ports
+  are closed before the system starts all applications once again.
+
+  ## Examples
+
+      System.restart()
+
+  """
+  @doc since: "1.9.0"
+  defdelegate restart(), to: :init
+
   @doc """
   Carefully stops the Erlang runtime system.
 
@@ -517,8 +585,6 @@ defmodule System do
   Note that on many platforms, only the status codes 0-255 are supported
   by the operating system.
 
-  For more information, see `:init.stop/1`.
-
   ## Examples
 
       System.stop(0)
@@ -793,7 +859,7 @@ defmodule System do
   end
 
   @doc """
-  Returns the current OS time.
+  Returns the current operating system (OS) time.
 
   The result is returned in the `:native` time unit.
 
@@ -808,7 +874,7 @@ defmodule System do
   end
 
   @doc """
-  Returns the current OS time in the given time `unit`.
+  Returns the current operating system (OS) time in the given time `unit`.
 
   This time may be adjusted forwards or backwards in time
   with no limitation and is not monotonic.
diff --git a/lib/elixir/lib/task.ex b/lib/elixir/lib/task.ex
index 039c71fe5..58b1c6937 100644
--- a/lib/elixir/lib/task.ex
+++ b/lib/elixir/lib/task.ex
@@ -89,7 +89,7 @@ defmodule Task do
 
   Since these tasks are supervised and not directly linked to
   the caller, they cannot be awaited on. `start_link/1`, unlike
-  `async/1`, returns `{:ok, pid}` (which is the result expecte
+  `async/1`, returns `{:ok, pid}` (which is the result expected
   by supervisors).
 
   `use Task` defines a `child_spec/1` function, allowing the
@@ -579,7 +579,6 @@ defmodule Task do
   end
 
   @doc false
-  # TODO: Remove on 2.0
   @deprecated "Pattern match directly on the message instead"
   def find(tasks, {ref, reply}) when is_reference(ref) do
     Enum.find_value(tasks, fn
diff --git a/lib/elixir/lib/task/supervisor.ex b/lib/elixir/lib/task/supervisor.ex
index 1a5411e3f..65ee29c1f 100644
--- a/lib/elixir/lib/task/supervisor.ex
+++ b/lib/elixir/lib/task/supervisor.ex
@@ -78,7 +78,7 @@ defmodule Task.Supervisor do
   give them directly to `start_child` and `async`.
   """
   @spec start_link([option]) :: Supervisor.on_start()
-  # TODO: Deprecate passing restart and shutdown here on Elixir v1.9.
+  # TODO: Deprecate passing restart and shutdown here on Elixir v1.10.
   def start_link(options \\ []) do
     {restart, options} = Keyword.pop(options, :restart, :temporary)
     {shutdown, options} = Keyword.pop(options, :shutdown, 5000)
diff --git a/lib/elixir/lib/tuple.ex b/lib/elixir/lib/tuple.ex
index b104a4061..3efbb0fc6 100644
--- a/lib/elixir/lib/tuple.ex
+++ b/lib/elixir/lib/tuple.ex
@@ -4,9 +4,9 @@ defmodule Tuple do
 
   Please note the following functions for tuples are found in `Kernel`:
 
-    * `elem/2` - access a tuple by index
-    * `put_elem/3` - insert a value into a tuple by index
-    * `tuple_size/1` - get the number of elements in a tuple
+    * `elem/2` - accesses a tuple by index
+    * `put_elem/3` - inserts a value into a tuple by index
+    * `tuple_size/1` - gets the number of elements in a tuple
 
   Tuples are intended as fixed-size containers for multiple elements.
   To manipulate a collection of elements, use a list instead. `Enum`
diff --git a/lib/elixir/lib/uri.ex b/lib/elixir/lib/uri.ex
index cbb3b4905..c7255ea76 100644
--- a/lib/elixir/lib/uri.ex
+++ b/lib/elixir/lib/uri.ex
@@ -128,10 +128,9 @@ defmodule URI do
       %{"percent" => "oh yes!", "starting" => "map"}
 
   """
-  @spec decode_query(binary, %{binary => binary}) :: %{binary => binary}
+  @spec decode_query(binary, %{optional(binary) => binary}) :: %{optional(binary) => binary}
   def decode_query(query, map \\ %{})
 
-  # TODO: Remove on 2.0
   def decode_query(query, %_{} = dict) when is_binary(query) do
     IO.warn("URI.decode_query/2 is deprecated, please use URI.decode_query/1")
     decode_query_into_dict(query, dict)
@@ -141,7 +140,6 @@ defmodule URI do
     decode_query_into_map(query, map)
   end
 
-  # TODO: Remove on 2.0
   def decode_query(query, dict) when is_binary(query) do
     IO.warn("URI.decode_query/2 is deprecated, please use URI.decode_query/1")
     decode_query_into_dict(query, dict)
diff --git a/lib/elixir/pages/Compatibility and Deprecations.md b/lib/elixir/pages/Compatibility and Deprecations.md
index f015a71a3..89a0109e7 100644
--- a/lib/elixir/pages/Compatibility and Deprecations.md	
+++ b/lib/elixir/pages/Compatibility and Deprecations.md	
@@ -51,6 +51,7 @@ Elixir version | Supported Erlang/OTP versions
 1.6            | 19 - 20 (and Erlang/OTP 21 from v1.6.6)
 1.7            | 19 - 21
 1.8            | 20 - 21
+1.9            | 20 - 21
 
 While Elixir often adds compatibility to new Erlang/OTP versions on released branches, such as support for Erlang/OTP 20 in v1.4.5, those releases usually contain the minimum changes for Elixir to run without errors. Only the next minor release, in this case v1.5.0, does effectively leverage the new features provided by the latest Erlang/OTP release.
 
@@ -68,80 +69,84 @@ Elixir deprecations happen in 3 steps:
 
 ### Table of deprecations
 
-Deprecated feature                               | Hard-deprecated in | Replaced by (available since)
-:----------------------------------------------- | :----------------- | :----------------------------
-Passing a non-empty list to `Enum.into/2`        | [v1.8]        | `Kernel.++/2` or `Keyword.merge/2` (v1.0)
-Passing a non-empty list to `:into` in `for`     | [v1.8]        | `Kernel.++/2` or `Keyword.merge/2` (v1.0)
-`:seconds`, `:milliseconds`, etc. as time units  | [v1.8]        | `:second`, `:millisecond`, etc. (v1.4)
-`Inspect.Algebra.surround/3`                     | [v1.8]        | `Inspect.Algebra.concat/2` and `Inspect.Algebra.nest/2` (v1.0)
-`Inspect.Algebra.surround_many/6`                | [v1.8]        | `Inspect.Algebra.container_doc/6` (v1.6)
-`Kernel.ParallelCompiler.files/2`                | [v1.8]        | `Kernel.ParallelCompiler.compile/2` (v1.6)
-`Kernel.ParallelCompiler.files_to_path/2`        | [v1.8]        | `Kernel.ParallelCompiler.compile_to_path/2` (v1.6)
-`Kernel.ParallelRequire.files/2`                 | [v1.8]        | `Kernel.ParallelCompiler.require/2` (v1.6)
-`System.cwd/0` and `System.cwd!/0`               | [v1.8]        | `File.cwd/0` and `File.cwd!/0` (v1.0)
-`mix compile.erlang` returning `{:ok, contents}` or `:error` as the callback in `Mix.Compilers.Erlang.compile/6`| [v1.8] | Return `{:ok, contents, warnings}` or `{:error, errors, warnings}`
-`Code.get_docs/2`                                | [v1.7]        | `Code.fetch_docs/1` (v1.7)
-Calling `super/1` on GenServer callbacks         | [v1.7]        | Not calling `super/1` (v1.0)
-`Enum.chunk/2`[`/3/4`](`Enum.chunk/4`)           | [v1.7]        | `Enum.chunk_every/2`[`/3/4`](`Enum.chunk_every/4`) (v1.5)
-`not left in right`                              | [v1.7]        | [`left not in right`](`Kernel.SpecialForms.in/2`) (v1.5)
-`Registry.start_link/3`                          | [v1.7]        | `Registry.start_link/1` (v1.5)
-`Stream.chunk/2`[`/3/4`](`Stream.chunk/4`)       | [v1.7]        | `Stream.chunk_every/2`[`/3/4`](`Stream.chunk_every/4`) (v1.5)
-`Enum.partition/2`                               | [v1.6]        | `Enum.split_with/2` (v1.4)
-`Keyword.replace/3`                              | [v1.6]        | `Keyword.fetch/2` + `Keyword.put/3` (v1.0)
-`Macro.unescape_tokens/1` and `Macro.unescape_tokens/2` | [v1.6] | Use `Enum.map/2` to traverse over the arguments (v1.0)
-`Module.add_doc/6`                               | [v1.6]        | `@doc` module attribute (v1.0)
-`Map.replace/3`                                  | [v1.6]        | `Map.fetch/2` + `Map.put/3` (v1.0)
-`Range.range?/1`                                 | [v1.6]        | Pattern match on `_.._` (v1.0)
-`Atom.to_char_list/1`                            | [v1.5]        | `Atom.to_charlist/1` (v1.3)
-`Enum.filter_map/3`                              | [v1.5]        | `Enum.filter/2` + `Enum.map/2` or [`for`](`Kernel.SpecialForms.for/1`) comprehensions (v1.0)
-`Float.to_char_list/1`                           | [v1.5]        | `Float.to_charlist/1` (v1.3)
-`GenEvent` module                                | [v1.5]        | `Supervisor` and `GenServer` (v1.0);<br/>[`GenStage`](https://hex.pm/packages/gen_stage) (v1.3);<br/>[`:gen_event`](http://www.erlang.org/doc/man/gen_event.html) (Erlang/OTP 17)
-`Integer.to_char_list/1` and `Integer.to_char_list/2` | [v1.5]   | `Integer.to_charlist/1` and `Integer.to_charlist/2` (v1.3)
-`Kernel.to_char_list/1`                          | [v1.5]        | `Kernel.to_charlist/1` (v1.3)
-`List.Chars.to_char_list/1`                      | [v1.5]        | `List.Chars.to_charlist/1` (v1.3)
-`Stream.filter_map/3`                            | [v1.5]        | `Stream.filter/2` + `Stream.map/2` (v1.0)
-`String.ljust/3` and `String.rjust/3`            | [v1.5]        | Use `String.pad_leading/3` and `String.pad_trailing/3` with a binary padding (v1.3)
-`String.strip/1` and `String.strip/2`            | [v1.5]        | `String.trim/1` and `String.trim/2` (v1.3)
-`String.lstrip/1` and `String.rstrip/1`          | [v1.5]        | `String.trim_leading/1` and `String.trim_trailing/1` (v1.3)
-`String.lstrip/2` and `String.rstrip/2`          | [v1.5]        | Use `String.trim_leading/2` and `String.trim_trailing/2` with a binary as second argument (v1.3)
-`String.to_char_list/1`                          | [v1.5]        | `String.to_charlist/1` (v1.3)
-`()` to mean `nil`                               | [v1.5]        | `nil` (v1.0)
-`char_list/0` type                               | [v1.5]        | `t:charlist/0` type (v1.3)
-`:char_lists` key in `t:Inspect.Opts.t/0` type   | [v1.5]        | `:charlists` key (v1.3)
-`:as_char_lists` value in `t:Inspect.Opts.t/0` type | [v1.5]     | `:as_charlists` value (v1.3)
-`@compile {:parse_transform, _}` in `Module`     | [v1.5]        | *None*
-EEx: `<%=` in middle and end expressions         | [v1.5]        | Use `<%` (`<%=` is allowed only on start expressions) (v1.0)
-`Access.key/1`                                   | [v1.4]        | `Access.key/2` (v1.3)
-`Behaviour` module                               | [v1.4]        | `@callback` module attribute (v1.0)
-`Enum.uniq/2`                                    | [v1.4]        | `Enum.uniq_by/2` (v1.2)
-`Float.to_char_list/2`                           | [v1.4]        | `:erlang.float_to_list/2` (Erlang/OTP 17)
-`Float.to_string/2`                              | [v1.4]        | `:erlang.float_to_binary/2` (Erlang/OTP 17)
-`HashDict` module                                | [v1.4]        | `Map` (v1.2)
-`HashSet` module                                 | [v1.4]        | `MapSet` (v1.1)
- Multi-letter aliases in `OptionParser`          | [v1.4]        | Use single-letter aliases (v1.0)
-`Set` module                                     | [v1.4]        | `MapSet` (v1.1)
-`Stream.uniq/2`                                  | [v1.4]        | `Stream.uniq_by/2` (v1.2)
-`IEx.Helpers.import_file/2`                      | [v1.4]        | `IEx.Helpers.import_file_if_available/1` (v1.3)
-`Mix.Utils.camelize/1`                           | [v1.4]        | `Macro.camelize/1` (v1.2)
-`Mix.Utils.underscore/1`                         | [v1.4]        | `Macro.underscore/1` (v1.2)
-Variable used as function call                   | [v1.4]        | Use parentheses (v1.0)
-Anonymous functions with no expression after `->` | [v1.4]       | Use an expression or explicitly return `nil` (v1.0)
-Support for making private functions overridable | [v1.4]        | Use public functions (v1.0)
-`Dict` module                                    | [v1.3]        | `Keyword` (v1.0) or `Map` (v1.2)
-`Keyword.size/1`                                 | [v1.3]        | `Kernel.length/1` (v1.0)
-`Map.size/1`                                     | [v1.3]        | `Kernel.map_size/1` (v1.0)
-`Set` behaviour                                  | [v1.3]        | `MapSet` data structure (v1.1)
-`String.valid_character?/1`                      | [v1.3]        | `String.valid?/1` (v1.0)
-`Task.find/2`                                    | [v1.3]        | Use direct message matching (v1.0)
-`:append_first` option in `Kernel.defdelegate/2` | [v1.3]        | Define the function explicitly (v1.0)
-`/r` option in `Regex`                           | [v1.3]        | `/U` (v1.1)
-`\x{X*}` inside strings/sigils/charlists         | [v1.3]        | `\uXXXX` or `\u{X*}` (v1.1)
-Map or dictionary as second argument in `Enum.group_by/3` | [v1.3] | `Enum.reduce/3` (v1.0)
-Non-map as second argument in `URI.decode_query/2` | [v1.3]      | Use a map (v1.0)
-`Dict` behaviour                                 | [v1.2]        | `MapSet` data structure (v1.1)
-`Access` protocol                                | [v1.1]        | `Access` behaviour (v1.1)
-`as: true \| false` in `alias/2` and `require/2` | [v1.1]        | *None*
-`?\xHEX`                                         | [v1.1]        | `0xHEX` (v1.0)
+The first column is the version the feature was hard deprecated. The second column shortly describes the deprecated feature and the third column explains the replacement and from which the version the replacement is available from.
+
+Version | Deprecated feature                                  | Replaced by (available since)
+:-------| :-------------------------------------------------- | :---------------------------------------------------------------
+[v1.9]  | `Mix.Project.load_paths/1`                          | `Mix.Project.compile_path/1` (v1.0)
+[v1.9]  | `--detached` in CLI                                 | `--erl "-detached"` (v1.0)
+[v1.8]  | Passing a non-empty list to `Enum.into/2`           | `Kernel.++/2` or `Keyword.merge/2` (v1.0)
+[v1.8]  | Passing a non-empty list to `:into` in `for`        | `Kernel.++/2` or `Keyword.merge/2` (v1.0)
+[v1.8]  | `:seconds`, `:milliseconds`, etc. as time units     | `:second`, `:millisecond`, etc. (v1.4)
+[v1.8]  | `Inspect.Algebra.surround/3`                        | `Inspect.Algebra.concat/2` and `Inspect.Algebra.nest/2` (v1.0)
+[v1.8]  | `Inspect.Algebra.surround_many/6`                   | `Inspect.Algebra.container_doc/6` (v1.6)
+[v1.8]  | `Kernel.ParallelCompiler.files/2`                   | `Kernel.ParallelCompiler.compile/2` (v1.6)
+[v1.8]  | `Kernel.ParallelCompiler.files_to_path/2`           | `Kernel.ParallelCompiler.compile_to_path/2` (v1.6)
+[v1.8]  | `Kernel.ParallelRequire.files/2`                    | `Kernel.ParallelCompiler.require/2` (v1.6)
+[v1.8]  | `System.cwd/0` and `System.cwd!/0`                  | `File.cwd/0` and `File.cwd!/0` (v1.0)
+[v1.8]  | Returning `{:ok, contents}` or `:error` from `Mix.Compilers.Erlang.compile/6`'s callback | Return `{:ok, contents, warnings}` or `{:error, errors, warnings}` (v1.6)
+[v1.7]  | `Code.get_docs/2`                                   | `Code.fetch_docs/1` (v1.7)
+[v1.7]  | Calling `super/1` on GenServer callbacks            | Implenting the behaviour explicitly without calling `super/1` (v1.0)
+[v1.7]  | `Enum.chunk/2`[`/3/4`](`Enum.chunk/4`)              | `Enum.chunk_every/2`[`/3/4`](`Enum.chunk_every/4`) (v1.5)
+[v1.7]  | `not left in right`                                 | [`left not in right`](`Kernel.SpecialForms.in/2`) (v1.5)
+[v1.7]  | `Registry.start_link/3`                             | `Registry.start_link/1` (v1.5)
+[v1.7]  | `Stream.chunk/2`[`/3/4`](`Stream.chunk/4`)          | `Stream.chunk_every/2`[`/3/4`](`Stream.chunk_every/4`) (v1.5)
+[v1.6]  | `Enum.partition/2`                                  | `Enum.split_with/2` (v1.4)
+[v1.6]  | `Keyword.replace/3`                                 | `Keyword.fetch/2` + `Keyword.put/3` (v1.0)
+[v1.6]  | `Macro.unescape_tokens/1/2`                         | Use `Enum.map/2` to traverse over the arguments (v1.0)
+[v1.6]  | `Module.add_doc/6`                                  | `@doc` module attribute (v1.0)
+[v1.6]  | `Map.replace/3`                                     | `Map.fetch/2` + `Map.put/3` (v1.0)
+[v1.6]  | `Range.range?/1`                                    | Pattern match on `_.._` (v1.0)
+[v1.5]  | `Atom.to_char_list/1`                               | `Atom.to_charlist/1` (v1.3)
+[v1.5]  | `Enum.filter_map/3`                                 | `Enum.filter/2` + `Enum.map/2` or [`for`](`Kernel.SpecialForms.for/1`) comprehensions (v1.0)
+[v1.5]  | `Float.to_char_list/1`                              | `Float.to_charlist/1` (v1.3)
+[v1.5]  | `GenEvent` module                                   | `Supervisor` and `GenServer` (v1.0);<br/>[`GenStage`](https://hex.pm/packages/gen_stage) (v1.3);<br/>[`:gen_event`](http://www.erlang.org/doc/man/gen_event.html) (Erlang/OTP 17)
+[v1.5]  | `Integer.to_char_list/1/2`                          | `Integer.to_charlist/1` and `Integer.to_charlist/2` (v1.3)
+[v1.5]  | `Kernel.to_char_list/1`                             | `Kernel.to_charlist/1` (v1.3)
+[v1.5]  | `List.Chars.to_char_list/1`                         | `List.Chars.to_charlist/1` (v1.3)
+[v1.5]  | `Stream.filter_map/3`                               | `Stream.filter/2` + `Stream.map/2` (v1.0)
+[v1.5]  | `String.ljust/3` and `String.rjust/3`               | Use `String.pad_leading/3` and `String.pad_trailing/3` with a binary padding (v1.3)
+[v1.5]  | `String.strip/1` and `String.strip/2`               | `String.trim/1` and `String.trim/2` (v1.3)
+[v1.5]  | `String.lstrip/1` and `String.rstrip/1`             | `String.trim_leading/1` and `String.trim_trailing/1` (v1.3)
+[v1.5]  | `String.lstrip/2` and `String.rstrip/2`             | Use `String.trim_leading/2` and `String.trim_trailing/2` with a binary as second argument (v1.3)
+[v1.5]  | `String.to_char_list/1`                             | `String.to_charlist/1` (v1.3)
+[v1.5]  | `()` to mean `nil`                                  | `nil` (v1.0)
+[v1.5]  | `char_list/0` type                                  | `t:charlist/0` type (v1.3)
+[v1.5]  | `:char_lists` key in `t:Inspect.Opts.t/0` type      | `:charlists` key (v1.3)
+[v1.5]  | `:as_char_lists` value in `t:Inspect.Opts.t/0` type | `:as_charlists` value (v1.3)
+[v1.5]  | `@compile {:parse_transform, _}` in `Module`        | *None*
+[v1.5]  | EEx: `<%=` in middle and end expressions            | Use `<%` (`<%=` is allowed only on start expressions) (v1.0)
+[v1.4]  | `Access.key/1`                                      | `Access.key/2` (v1.3)
+[v1.4]  | `Behaviour` module                                  | `@callback` module attribute (v1.0)
+[v1.4]  | `Enum.uniq/2`                                       | `Enum.uniq_by/2` (v1.2)
+[v1.4]  | `Float.to_char_list/2`                              | `:erlang.float_to_list/2` (Erlang/OTP 17)
+[v1.4]  | `Float.to_string/2`                                 | `:erlang.float_to_binary/2` (Erlang/OTP 17)
+[v1.4]  | `HashDict` module                                   | `Map` (v1.2)
+[v1.4]  | `HashSet` module                                    | `MapSet` (v1.1)
+[v1.4]  |  Multi-letter aliases in `OptionParser`             | Use single-letter aliases (v1.0)
+[v1.4]  | `Set` module                                        | `MapSet` (v1.1)
+[v1.4]  | `Stream.uniq/2`                                     | `Stream.uniq_by/2` (v1.2)
+[v1.4]  | `IEx.Helpers.import_file/2`                         | `IEx.Helpers.import_file_if_available/1` (v1.3)
+[v1.4]  | `Mix.Utils.camelize/1`                              | `Macro.camelize/1` (v1.2)
+[v1.4]  | `Mix.Utils.underscore/1`                            | `Macro.underscore/1` (v1.2)
+[v1.4]  | Variable used as function call                      | Use parentheses (v1.0)
+[v1.4]  | Anonymous functions with no expression after `->`   | Use an expression or explicitly return `nil` (v1.0)
+[v1.4]  | Support for making private functions overridable    | Use public functions (v1.0)
+[v1.3]  | `Dict` module                                       | `Keyword` (v1.0) or `Map` (v1.2)
+[v1.3]  | `Keyword.size/1`                                    | `Kernel.length/1` (v1.0)
+[v1.3]  | `Map.size/1`                                        | `Kernel.map_size/1` (v1.0)
+[v1.3]  | `Set` behaviour                                     | `MapSet` data structure (v1.1)
+[v1.3]  | `String.valid_character?/1`                         | `String.valid?/1` (v1.0)
+[v1.3]  | `Task.find/2`                                       | Use direct message matching (v1.0)
+[v1.3]  | `:append_first` option in `Kernel.defdelegate/2`    | Define the function explicitly (v1.0)
+[v1.3]  | `/r` option in `Regex`                              | `/U` (v1.1)
+[v1.3]  | `\x{X*}` inside strings/sigils/charlists            | `\uXXXX` or `\u{X*}` (v1.1)
+[v1.3]  | Map/dictionary as 2nd argument in `Enum.group_by/3` | `Enum.reduce/3` (v1.0)
+[v1.3]  | Non-map as 2nd argument in `URI.decode_query/2`     | Use a map (v1.0)
+[v1.2]  | `Dict` behaviour                                    | `MapSet` data structure (v1.1)
+[v1.1]  | `Access` protocol                                   | `Access` behaviour (v1.1)
+[v1.1]  | `as: true \| false` in `alias/2` and `require/2`    | *None*
+[v1.1]  | `?\xHEX`                                            | `0xHEX` (v1.0)
 
 [v1.1]: https://github.com/elixir-lang/elixir/blob/v1.1/CHANGELOG.md#4-deprecations
 [v1.2]: https://github.com/elixir-lang/elixir/blob/v1.2/CHANGELOG.md#changelog-for-elixir-v12
@@ -151,3 +156,4 @@ Non-map as second argument in `URI.decode_query/2` | [v1.3]      | Use a map (v1
 [v1.6]: https://github.com/elixir-lang/elixir/blob/v1.6/CHANGELOG.md#4-deprecations
 [v1.7]: https://github.com/elixir-lang/elixir/blob/v1.7/CHANGELOG.md#4-hard-deprecations
 [v1.8]: https://github.com/elixir-lang/elixir/blob/v1.8/CHANGELOG.md#4-hard-deprecations
+[v1.9]: https://github.com/elixir-lang/elixir/blob/master/CHANGELOG.md#4-hard-deprecations
diff --git a/lib/elixir/pages/Library Guidelines.md b/lib/elixir/pages/Library Guidelines.md
index 419253ba1..f324d1682 100644
--- a/lib/elixir/pages/Library Guidelines.md	
+++ b/lib/elixir/pages/Library Guidelines.md	
@@ -256,24 +256,22 @@ and then defining a `my_app/application.ex` file with the following template:
 
 ```elixir
 defmodule MyApp.Application do
-    # See https://hexdocs.pm/elixir/Application.html
-    # for more information on OTP Applications
-    @moduledoc false
-
-    use Application
-
-    def start(_type, _args) do
-      # List all child processes to be supervised
-      children = [
-        # Starts a worker by calling: MyApp.Worker.start_link(arg)
-        # {MyApp.Worker, arg},
-      ]
-
-      # See https://hexdocs.pm/elixir/Supervisor.html
-      # for other strategies and supported options
-      opts = [strategy: :one_for_one, name: MyApp.Supervisor]
-      Supervisor.start_link(children, opts)
-    end
+  # See https://hexdocs.pm/elixir/Application.html
+  # for more information on OTP Applications
+  @moduledoc false
+
+  use Application
+
+  def start(_type, _args) do
+    children = [
+      # Starts a worker by calling: MyApp.Worker.start_link(arg)
+      # {MyApp.Worker, arg}
+    ]
+
+    # See https://hexdocs.pm/elixir/Supervisor.html
+    # for other strategies and supported options
+    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
+    Supervisor.start_link(children, opts)
   end
 end
 ```
diff --git a/lib/elixir/pages/Naming Conventions.md b/lib/elixir/pages/Naming Conventions.md
index 031d96211..3e455f5ec 100644
--- a/lib/elixir/pages/Naming Conventions.md	
+++ b/lib/elixir/pages/Naming Conventions.md	
@@ -67,9 +67,9 @@ The version without `!` is preferred when you want to handle different outcomes
 
 However, if you expect the outcome to always to be successful (e.g. if you expect the file always to exist), the bang variation can be more convenient and will raise a more helpful error message (than a failed pattern match) on failure.
 
-More examples of paired functions: `Base.decode16/2` and `Base.decode16!/2`, `File.cwd/0` and `File.cwd!/0`
+More examples of paired functions: `Base.decode16/2` and `Base.decode16!/2`, `File.cwd/0` and `File.cwd!/0`.
 
-There are also some non-paired functions, with no non-bang variant. The bang still signifies that it will raise an exception on failure. Examples: `Mix.Config.validate!/1`, `Protocol.assert_protocol!/1`
+There are also some non-paired functions, with no non-bang variant. The bang still signifies that it will raise an exception on failure. Example: `Protocol.assert_protocol!/1`.
 
 In macro code, the bang on `Kernel.alias!/1` and `Kernel.var!/2` signifies that [macro hygiene](https://elixir-lang.org/getting-started/meta/macros.html#macros-hygiene) is set aside.
 
diff --git a/lib/elixir/pages/Syntax Reference.md b/lib/elixir/pages/Syntax Reference.md
index 57f7a98b1..631325b60 100644
--- a/lib/elixir/pages/Syntax Reference.md	
+++ b/lib/elixir/pages/Syntax Reference.md	
@@ -58,12 +58,14 @@ Strings are always represented as themselves in the AST.
 
 ### Charlists
 
-Charlists in Elixir are written in single-quotes, such as `'foo'`. Any single-quote inside the string must be escaped with `\ `. Charlists are a list of integers, each integer representing a Unicode character.
+Charlists in Elixir are written in single-quotes, such as `'foo'`. Any single-quote inside the string must be escaped with `\ `. Charlists are made of non-negative integers, where each integer represents a Unicode codepoint.
 
 Multi-line charlists are written with three single-quotes (`'''`), the same multi-line strings are.
 
 Charlists are always represented as themselves in the AST.
 
+For more in-depth information, please read the "Charlists" section in the `List` module.
+
 ### Lists, tuples and binaries
 
 Data structures such as lists, tuples, and binaries are marked respectively by the delimiters `[...]`, `{...}`, and `<<...>>`. Each element is separated by comma. A trailing comma is also allowed, such as in `[1, 2, 3,]`.
diff --git a/lib/elixir/pages/Typespecs.md b/lib/elixir/pages/Typespecs.md
index c8e834f3f..817ed03ff 100644
--- a/lib/elixir/pages/Typespecs.md
+++ b/lib/elixir/pages/Typespecs.md
@@ -1,6 +1,6 @@
 # Typespecs
 
-Elixir comes with a notation for declaring types and specifications. Elixir is a dynamically typed language, and as such, type specifications are never used by the compiler to optimize or modify code. Still, using type specifications is useful because
+Elixir comes with a notation for declaring types and specifications. Elixir is a dynamically typed language, and as such, type specifications are never used by the compiler to optimize or modify code. Still, using type specifications is useful because:
 
   * they provide documentation (for example, tools such as [ExDoc](https://github.com/elixir-lang/ex_doc) show type specifications in the documentation)
   * they're used by tools such as [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html), that can analyze code with typespec to find type inconsistencies and possible bugs
@@ -149,6 +149,8 @@ Built-in type           | Defined as
 `struct()`              | `%{:__struct__ => atom(), optional(atom()) => any()}`
 `timeout()`             | `:infinity` \| `non_neg_integer()`
 
+`as_boolean(t)` exists to signal users that the given value will be treated as a boolean, where `nil` and `false` will be evaluated as `false` and everything else is `true`. For example, `Enum.filter/2` has the following specification: `filter(t, (element -> as_boolean(term))) :: list`.
+
 ### Remote types
 
 Any module is also able to define its own types and the modules in Elixir are no exception. For example, the `Range` module defines a `t/0` type that represents a range: this type can be referred to as `t:Range.t/0`. In a similar fashion, a string is `t:String.t/0`, any enumerable can be `t:Enum.t/0`, and so on.
diff --git a/lib/elixir/pages/Writing Documentation.md b/lib/elixir/pages/Writing Documentation.md
index 00976d39c..285af6e6f 100644
--- a/lib/elixir/pages/Writing Documentation.md	
+++ b/lib/elixir/pages/Writing Documentation.md	
@@ -86,7 +86,7 @@ When writing documentation:
   * Start new sections with second level Markdown headers `##`. First level headers are reserved for module and function names.
 
   * Place documentation before the first clause of multi-clause functions. Documentation is always per function and arity and not per clause.
-  
+
   * Use the `:since` key in the documentation metadata to annotate whenever new functions or modules are added to your API.
 
 ## Doctests
diff --git a/lib/elixir/src/elixir.erl b/lib/elixir/src/elixir.erl
index e0cc49b09..f925d6089 100644
--- a/lib/elixir/src/elixir.erl
+++ b/lib/elixir/src/elixir.erl
@@ -10,7 +10,7 @@
 -define(system, 'Elixir.System').
 
 %% Top level types
-%% TODO: Remove char_list type by 2.0
+%% TODO: Remove char_list type on v2.0
 -export_type([charlist/0, char_list/0, nonempty_charlist/0, struct/0, as_boolean/1, keyword/0, keyword/1]).
 -type charlist() :: string().
 -type char_list() :: string().
diff --git a/lib/elixir/src/elixir_clauses.erl b/lib/elixir/src/elixir_clauses.erl
index 3a2800e3f..b1fd214c7 100644
--- a/lib/elixir/src/elixir_clauses.erl
+++ b/lib/elixir/src/elixir_clauses.erl
@@ -189,7 +189,7 @@ expand_with_else(Meta, Opts, E, HasMatch) ->
 'try'(Meta, Opts, E) when not is_list(Opts) ->
   form_error(Meta, ?key(E, file), elixir_expand, {invalid_args, 'try'});
 'try'(Meta, Opts, E) ->
-  % TODO: Make this an error in Elixir 2.0
+  % TODO: Make this an error on v2.0
   case Opts of
     [{do, _}, {else, _}] ->
       form_warn(Meta, ?key(E, file), ?MODULE, {try_with_only_else_clause, origin(Meta, 'try')});
diff --git a/lib/elixir/src/elixir_dispatch.erl b/lib/elixir/src/elixir_dispatch.erl
index 57874a3a6..dc5d67483 100644
--- a/lib/elixir/src/elixir_dispatch.erl
+++ b/lib/elixir/src/elixir_dispatch.erl
@@ -328,12 +328,11 @@ elixir_imported_macros() ->
 %% Inline common cases.
 check_deprecation(Meta, ?kernel, to_char_list, 1, E) ->
   Message = "Kernel.to_char_list/1 is deprecated. Use Kernel.to_charlist/1 instead",
-  elixir_errors:warn(?line(Meta), ?key(E, file), Message);
+  elixir_errors:erl_warn(?line(Meta), ?key(E, file), Message);
 check_deprecation(_, ?kernel, _, _, _) ->
   ok;
 check_deprecation(_, erlang, _, _, _) ->
   ok;
-%% TODO: Fully deprecate System.stacktrace and eventually remove it on v2.0.
 check_deprecation(Meta, 'Elixir.System', stacktrace, 0, #{contextual_vars := Vars} = E) ->
   case lists:member('__STACKTRACE__', Vars) of
     true ->
@@ -344,7 +343,7 @@ check_deprecation(Meta, 'Elixir.System', stacktrace, 0, #{contextual_vars := Var
           "If you want to support only Elixir v1.7+, you must access __STACKTRACE__ "
           "inside a rescue/catch. If you want to support earlier Elixir versions, "
           "move System.stacktrace/0 inside a rescue/catch",
-      elixir_errors:warn(?line(Meta), ?key(E, file), Message)
+      elixir_errors:erl_warn(?line(Meta), ?key(E, file), Message)
   end;
 check_deprecation(Meta, Receiver, Name, Arity, E) ->
   case (get(elixir_compiler_dest) == undefined) andalso is_module_loaded(Receiver) andalso
@@ -353,7 +352,7 @@ check_deprecation(Meta, Receiver, Name, Arity, E) ->
       case lists:keyfind({Name, Arity}, 1, Deprecations) of
         {_, Message} ->
           Warning = deprecation_message(Receiver, Name, Arity, Message),
-          elixir_errors:warn(?line(Meta), ?key(E, file), Warning);
+          elixir_errors:erl_warn(?line(Meta), ?key(E, file), Warning);
         false ->
           ok
       end;
diff --git a/lib/elixir/src/elixir_erl.erl b/lib/elixir/src/elixir_erl.erl
index 9092b8d88..26a72e83f 100644
--- a/lib/elixir/src/elixir_erl.erl
+++ b/lib/elixir/src/elixir_erl.erl
@@ -320,7 +320,12 @@ typespecs_form(Map, TranslatedTypespecs, MacroNames) ->
   Forms1 = types_form(Types, Forms0),
   Forms2 = callspecs_form(spec, Specs, [], MacroNames, Forms1, Map),
   Forms3 = callspecs_form(callback, AllCallbacks, OptionalCallbacks, MacroCallbackNames, Forms2, Map),
-  {Types, AllCallbacks, Forms3}.
+
+  AllCallbacksWithoutSpecs = lists:usort([
+    {Kind, Name, Arity} || {Kind, {Name, Arity}, _Line, _Spec} <- AllCallbacks
+  ]),
+
+  {Types, AllCallbacksWithoutSpecs, Forms3}.
 
 %% Types
 
@@ -371,8 +376,8 @@ callspecs_form(_Kind, [], _Optional, _Macros, Forms, _ModuleMap) ->
 callspecs_form(Kind, Entries, Optional, Macros, Forms, ModuleMap) ->
   #{unreachable := Unreachable} = ModuleMap,
 
-  SpecsMap =
-    lists:foldl(fun({_, NameArity, Line, Spec}, Acc) ->
+  {SpecsMap, Signatures} =
+    lists:foldl(fun({_, NameArity, Line, Spec}, {Acc, NA}) ->
       case Kind of
         spec -> validate_spec_for_existing_function(ModuleMap, NameArity, Line);
         _ -> ok
@@ -381,15 +386,16 @@ callspecs_form(Kind, Entries, Optional, Macros, Forms, ModuleMap) ->
       case lists:member(NameArity, Unreachable) of
         false ->
           case Acc of
-            #{NameArity := List} -> Acc#{NameArity := [{Spec, Line} | List]};
-            #{} -> Acc#{NameArity => [{Spec, Line}]}
+            #{NameArity := List} -> {Acc#{NameArity := [{Spec, Line} | List]}, NA};
+            #{} -> {Acc#{NameArity => [{Spec, Line}]}, [NameArity | NA]}
           end;
         true ->
-          Acc
+          {Acc, NA}
       end
-    end, #{}, Entries),
+    end, {#{}, []}, Entries),
 
-  maps:fold(fun(NameArity, ExprsLines, Acc) ->
+  lists:foldl(fun(NameArity, Acc) ->
+    #{NameArity := ExprsLines} = SpecsMap,
     {Exprs, Lines} = lists:unzip(ExprsLines),
     Line = lists:min(Lines),
 
@@ -410,7 +416,7 @@ callspecs_form(Kind, Entries, Optional, Macros, Forms, ModuleMap) ->
       false ->
         [{attribute, Line, Kind, {Key, lists:reverse(Value)}} | Acc]
     end
-  end, Forms, SpecsMap).
+  end, Forms, lists:sort(Signatures)).
 
 spec_for_macro({type, Line, 'fun', [{type, _, product, Args} | T]}) ->
   NewArgs = [{type, Line, term, []} | Args],
@@ -518,8 +524,7 @@ get_callback_docs(Set, Callbacks) ->
     [],
     doc_value(Doc),
     Meta
-   } || {Kind, {Name, Arity}, _, _} <- Callbacks,
-        {Key, Line, Doc, Meta} <- ets:lookup(Set, {Kind, Name, Arity})].
+   } || Callback <- Callbacks, {Key, Line, Doc, Meta} <- ets:lookup(Set, Callback)].
 
 get_type_docs(Set, Types) ->
   [{Key,
diff --git a/lib/elixir/src/elixir_erl_compiler.erl b/lib/elixir/src/elixir_erl_compiler.erl
index 86b4d8073..3695ad8db 100644
--- a/lib/elixir/src/elixir_erl_compiler.erl
+++ b/lib/elixir/src/elixir_erl_compiler.erl
@@ -99,7 +99,7 @@ handle_file_warning(_, _File, {_Line, erl_lint, _}) -> ok;
 
 handle_file_warning(_, File, {Line, Module, Desc}) ->
   Message = custom_format(Module, Desc),
-  elixir_errors:warn(Line, File, Message).
+  elixir_errors:erl_warn(Line, File, Message).
 
 %% Handle warnings
 
diff --git a/lib/elixir/src/elixir_erl_pass.erl b/lib/elixir/src/elixir_erl_pass.erl
index 7d728543c..6cbfae02e 100644
--- a/lib/elixir/src/elixir_erl_pass.erl
+++ b/lib/elixir/src/elixir_erl_pass.erl
@@ -379,6 +379,10 @@ translate_with_else(Meta, [], S) ->
   {VarName, _, SC} = elixir_erl_var:build('_', S),
   Var = {var, Ann, VarName},
   {{clause, Ann, [Var], [], [Var]}, SC};
+translate_with_else(Meta, [{else, [{'->', _, [[{Var, _, Ctx}], Clause]}]}], S) when is_atom(Var), is_atom(Ctx) ->
+  {ElseVarErl, SV} = elixir_erl_var:assign(Meta, Var, Ctx, S),
+  {TranslatedClause, SC} = elixir_erl_pass:translate(Clause, SV),
+  {{clause, Meta, [ElseVarErl], [], [TranslatedClause]}, SC};
 translate_with_else(Meta, [{else, Else}], S) ->
   Generated = ?generated(Meta),
   ElseVarEx = {else, Generated, ?var_context},
diff --git a/lib/elixir/src/elixir_errors.erl b/lib/elixir/src/elixir_errors.erl
index 24805cdb1..192ef277a 100644
--- a/lib/elixir/src/elixir_errors.erl
+++ b/lib/elixir/src/elixir_errors.erl
@@ -5,18 +5,18 @@
 %% the line number to be none (as it may happen in some erlang errors).
 -module(elixir_errors).
 -export([compile_error/3, compile_error/4,
-         form_error/4, form_warn/4, parse_error/4, bare_warn/3, warn/3]).
+         form_error/4, form_warn/4, parse_error/4, erl_warn/3, io_warn/3]).
 -include("elixir.hrl").
 
--spec warn(non_neg_integer() | none, unicode:chardata(), unicode:chardata()) -> ok.
-warn(none, File, Warning) ->
-  warn(0, File, Warning);
-warn(Line, File, Warning) when is_integer(Line), is_binary(File) ->
+-spec erl_warn(non_neg_integer() | none, unicode:chardata(), unicode:chardata()) -> ok.
+erl_warn(none, File, Warning) ->
+  erl_warn(0, File, Warning);
+erl_warn(Line, File, Warning) when is_integer(Line), is_binary(File) ->
   send_warning(File, Line, Warning),
   print_warning([Warning, "\n  ", file_format(Line, File), $\n]).
 
--spec bare_warn(non_neg_integer() | nil, unicode:chardata() | nil, unicode:chardata()) -> ok.
-bare_warn(Line, File, Message) when is_integer(Line) or (Line == nil), is_binary(File) or (File == nil) ->
+-spec io_warn(non_neg_integer() | nil, unicode:chardata() | nil, unicode:chardata()) -> ok.
+io_warn(Line, File, Message) when is_integer(Line) or (Line == nil), is_binary(File) or (File == nil) ->
   send_warning(File, Line, Message),
   print_warning(Message).
 
@@ -35,7 +35,7 @@ form_error(Meta, File, Module, Desc) ->
 -spec form_warn(list(), binary(), module(), any()) -> ok.
 form_warn(Meta, File, Module, Desc) when is_list(Meta) ->
   {MetaFile, MetaLine} = meta_location(Meta, File),
-  warn(MetaLine, MetaFile, Module:format_error(Desc)).
+  erl_warn(MetaLine, MetaFile, Module:format_error(Desc)).
 
 %% Compilation error.
 
diff --git a/lib/elixir/src/elixir_expand.erl b/lib/elixir/src/elixir_expand.erl
index f43db22e8..d2c6656e3 100644
--- a/lib/elixir/src/elixir_expand.erl
+++ b/lib/elixir/src/elixir_expand.erl
@@ -311,7 +311,7 @@ expand({super, Meta, Args}, E) when is_list(Args) ->
               [element(1, Function), Arity]
             ),
 
-          elixir_errors:warn(?line(Meta), ?key(E, file), Message);
+          elixir_errors:erl_warn(?line(Meta), ?key(E, file), Message);
 
         _ ->
           ok
@@ -403,11 +403,11 @@ expand({Name, Meta, Kind} = Var, E) when is_atom(Name), is_atom(Kind) ->
         _ ->
           case ?key(E, prematch_vars) of
             warn ->
-              %% TODO: Remove warn option on 2.0
+              %% TODO: Remove warn option on v2.0
               Message =
                 io_lib:format("variable \"~ts\" does not exist and is being expanded to \"~ts()\","
                   " please use parentheses to remove the ambiguity or change the variable name", [Name, Name]),
-              elixir_errors:warn(?line(Meta), ?key(E, file), Message),
+              elixir_errors:erl_warn(?line(Meta), ?key(E, file), Message),
               expand({Name, Meta, []}, E);
 
             raise ->
@@ -489,7 +489,7 @@ expand(Pid, E) when is_pid(Pid) ->
     nil ->
       {Pid, E};
     Function ->
-      %% TODO: Make me an error on 2.0
+      %% TODO: Make me an error on v2.0
       elixir_errors:form_warn([], ?key(E, file), ?MODULE,
                               {invalid_pid_in_function, Pid, Function}),
       {Pid, E}
diff --git a/lib/elixir/src/elixir_module.erl b/lib/elixir/src/elixir_module.erl
index eaf220047..7f9bf06c0 100644
--- a/lib/elixir/src/elixir_module.erl
+++ b/lib/elixir/src/elixir_module.erl
@@ -142,7 +142,7 @@ compile(Line, Module, Block, Vars, E) ->
 validate_compile_opts(Opts, Defs, Unreachable, File, Line) ->
   lists:flatmap(fun (Opt) -> validate_compile_opt(Opt, Defs, Unreachable, File, Line) end, Opts).
 
-%% TODO: Make this an error on 2.0
+%% TODO: Make this an error on v2.0
 validate_compile_opt({parse_transform, Module} = Opt, _Defs, _Unreachable, File, Line) ->
   elixir_errors:form_warn([{line, Line}], File, ?MODULE, {parse_transform, Module}),
   [Opt];
diff --git a/lib/elixir/src/elixir_overridable.erl b/lib/elixir/src/elixir_overridable.erl
index 34f72c076..70910f106 100644
--- a/lib/elixir/src/elixir_overridable.erl
+++ b/lib/elixir/src/elixir_overridable.erl
@@ -4,7 +4,6 @@
 -include("elixir.hrl").
 -define(attr, {elixir, overridable}).
 
-%% TODO: Use DataSet/DataBag for overridables
 setup({DataSet, _DataBag}) ->
   ets:insert(DataSet, {?attr, #{}}).
 
diff --git a/lib/elixir/src/elixir_parser.yrl b/lib/elixir/src/elixir_parser.yrl
index 3c26d997f..0380c2f67 100644
--- a/lib/elixir/src/elixir_parser.yrl
+++ b/lib/elixir/src/elixir_parser.yrl
@@ -683,8 +683,8 @@ number_value({_, {_, _, Value}, _}) ->
 %% Operators
 
 build_op({_Kind, Location, 'in'}, {UOp, _, [Left]}, Right) when ?rearrange_uop(UOp) ->
-  %% TODO: Remove "not left in right" rearrangement on 2.0
-  elixir_errors:warn(line_from_location(Location), ?file(),
+  %% TODO: Remove "not left in right" rearrangement on v2.0
+  elixir_errors:erl_warn(line_from_location(Location), ?file(),
     "\"not expr1 in expr2\" is deprecated. "
     "Instead use \"expr1 not in expr2\" if you require Elixir v1.5+, "
     "or \"not(expr1 in expr2)\" if you have to support earlier Elixir versions"),
@@ -1035,27 +1035,27 @@ error_invalid_kw_identifier({_, _, do} = Token) ->
 error_invalid_kw_identifier({_, _, KW} = Token) ->
   return_error(meta_from_token(Token), "syntax error before: ", "'" ++ atom_to_list(KW) ++ ":'").
 
-%% TODO: Make this an error on Elixir v2.0.
+%% TODO: Make this an error on v2.0
 warn_empty_paren({_, {Line, _, _}}) ->
-  elixir_errors:warn(Line, ?file(),
+  elixir_errors:erl_warn(Line, ?file(),
     "invalid expression (). "
     "If you want to invoke or define a function, make sure there are "
     "no spaces between the function name and its arguments. If you wanted "
     "to pass an empty block, pass a value instead, such as a nil or an atom").
 
-%% TODO: Make this an error on Elixir v2.0.
+%% TODO: Make this an error on v2.0
 warn_trailing_comma({',', {Line, _, _}}) ->
-  elixir_errors:warn(Line, ?file(),
+  elixir_errors:erl_warn(Line, ?file(),
     "trailing commas are not allowed inside function/macro call arguments"
   ).
 
 warn_empty_stab_clause({stab_op, {Line, _, _}, '->'}) ->
-  elixir_errors:warn(Line, ?file(),
+  elixir_errors:erl_warn(Line, ?file(),
     "an expression is always required on the right side of ->. "
     "Please provide a value after ->").
 
 warn_pipe({arrow_op, {Line, _, _}, Op}, {_, [_ | _], [_ | _]}) ->
-  elixir_errors:warn(Line, ?file(),
+  elixir_errors:erl_warn(Line, ?file(),
     io_lib:format(
       "parentheses are required when piping into a function call. For example:\n\n"
       "    foo 1 ~ts bar 2 ~ts baz 3\n\n"
diff --git a/lib/elixir/src/elixir_rewrite.erl b/lib/elixir/src/elixir_rewrite.erl
index b8e3915ea..0b283e651 100644
--- a/lib/elixir/src/elixir_rewrite.erl
+++ b/lib/elixir/src/elixir_rewrite.erl
@@ -114,7 +114,6 @@ inline(?list, to_tuple, 1) -> {erlang, list_to_tuple};
 
 inline(?map, keys, 1) -> {maps, keys};
 inline(?map, merge, 2) -> {maps, merge};
-inline(?map, size, 1) -> {maps, size}; %% TODO: Remove on 2.0
 inline(?map, to_list, 1) -> {maps, to_list};
 inline(?map, values, 1) -> {maps, values};
 
diff --git a/lib/elixir/src/elixir_tokenizer.erl b/lib/elixir/src/elixir_tokenizer.erl
index 76b70244b..5468aa0d3 100644
--- a/lib/elixir/src/elixir_tokenizer.erl
+++ b/lib/elixir/src/elixir_tokenizer.erl
@@ -236,7 +236,7 @@ tokenize([$?, Char | T], Line, Column, Scope, Tokens) ->
     {Escape, Name} ->
       Msg = io_lib:format("found ? followed by codepoint 0x~.16B (~ts), please use ?~ts instead",
                           [Char, Name, Escape]),
-      elixir_errors:warn(Line, Scope#elixir_tokenizer.file, Msg);
+      elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, Msg);
     false ->
       ok
   end,
@@ -423,7 +423,7 @@ tokenize([$:, H | T] = Original, Line, Column, Scope, Tokens) when ?is_quote(H)
     {NewLine, NewColumn, Parts, Rest} ->
       case is_unnecessary_quote(Parts, Scope) of
         true ->
-          elixir_errors:warn(Line, Scope#elixir_tokenizer.file, io_lib:format(
+          elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, io_lib:format(
             "found quoted atom \"~ts\" but the quotes are not required. "
             "Atoms made exclusively of Unicode letters, numbers, underscore, "
             "and @ do not require quotes",
@@ -644,7 +644,7 @@ handle_strings(T, Line, Column, H, Scope, Tokens) ->
     {NewLine, NewColumn, Parts, [$: | Rest]} when ?is_space(hd(Rest)) ->
       case is_unnecessary_quote(Parts, Scope) of
         true ->
-          elixir_errors:warn(Line, Scope#elixir_tokenizer.file, io_lib:format(
+          elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, io_lib:format(
             "found quoted keyword \"~ts\" but the quotes are not required. "
             "Note that keywords are always atoms, even when quoted. "
             "Similar to atoms, keywords made exclusively of Unicode "
@@ -738,7 +738,7 @@ handle_dot([$., H | T] = Original, Line, Column, DotInfo, Scope, Tokens) when ?i
     {NewLine, NewColumn, [Part], Rest} when is_list(Part) ->
       case is_unnecessary_quote([Part], Scope) of
         true ->
-          elixir_errors:warn(Line, Scope#elixir_tokenizer.file, io_lib:format(
+          elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, io_lib:format(
             "found quoted call \"~ts\" but the quotes are not required. "
             "Calls made exclusively of Unicode letters, numbers, and underscore "
             "do not require quotes",
@@ -900,7 +900,7 @@ remove_heredoc_spaces(Body, Spaces, Marker, Scope) ->
                           "      \"\"\"~n"
                           "    end~n~n"
                           "The current heredoc line is indented too little", [[Marker, Marker, Marker]]),
-      elixir_errors:warn(Line, Scope#elixir_tokenizer.file, Msg),
+      elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, Msg),
       Acc
   end.
 
@@ -1118,7 +1118,7 @@ tokenize_identifier(String, Line, Column, Scope) ->
   end.
 
 list_to_codepoint_hex(List) ->
-  [io_lib:format(" ~4.16.0B", [Codepoint]) || Codepoint <- List].
+  [io_lib:format(" 0x~4.16.0B", [Codepoint]) || Codepoint <- List].
 
 tokenize_alias(Rest, Line, Column, Atom, Length, Ascii, Special, Scope, Tokens) ->
   if
@@ -1393,7 +1393,7 @@ invalid_do_with_fn_error(Prefix) ->
   {Prefix, ". Anonymous functions are written as:\n\n"
   "    fn pattern -> expression end"}.
 
-% TODO: Turn into an error on Elixir 2.0.
+% TODO: Turn into an error on v2.0
 maybe_warn_too_many_of_same_char([T | _] = Token, [T | _] = _Rest, Line, Scope) ->
   Warning =
     case T of
@@ -1401,11 +1401,11 @@ maybe_warn_too_many_of_same_char([T | _] = Token, [T | _] = _Rest, Line, Scope)
       _ -> io_lib:format("please use a space between \"~ts\" and the next \"~ts\"", [Token, [T]])
     end,
   Message = io_lib:format("found \"~ts\" followed by \"~ts\", ~ts", [Token, [T], Warning]),
-  elixir_errors:warn(Line, Scope#elixir_tokenizer.file, Message);
+  elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, Message);
 maybe_warn_too_many_of_same_char(_Token, _Rest, _Line, _Scope) ->
   ok.
 
-%% TODO: Turn into an error on Elixir v2.0
+%% TODO: Turn into an error on v2.0
 maybe_warn_for_ambiguous_bang_before_equals(Kind, Atom, [$= | _], Scope, Line) ->
   {What, Identifier} =
     case Kind of
@@ -1419,7 +1419,7 @@ maybe_warn_for_ambiguous_bang_before_equals(Kind, Atom, [$= | _], Scope, Line) -
                           "It is unclear if you mean \"~ts ~ts=\" or \"~ts =\". Please add "
                           "a space before or after ~ts to remove the ambiguity",
                           [What, Identifier, [Last], lists:droplast(Identifier), [Last], Identifier, [Last]]),
-      elixir_errors:warn(Line, Scope#elixir_tokenizer.file, Msg);
+      elixir_errors:erl_warn(Line, Scope#elixir_tokenizer.file, Msg);
     _ ->
       ok
   end;
diff --git a/lib/elixir/test/elixir/calendar/datetime_test.exs b/lib/elixir/test/elixir/calendar/datetime_test.exs
index fe8c2b384..e023f4bf5 100644
--- a/lib/elixir/test/elixir/calendar/datetime_test.exs
+++ b/lib/elixir/test/elixir/calendar/datetime_test.exs
@@ -789,4 +789,22 @@ defmodule DateTimeTest do
       assert DateTime.to_iso8601(datetime_map) == "2018-07-01T12:34:25.123456Z"
     end
   end
+
+  describe "to_date/1" do
+    test "upcasting" do
+      assert catch_error(DateTime.to_date(~N[2000-02-29 12:23:34]))
+    end
+  end
+
+  describe "to_time/1" do
+    test "upcasting" do
+      assert catch_error(DateTime.to_time(~N[2000-02-29 12:23:34]))
+    end
+  end
+
+  describe "to_naive/1" do
+    test "upcasting" do
+      assert catch_error(DateTime.to_naive(~N[2000-02-29 12:23:34]))
+    end
+  end
 end
diff --git a/lib/elixir/test/elixir/calendar/iso_test.exs b/lib/elixir/test/elixir/calendar/iso_test.exs
index 7a2b18b48..9a52a658d 100644
--- a/lib/elixir/test/elixir/calendar/iso_test.exs
+++ b/lib/elixir/test/elixir/calendar/iso_test.exs
@@ -42,6 +42,54 @@ defmodule Calendar.ISOTest do
     end
   end
 
+  describe "naive_datetime_to_iso_days/7" do
+    test "raises with invalid dates" do
+      assert_raise ArgumentError, "invalid date: 2018-02-30", fn ->
+        Calendar.ISO.naive_datetime_to_iso_days(2018, 2, 30, 0, 0, 0, 0)
+      end
+
+      assert_raise ArgumentError, "invalid date: 2017-11--03", fn ->
+        Calendar.ISO.naive_datetime_to_iso_days(2017, 11, -3, 0, 0, 0, 0)
+      end
+    end
+  end
+
+  describe "day_of_week/3" do
+    test "raises with invalid dates" do
+      assert_raise ArgumentError, "invalid date: 2018-02-30", fn ->
+        Calendar.ISO.day_of_week(2018, 2, 30)
+      end
+
+      assert_raise ArgumentError, "invalid date: 2017-11-00", fn ->
+        Calendar.ISO.day_of_week(2017, 11, 0)
+      end
+    end
+  end
+
+  describe "day_of_era/3" do
+    test "raises with invalid dates" do
+      assert_raise ArgumentError, "invalid date: 2018-02-30", fn ->
+        Calendar.ISO.day_of_era(2018, 2, 30)
+      end
+
+      assert_raise ArgumentError, "invalid date: 2017-11-00", fn ->
+        Calendar.ISO.day_of_era(2017, 11, 0)
+      end
+    end
+  end
+
+  describe "day_of_year/3" do
+    test "raises with invalid dates" do
+      assert_raise ArgumentError, "invalid date: 2018-02-30", fn ->
+        Calendar.ISO.day_of_year(2018, 2, 30)
+      end
+
+      assert_raise ArgumentError, "invalid date: 2017-11-00", fn ->
+        Calendar.ISO.day_of_year(2017, 11, 0)
+      end
+    end
+  end
+
   defp iso_day_roundtrip(year, month, day) do
     iso_days = Calendar.ISO.date_to_iso_days(year, month, day)
     Calendar.ISO.date_from_iso_days(iso_days)
diff --git a/lib/elixir/test/elixir/calendar/naive_datetime_test.exs b/lib/elixir/test/elixir/calendar/naive_datetime_test.exs
index 2ce902392..4d6e31b65 100644
--- a/lib/elixir/test/elixir/calendar/naive_datetime_test.exs
+++ b/lib/elixir/test/elixir/calendar/naive_datetime_test.exs
@@ -176,41 +176,51 @@ defmodule NaiveDateTimeTest do
     assert catch_error(NaiveDateTime.truncate(~T[00:00:00.000000], :millisecond))
   end
 
-  test "to_date/2 with datetime" do
-    dt = %DateTime{
-      year: 2000,
-      month: 2,
-      day: 29,
-      zone_abbr: "CET",
-      hour: 23,
-      minute: 0,
-      second: 7,
-      microsecond: {3000, 6},
-      utc_offset: 3600,
-      std_offset: 0,
-      time_zone: "Europe/Warsaw"
-    }
+  describe "to_date/2" do
+    test "downcasting" do
+      dt = %DateTime{
+        year: 2000,
+        month: 2,
+        day: 29,
+        zone_abbr: "CET",
+        hour: 23,
+        minute: 0,
+        second: 7,
+        microsecond: {3000, 6},
+        utc_offset: 3600,
+        std_offset: 0,
+        time_zone: "Europe/Warsaw"
+      }
+
+      assert NaiveDateTime.to_date(dt) == ~D[2000-02-29]
+    end
 
-    assert NaiveDateTime.to_date(dt) == ~D[2000-02-29]
-    assert catch_error(NaiveDateTime.to_date(~D[2000-02-29]))
+    test "upcasting" do
+      assert catch_error(NaiveDateTime.to_date(~D[2000-02-29]))
+    end
   end
 
-  test "to_time/2 with datetime" do
-    dt = %DateTime{
-      year: 2000,
-      month: 2,
-      day: 29,
-      zone_abbr: "CET",
-      hour: 23,
-      minute: 0,
-      second: 7,
-      microsecond: {3000, 6},
-      utc_offset: 3600,
-      std_offset: 0,
-      time_zone: "Europe/Warsaw"
-    }
+  describe "to_time/2" do
+    test "downcasting" do
+      dt = %DateTime{
+        year: 2000,
+        month: 2,
+        day: 29,
+        zone_abbr: "CET",
+        hour: 23,
+        minute: 0,
+        second: 7,
+        microsecond: {3000, 6},
+        utc_offset: 3600,
+        std_offset: 0,
+        time_zone: "Europe/Warsaw"
+      }
+
+      assert NaiveDateTime.to_time(dt) == ~T[23:00:07.003000]
+    end
 
-    assert NaiveDateTime.to_time(dt) == ~T[23:00:07.003000]
-    assert catch_error(NaiveDateTime.to_time(~T[00:00:00.000000]))
+    test "upcasting" do
+      assert catch_error(NaiveDateTime.to_time(~T[00:00:00.000000]))
+    end
   end
 end
diff --git a/lib/elixir/test/elixir/file_test.exs b/lib/elixir/test/elixir/file_test.exs
index 2f2b656b0..4f5357787 100644
--- a/lib/elixir/test/elixir/file_test.exs
+++ b/lib/elixir/test/elixir/file_test.exs
@@ -140,6 +140,35 @@ defmodule FileTest do
       end
     end
 
+    test "rename! file to existing file default behaviour" do
+      src = tmp_fixture_path("file.txt")
+      dest = tmp_path("tmp.file")
+
+      File.write!(dest, "hello")
+
+      try do
+        assert File.exists?(dest)
+        assert File.rename!(src, dest) == :ok
+        refute File.exists?(src)
+        assert File.read!(dest) == "FOO\n"
+      after
+        File.rm_rf(src)
+        File.rm_rf(dest)
+      end
+    end
+
+    test "rename! with invalid file" do
+      src = tmp_fixture_path("invalid.txt")
+      dest = tmp_path("tmp.file")
+
+      message =
+        "could not rename from #{inspect(src)} to #{inspect(dest)}: no such file or directory"
+
+      assert_raise File.RenameError, message, fn ->
+        File.rename!(src, dest)
+      end
+    end
+
     test "rename dir to existing file" do
       src = tmp_fixture_path("cp_r")
       dest = tmp_path("tmp.file")
diff --git a/lib/elixir/test/elixir/float_test.exs b/lib/elixir/test/elixir/float_test.exs
index 510508064..3545e4e7f 100644
--- a/lib/elixir/test/elixir/float_test.exs
+++ b/lib/elixir/test/elixir/float_test.exs
@@ -55,21 +55,41 @@ defmodule FloatTest do
     assert Float.floor(1.32453e-10) === 0.0
   end
 
-  test "floor/2 with precision" do
-    assert Float.floor(12.524235, 0) === 12.0
-    assert Float.floor(-12.524235, 0) === -13.0
+  describe "floor/2" do
+    test "with 0.0" do
+      for precision <- 0..15 do
+        assert Float.floor(0.0, precision) === 0.0
+        assert Float.floor(-0.0, precision) === -0.0
+      end
+    end
+
+    test "floor/2 with precision" do
+      assert Float.floor(12.524235, 0) === 12.0
+      assert Float.floor(-12.524235, 0) === -13.0
 
-    assert Float.floor(12.52, 2) === 12.51
-    assert Float.floor(-12.52, 2) === -12.52
+      assert Float.floor(12.52, 2) === 12.51
+      assert Float.floor(-12.52, 2) === -12.52
 
-    assert Float.floor(12.524235, 2) === 12.52
-    assert Float.floor(-12.524235, 3) === -12.525
+      assert Float.floor(12.524235, 2) === 12.52
+      assert Float.floor(-12.524235, 3) === -12.525
 
-    assert Float.floor(12.32453e-20, 2) === 0.0
-    assert Float.floor(-12.32453e-20, 2) === -0.01
+      assert Float.floor(12.32453e-20, 2) === 0.0
+      assert Float.floor(-12.32453e-20, 2) === -0.01
+
+      assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
+        Float.floor(1.1, 16)
+      end
+    end
 
-    assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
-      Float.floor(1.1, 16)
+    test "with subnormal floats" do
+      assert Float.floor(-5.0e-324, 0) === -1.0
+      assert Float.floor(-5.0e-324, 1) === -0.1
+      assert Float.floor(-5.0e-324, 2) === -0.01
+      assert Float.floor(-5.0e-324, 15) === -0.000000000000001
+
+      for precision <- 0..15 do
+        assert Float.floor(5.0e-324, precision) === 0.0
+      end
     end
   end
 
@@ -88,36 +108,72 @@ defmodule FloatTest do
     assert Float.ceil(0.0) === 0.0
   end
 
-  test "ceil/2 with precision" do
-    assert Float.ceil(12.524235, 0) === 13.0
-    assert Float.ceil(-12.524235, 0) === -12.0
+  describe "ceil/2" do
+    test "with 0.0" do
+      for precision <- 0..15 do
+        assert Float.ceil(0.0, precision) === 0.0
+        assert Float.ceil(-0.0, precision) === -0.0
+      end
+    end
 
-    assert Float.ceil(12.52, 2) === 12.52
-    assert Float.ceil(-12.52, 2) === -12.51
+    test "with regular floats" do
+      assert Float.ceil(12.524235, 0) === 13.0
+      assert Float.ceil(-12.524235, 0) === -12.0
 
-    assert Float.ceil(12.524235, 2) === 12.53
-    assert Float.ceil(-12.524235, 3) === -12.524
+      assert Float.ceil(12.52, 2) === 12.52
+      assert Float.ceil(-12.52, 2) === -12.51
 
-    assert Float.ceil(12.32453e-20, 2) === 0.01
-    assert Float.ceil(-12.32453e-20, 2) === 0.0
+      assert Float.ceil(12.524235, 2) === 12.53
+      assert Float.ceil(-12.524235, 3) === -12.524
 
-    assert Float.ceil(0.0, 2) === 0.0
+      assert Float.ceil(12.32453e-20, 2) === 0.01
+      assert Float.ceil(-12.32453e-20, 2) === 0.0
 
-    assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
-      Float.ceil(1.1, 16)
+      assert Float.ceil(0.0, 2) === 0.0
+
+      assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
+        Float.ceil(1.1, 16)
+      end
+    end
+
+    test "with subnormal floats" do
+      assert Float.ceil(5.0e-324, 0) === 1.0
+      assert Float.ceil(5.0e-324, 1) === 0.1
+      assert Float.ceil(5.0e-324, 2) === 0.01
+      assert Float.ceil(5.0e-324, 15) === 0.000000000000001
+
+      for precision <- 0..15 do
+        assert Float.ceil(-5.0e-324, precision) === -0.0
+      end
     end
   end
 
-  test "round/2" do
-    assert Float.round(5.5675, 3) === 5.567
-    assert Float.round(-5.5674, 3) === -5.567
-    assert Float.round(5.5, 3) === 5.5
-    assert Float.round(5.5e-10, 10) === 5.0e-10
-    assert Float.round(5.5e-10, 8) === 0.0
-    assert Float.round(5.0, 0) === 5.0
+  describe "round/2" do
+    test "with 0.0" do
+      for precision <- 0..15 do
+        assert Float.round(0.0, precision) === 0.0
+        assert Float.round(-0.0, precision) === -0.0
+      end
+    end
 
-    assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
-      Float.round(1.1, 16)
+    test "with regular floats" do
+      assert Float.round(5.5675, 3) === 5.567
+      assert Float.round(-5.5674, 3) === -5.567
+      assert Float.round(5.5, 3) === 5.5
+      assert Float.round(5.5e-10, 10) === 5.0e-10
+      assert Float.round(5.5e-10, 8) === 0.0
+      assert Float.round(5.0, 0) === 5.0
+
+      assert_raise ArgumentError, "precision 16 is out of valid range of 0..15", fn ->
+        Float.round(1.1, 16)
+      end
+    end
+
+    test "with subnormal floats" do
+      for precision <- 0..15 do
+        assert Float.round(5.0e-324, precision) === 0.0
+        assert Float.round(-5.0e-324, precision) === -0.0
+      end
     end
   end
 
@@ -126,6 +182,12 @@ defmodule FloatTest do
       assert Float.ratio(0.0) == {0, 1}
     end
 
+    test "with regular floats" do
+      assert Float.ratio(3.14) == {7_070_651_414_971_679, 2_251_799_813_685_248}
+      assert Float.ratio(-3.14) == {-7_070_651_414_971_679, 2_251_799_813_685_248}
+      assert Float.ratio(1.5) == {3, 2}
+    end
+
     test "with subnormal floats" do
       assert Float.ratio(5.0e-324) ==
                {1,
diff --git a/lib/elixir/test/elixir/io/ansi/docs_test.exs b/lib/elixir/test/elixir/io/ansi/docs_test.exs
index 6e5a645ea..7c11b8021 100644
--- a/lib/elixir/test/elixir/io/ansi/docs_test.exs
+++ b/lib/elixir/test/elixir/io/ansi/docs_test.exs
@@ -321,6 +321,11 @@ defmodule IO.ANSI.DocsTest do
     assert format("`https_proxy`") == "\e[36mhttps_proxy\e[0m\n\e[0m"
   end
 
+  test "escaping of several Markdown links in one line" do
+    assert format("[List](`List`) (`[1, 2, 3]`), [Map](`Map`)") ==
+             "List (\e[36mList\e[0m) (\e[36m[1, 2, 3]\e[0m), Map (\e[36mMap\e[0m)\n\e[0m"
+  end
+
   test "lone thing that looks like a table line isn't" do
     assert format("one\n2 | 3\ntwo\n") == "one 2 | 3 two\n\e[0m"
   end
diff --git a/lib/elixir/test/elixir/kernel/cli_test.exs b/lib/elixir/test/elixir/kernel/cli_test.exs
index 39f7664ca..151464a9a 100644
--- a/lib/elixir/test/elixir/kernel/cli_test.exs
+++ b/lib/elixir/test/elixir/kernel/cli_test.exs
@@ -2,7 +2,7 @@ Code.require_file("../test_helper.exs", __DIR__)
 
 import PathHelpers
 
-defmodule Kernel.CLI.ARGVTest do
+defmodule Kernel.CLITest do
   use ExUnit.Case, async: true
 
   import ExUnit.CaptureIO
@@ -23,21 +23,7 @@ defmodule Kernel.CLI.ARGVTest do
              assert run(["-e", "IO.puts :ok", "--", "sample.exs", "-o", "1", "2"]) ==
                       ["sample.exs", "-o", "1", "2"]
            end) == "ok\n"
-
-    assert capture_io(fn ->
-             assert run(["-e", "IO.puts :ok", "--hidden", "sample.exs", "-o", "1", "2"]) ==
-                      ["sample.exs", "-o", "1", "2"]
-           end) == "ok\n"
-
-    assert capture_io(fn ->
-             assert run(["-e", "IO.puts :ok", "--", "--hidden", "sample.exs", "-o", "1", "2"]) ==
-                      ["--hidden", "sample.exs", "-o", "1", "2"]
-           end) == "ok\n"
   end
-end
-
-defmodule Kernel.CLI.OptionParsingTest do
-  use ExUnit.Case, async: true
 
   test "properly parses paths" do
     root = fixture_path("../../..") |> to_charlist
@@ -53,30 +39,21 @@ defmodule Kernel.CLI.OptionParsingTest do
     # pz
     assert to_charlist(Path.expand('lib/list', root)) in path
   end
-end
 
-defmodule Kernel.CLI.AtExitTest do
-  use ExUnit.Case, async: true
-
-  test "invokes at_exit callbacks" do
-    assert elixir(fixture_path("at_exit.exs") |> to_charlist) ==
-             'goodbye cruel world with status 1\n'
-  end
-end
+  test "properly formats errors" do
+    assert String.starts_with?(elixir('-e ":erlang.throw 1"'), "** (throw) 1")
+    assert String.starts_with?(elixir('-e ":erlang.error 1"'), "** (ErlangError) Erlang error: 1")
+    assert String.starts_with?(elixir('-e "1 +"'), "** (TokenMissingError)")
 
-defmodule Kernel.CLI.ErrorTest do
-  use ExUnit.Case, async: true
-
-  test "properly format errors" do
-    assert :string.str('** (throw) 1', elixir('-e "throw 1"')) == 0
-    assert :string.str('** (ErlangError) Erlang error: 1', elixir('-e "error 1"')) == 0
+    assert elixir('-e "Task.async(fn -> raise ArgumentError end) |> Task.await"') =~
+             "an exception was raised:\n    ** (ArgumentError) argument error"
 
     assert elixir('-e "IO.puts(Process.flag(:trap_exit, false)); exit({:shutdown, 1})"') ==
-             'false\n'
+             "false\n"
   end
 
   test "blames exceptions" do
-    error = to_string(elixir('-e "Access.fetch :foo, :bar"'))
+    error = elixir('-e "Access.fetch :foo, :bar"')
     assert error =~ "** (FunctionClauseError) no function clause matching in Access.fetch/2"
     assert error =~ "The following arguments were given to Access.fetch/2"
     assert error =~ ":foo"
@@ -85,6 +62,40 @@ defmodule Kernel.CLI.ErrorTest do
   end
 end
 
+defmodule Kernel.CLI.RPCTest do
+  use ExUnit.Case, async: true
+
+  defp rpc_eval(command) do
+    node = "cli-rpc#{System.unique_integer()}@127.0.0.1"
+    elixir('--name #{node} --rpc-eval #{node} "#{command}"')
+  end
+
+  test "invokes command on remote node" do
+    assert rpc_eval("IO.puts :ok") == "ok\n"
+  end
+
+  test "properly formats errors" do
+    assert String.starts_with?(rpc_eval(":erlang.throw 1"), "** (throw) 1")
+    assert String.starts_with?(rpc_eval(":erlang.error 1"), "** (ErlangError) Erlang error: 1")
+    assert String.starts_with?(rpc_eval("1 +"), "** (TokenMissingError)")
+
+    assert rpc_eval("Task.async(fn -> raise ArgumentError end) |> Task.await") =~
+             "an exception was raised:\n    ** (ArgumentError) argument error"
+
+    assert rpc_eval("IO.puts(Process.flag(:trap_exit, false)); exit({:shutdown, 1})") ==
+             "false\n"
+  end
+end
+
+defmodule Kernel.CLI.AtExitTest do
+  use ExUnit.Case, async: true
+
+  test "invokes at_exit callbacks" do
+    assert elixir(fixture_path("at_exit.exs") |> to_charlist) ==
+             "goodbye cruel world with status 1\n"
+  end
+end
+
 defmodule Kernel.CLI.CompileTest do
   use ExUnit.Case, async: true
 
@@ -99,7 +110,7 @@ defmodule Kernel.CLI.CompileTest do
   end
 
   test "compiles code", context do
-    assert elixirc('#{context[:fixture]} -o #{context[:tmp_dir_path]}') == ''
+    assert elixirc('#{context[:fixture]} -o #{context[:tmp_dir_path]}') == ""
     assert File.regular?(context[:beam_file_path])
 
     # Assert that the module is loaded into memory with the proper destination for the BEAM file.
@@ -111,23 +122,19 @@ defmodule Kernel.CLI.CompileTest do
 
   test "fails on missing patterns", context do
     output = elixirc('#{context[:fixture]} non_existing.ex -o #{context[:tmp_dir_path]}')
-    assert :string.str(output, 'non_existing.ex') > 0, "expected non_existing.ex to be mentioned"
-
-    assert :string.str(output, 'compile_sample.ex') == 0,
-           "expected compile_sample.ex to not be mentioned"
-
-    refute File.exists?(context[:beam_file_path]), "expected the sample to not be compiled"
+    assert output =~ "non_existing.ex"
+    refute output =~ "compile_sample.ex"
+    refute File.exists?(context[:beam_file_path])
   end
 
   test "fails on missing write access to .beam file", context do
     compilation_args = '#{context[:fixture]} -o #{context[:tmp_dir_path]}'
 
-    assert elixirc(compilation_args) == ''
+    assert elixirc(compilation_args) == ""
     assert File.regular?(context[:beam_file_path])
 
     # Set the .beam file to read-only
     File.chmod!(context[:beam_file_path], 4)
-
     {:ok, %{access: access}} = File.stat(context[:beam_file_path])
 
     # Can only assert when read-only applies to the user
@@ -135,11 +142,9 @@ defmodule Kernel.CLI.CompileTest do
       output = elixirc(compilation_args)
 
       expected =
-        '(File.Error) could not write to "' ++
-          String.to_charlist(context[:beam_file_path]) ++ '": permission denied'
+        "(File.Error) could not write to #{inspect(context[:beam_file_path])}: permission denied"
 
-      assert :string.str(output, expected) > 0,
-             "expected compilation error message due to not having write access"
+      assert String.contains?(output, expected)
     end
   end
 end
diff --git a/lib/elixir/test/elixir/kernel/dialyzer_test.exs b/lib/elixir/test/elixir/kernel/dialyzer_test.exs
index 2ea2816e0..c9d4755e6 100644
--- a/lib/elixir/test/elixir/kernel/dialyzer_test.exs
+++ b/lib/elixir/test/elixir/kernel/dialyzer_test.exs
@@ -27,7 +27,7 @@ defmodule Kernel.DialyzerTest do
     dialyzer_run(analysis_type: :plt_build, output_plt: plt, apps: [:erts], files: files)
 
     # Compile Dialyzer fixtures
-    assert '' = elixirc("#{fixture_path("dialyzer")} -o #{dir}")
+    assert elixirc("#{fixture_path("dialyzer")} -o #{dir}") == ""
 
     {:ok, [base_dir: dir, base_plt: plt]}
   end
diff --git a/lib/elixir/test/elixir/kernel/docs_test.exs b/lib/elixir/test/elixir/kernel/docs_test.exs
index fd26dd2a5..c9c3ab0fc 100644
--- a/lib/elixir/test/elixir/kernel/docs_test.exs
+++ b/lib/elixir/test/elixir/kernel/docs_test.exs
@@ -164,9 +164,17 @@ defmodule Kernel.DocsTest do
           @callback bar() :: term
           @callback baz(any, term) :: any
 
+          @doc "Callback with multiple clauses"
+          @callback callback_multi(integer) :: integer
+          @callback callback_multi(atom) :: atom
+
           @doc "Macrocallback doc"
           @macrocallback qux(any) :: any
 
+          @doc "Macrocallback with multiple clauses"
+          @macrocallback macrocallback_multi(integer) :: integer
+          @macrocallback macrocallback_multi(atom) :: atom
+
           @doc "Function doc"
           @doc since: "1.2.3", color: :red
           @doc color: :blue, stable: true
@@ -210,6 +218,7 @@ defmodule Kernel.DocsTest do
       [
         callback_bar,
         callback_baz,
+        callback_multi,
         callback_foo,
         function_struct_0,
         function_struct_1,
@@ -219,6 +228,7 @@ defmodule Kernel.DocsTest do
         function_nullary,
         function_qux,
         guard_is_zero,
+        macrocallback_multi,
         macrocallback_qux,
         type_bar,
         type_foo
@@ -231,6 +241,9 @@ defmodule Kernel.DocsTest do
               %{since: "1.2.3", deprecated: "use baz/2 instead", color: :blue, stable: true}} =
                callback_foo
 
+      assert {{:callback, :callback_multi, 1}, _, [], %{"en" => "Callback with multiple clauses"},
+              %{}} = callback_multi
+
       assert {{:function, :__struct__, 0}, _, ["%Kernel.DocsTest.SampleDocs{}"],
               %{"en" => "My struct"}, %{}} = function_struct_0
 
@@ -259,6 +272,9 @@ defmodule Kernel.DocsTest do
       assert {{:macro, :is_zero, 1}, _, ["is_zero(v)"], %{"en" => "A guard"}, %{guard: true}} =
                guard_is_zero
 
+      assert {{:macrocallback, :macrocallback_multi, 1}, _, [],
+              %{"en" => "Macrocallback with multiple clauses"}, %{}} = macrocallback_multi
+
       assert {{:macrocallback, :qux, 1}, _, [], %{"en" => "Macrocallback doc"}, %{}} =
                macrocallback_qux
 
diff --git a/lib/elixir/test/elixir/kernel/errors_test.exs b/lib/elixir/test/elixir/kernel/errors_test.exs
index 50d827618..62dbefec5 100644
--- a/lib/elixir/test/elixir/kernel/errors_test.exs
+++ b/lib/elixir/test/elixir/kernel/errors_test.exs
@@ -110,11 +110,11 @@ defmodule Kernel.ErrorsTest do
 
     Got:
 
-        "foó" \(codepoints 0066 006F 006F 0301\)
+        "foó" \(codepoints 0x0066 0x006F 0x006F 0x0301\)
 
     Expected:
 
-        "foó" \(codepoints 0066 006F 00F3\)
+        "foó" \(codepoints 0x0066 0x006F 0x00F3\)
 
     """
 
@@ -750,8 +750,7 @@ defmodule Kernel.ErrorsTest do
 
   test "already compiled module" do
     assert_eval_raise ArgumentError,
-                      "could not call eval_quoted with argument Record " <>
-                        "because the module is already compiled",
+                      "could not call Module.eval_quoted/4 because the module Record is already compiled",
                       'Module.eval_quoted Record, quote(do: 1), [], file: __ENV__.file'
   end
 
diff --git a/lib/elixir/test/elixir/list_test.exs b/lib/elixir/test/elixir/list_test.exs
index 71242fc2e..b0a470a43 100644
--- a/lib/elixir/test/elixir/list_test.exs
+++ b/lib/elixir/test/elixir/list_test.exs
@@ -258,13 +258,27 @@ defmodule ListTest do
     end
   end
 
-  describe "ascii_printable?/1" do
+  describe "ascii_printable?/2" do
     test "proper lists without limit" do
       assert List.ascii_printable?([])
       assert List.ascii_printable?('abc')
       refute(List.ascii_printable?('abc' ++ [0]))
-      assert List.ascii_printable?('abc \a\b\e\f\n\r\t\v def')
       refute List.ascii_printable?('mañana')
+
+      printable_chars = '\a\b\t\n\v\f\r\e' ++ Enum.to_list(32..126)
+      non_printable_chars = '🌢áéíóúźç©¢🂭'
+
+      assert List.ascii_printable?(printable_chars)
+
+      for char <- printable_chars do
+        assert List.ascii_printable?([char])
+      end
+
+      refute List.ascii_printable?(non_printable_chars)
+
+      for char <- non_printable_chars do
+        refute List.ascii_printable?([char])
+      end
     end
 
     test "proper lists with limit" do
@@ -274,6 +288,7 @@ defmodule ListTest do
 
     test "improper lists" do
       refute List.ascii_printable?('abc' ++ ?d)
+      assert List.ascii_printable?('abc' ++ ?d, 3)
     end
   end
 end
diff --git a/lib/elixir/test/elixir/macro_test.exs b/lib/elixir/test/elixir/macro_test.exs
index 29bed08a8..f2f841e34 100644
--- a/lib/elixir/test/elixir/macro_test.exs
+++ b/lib/elixir/test/elixir/macro_test.exs
@@ -236,8 +236,8 @@ defmodule MacroTest do
 
     test "does not expand module attributes" do
       message =
-        "could not call get_attribute with argument #{inspect(__MODULE__)} " <>
-          "because the module is already compiled"
+        "could not call Module.get_attribute/2 because the module #{inspect(__MODULE__)} " <>
+          "is already compiled. Use the Module.__info__/1 callback or Code.fetch_docs/1 instead"
 
       assert_raise ArgumentError, message, fn ->
         Macro.expand_once(quote(do: @foo), __ENV__)
@@ -788,7 +788,7 @@ defmodule MacroTest do
       Macro.pipe(1, quote(do: unquote()), 0)
     end
 
-    # TODO: restore this test when we drop unary operator support in pipes
+    # TODO: Restore this test when we drop unary operator support in pipes
     # assert_raise ArgumentError, ~r"cannot pipe 1 into \+1", fn ->
     #   Macro.pipe(1, quote(do: + 1), 0)
     # end
diff --git a/lib/elixir/test/elixir/module_test.exs b/lib/elixir/test/elixir/module_test.exs
index 2eec3caff..092d18331 100644
--- a/lib/elixir/test/elixir/module_test.exs
+++ b/lib/elixir/test/elixir/module_test.exs
@@ -259,12 +259,24 @@ defmodule ModuleTest do
     assert {:module, :root_defmodule, _, _} = result
   end
 
-  test "defmodule with alias as atom" do
+  test "does not leak alias from atom" do
     defmodule :"Elixir.ModuleTest.RawModule" do
       def hello, do: :world
     end
 
-    assert RawModule.hello() == :world
+    refute __ENV__.aliases[Elixir.ModuleTest]
+    refute __ENV__.aliases[Elixir.RawModule]
+    assert ModuleTest.RawModule.hello() == :world
+  end
+
+  test "does not leak alias from non-atom alias" do
+    defmodule __MODULE__.NonAtomAlias do
+      def hello, do: :world
+    end
+
+    refute __ENV__.aliases[Elixir.ModuleTest]
+    refute __ENV__.aliases[Elixir.NonAtomAlias]
+    assert Elixir.ModuleTest.NonAtomAlias.hello() == :world
   end
 
   test "create" do
@@ -414,4 +426,14 @@ defmodule ModuleTest do
   after
     purge(Foo)
   end
+
+  test "raise when called with already compiled module" do
+    message =
+      "could not call Module.get_attribute/2 because the module Enum is already compiled. " <>
+        "Use the Module.__info__/1 callback or Code.fetch_docs/1 instead"
+
+    assert_raise ArgumentError, message, fn ->
+      Module.get_attribute(Enum, :moduledoc)
+    end
+  end
 end
diff --git a/lib/elixir/test/elixir/protocol_test.exs b/lib/elixir/test/elixir/protocol_test.exs
index 867b8a88e..13e4d8a33 100644
--- a/lib/elixir/test/elixir/protocol_test.exs
+++ b/lib/elixir/test/elixir/protocol_test.exs
@@ -102,7 +102,7 @@ defmodule ProtocolTest do
   end
 
   test "protocol not implemented" do
-    message = "protocol ProtocolTest.Sample not implemented for :foo"
+    message = "protocol ProtocolTest.Sample not implemented for :foo of type Atom"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       sample = Sample
@@ -431,8 +431,8 @@ defmodule Protocol.ConsolidationTest do
 
   test "protocol not implemented" do
     message =
-      "protocol Protocol.ConsolidationTest.Sample not implemented for :foo. " <>
-        "This protocol is implemented for: Protocol.ConsolidationTest.ImplStruct"
+      "protocol Protocol.ConsolidationTest.Sample not implemented for :foo of type Atom. " <>
+        "This protocol is implemented for the following type(s): Protocol.ConsolidationTest.ImplStruct"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       sample = Sample
diff --git a/lib/elixir/test/elixir/string/chars_test.exs b/lib/elixir/test/elixir/string/chars_test.exs
index eebbbbb64..02a397a48 100644
--- a/lib/elixir/test/elixir/string/chars_test.exs
+++ b/lib/elixir/test/elixir/string/chars_test.exs
@@ -99,9 +99,13 @@ end
 defmodule String.Chars.ErrorsTest do
   use ExUnit.Case, async: true
 
+  defmodule Foo do
+    defstruct foo: "bar"
+  end
+
   test "bitstring" do
     message =
-      "protocol String.Chars not implemented for <<0, 1::size(4)>>, cannot convert a bitstring to a string"
+      "protocol String.Chars not implemented for <<0, 1::size(4)>> of type BitString, cannot convert a bitstring to a string"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       to_string(<<1::size(12)-integer-signed>>)
@@ -109,7 +113,7 @@ defmodule String.Chars.ErrorsTest do
   end
 
   test "tuple" do
-    message = "protocol String.Chars not implemented for {1, 2, 3}"
+    message = "protocol String.Chars not implemented for {1, 2, 3} of type Tuple"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       to_string({1, 2, 3})
@@ -117,7 +121,7 @@ defmodule String.Chars.ErrorsTest do
   end
 
   test "PID" do
-    message = ~r"^protocol String\.Chars not implemented for #PID<.+?>$"
+    message = ~r"^protocol String\.Chars not implemented for #PID<.+?> of type PID$"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       to_string(self())
@@ -125,7 +129,7 @@ defmodule String.Chars.ErrorsTest do
   end
 
   test "ref" do
-    message = ~r"^protocol String\.Chars not implemented for #Reference<.+?>$"
+    message = ~r"^protocol String\.Chars not implemented for #Reference<.+?> of type Reference$"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       to_string(make_ref()) == ""
@@ -133,7 +137,7 @@ defmodule String.Chars.ErrorsTest do
   end
 
   test "function" do
-    message = ~r"^protocol String\.Chars not implemented for #Function<.+?>$"
+    message = ~r"^protocol String\.Chars not implemented for #Function<.+?> of type Function$"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       to_string(fn -> nil end)
@@ -142,10 +146,19 @@ defmodule String.Chars.ErrorsTest do
 
   test "port" do
     [port | _] = Port.list()
-    message = ~r"^protocol String\.Chars not implemented for #Port<.+?>$"
+    message = ~r"^protocol String\.Chars not implemented for #Port<.+?> of type Port$"
 
     assert_raise Protocol.UndefinedError, message, fn ->
       to_string(port)
     end
   end
+
+  test "user-defined struct" do
+    message =
+      "protocol String\.Chars not implemented for %String.Chars.ErrorsTest.Foo{foo: \"bar\"} of type String.Chars.ErrorsTest.Foo (a struct)"
+
+    assert_raise Protocol.UndefinedError, message, fn ->
+      to_string(%Foo{})
+    end
+  end
 end
diff --git a/lib/elixir/test/elixir/supervisor/spec_test.exs b/lib/elixir/test/elixir/supervisor/spec_test.exs
index f295623ee..25d3d86e4 100644
--- a/lib/elixir/test/elixir/supervisor/spec_test.exs
+++ b/lib/elixir/test/elixir/supervisor/spec_test.exs
@@ -52,7 +52,7 @@ defmodule Supervisor.SpecTest do
     assert supervise(children, options) == {:ok, {{:one_for_all, 1, 1}, children}}
   end
 
-  test "supervise/2 with duplicated ids" do
+  test "supervise/2 with duplicated IDs" do
     children = [worker(GenEvent, []), worker(GenEvent, [])]
 
     assert_raise ArgumentError, fn ->
diff --git a/lib/elixir/test/elixir/system_test.exs b/lib/elixir/test/elixir/system_test.exs
index 59f4fa199..f0a234b89 100644
--- a/lib/elixir/test/elixir/system_test.exs
+++ b/lib/elixir/test/elixir/system_test.exs
@@ -38,6 +38,10 @@ defmodule SystemTest do
     assert System.endianness() == System.compiled_endianness()
   end
 
+  test "pid/0" do
+    assert is_binary(System.pid())
+  end
+
   test "argv/0" do
     list = elixir('-e "IO.inspect System.argv" -- -o opt arg1 arg2 --long-opt 10')
     {args, _} = Code.eval_string(list, [])
@@ -114,8 +118,6 @@ defmodule SystemTest do
         assert {"hello\r\n", 0} =
                  System.cmd(Path.join(File.cwd!(), @echo), ~w[/c echo hello], [{:arg0, "echo"}])
       end)
-    after
-      File.rm_rf!(tmp_path(@echo))
     end
   end
 
@@ -157,8 +159,6 @@ defmodule SystemTest do
         assert {"hello\n", 0} =
                  System.cmd(Path.join(File.cwd!(), @echo), ["hello"], [{:arg0, "echo"}])
       end)
-    after
-      File.rm_rf!(tmp_path(@echo))
     end
   end
 
diff --git a/lib/elixir/test/elixir/test_helper.exs b/lib/elixir/test/elixir/test_helper.exs
index ff8f3fd72..b7865e44f 100644
--- a/lib/elixir/test/elixir/test_helper.exs
+++ b/lib/elixir/test/elixir/test_helper.exs
@@ -24,7 +24,7 @@ defmodule PathHelpers do
   end
 
   def elixir(args) do
-    runcmd(elixir_executable(), args)
+    run_cmd(elixir_executable(), args)
   end
 
   def elixir_executable do
@@ -32,7 +32,7 @@ defmodule PathHelpers do
   end
 
   def elixirc(args) do
-    runcmd(elixirc_executable(), args)
+    run_cmd(elixirc_executable(), args)
   end
 
   def elixirc_executable do
@@ -46,12 +46,10 @@ defmodule PathHelpers do
     res
   end
 
-  defp runcmd(executable, args) do
-    :os.cmd(
-      :binary.bin_to_list(
-        "#{executable} #{IO.chardata_to_string(args)}#{redirect_std_err_on_win()}"
-      )
-    )
+  defp run_cmd(executable, args) do
+    '#{executable} #{IO.chardata_to_string(args)}#{redirect_std_err_on_win()}'
+    |> :os.cmd()
+    |> :binary.list_to_bin()
   end
 
   defp executable_path(name) do
diff --git a/lib/elixir/test/elixir/typespec_test.exs b/lib/elixir/test/elixir/typespec_test.exs
index acf3b940e..c06e16800 100644
--- a/lib/elixir/test/elixir/typespec_test.exs
+++ b/lib/elixir/test/elixir/typespec_test.exs
@@ -787,6 +787,17 @@ defmodule TypespecTest do
       end
     end
 
+    test "spec_to_callback/2" do
+      bytecode =
+        test_module do
+          @spec foo() :: term()
+          def foo(), do: :ok
+          Kernel.Typespec.spec_to_callback(__MODULE__, {:foo, 0})
+        end
+
+      assert specs(bytecode) == callbacks(bytecode)
+    end
+
     test "@spec(spec)" do
       bytecode =
         test_module do
diff --git a/lib/elixir/test/elixir/version_test.exs b/lib/elixir/test/elixir/version_test.exs
index c7e1c474a..eb0f2c663 100644
--- a/lib/elixir/test/elixir/version_test.exs
+++ b/lib/elixir/test/elixir/version_test.exs
@@ -55,6 +55,10 @@ defmodule VersionTest do
     assert Parser.lexer(">2.4.0", []) == [:>, "2.4.0"]
     assert Parser.lexer("> 2.4.0", []) == [:>, "2.4.0"]
     assert Parser.lexer("    >     2.4.0", []) == [:>, "2.4.0"]
+    assert Parser.lexer(" or 2.1.0", []) == [:||, :==, "2.1.0"]
+    assert Parser.lexer(" and 2.1.0", []) == [:&&, :==, "2.1.0"]
+    assert Parser.lexer(">= 2.0.0 and < 2.1.0", []) == [:>=, "2.0.0", :&&, :<, "2.1.0"]
+    assert Parser.lexer(">= 2.0.0 or < 2.1.0", []) == [:>=, "2.0.0", :||, :<, "2.1.0"]
   end
 
   test "parse/1" do
@@ -87,6 +91,8 @@ defmodule VersionTest do
     assert Version.parse("2.3.00") == :error
     assert Version.parse("2.03.0") == :error
     assert Version.parse("02.3.0") == :error
+    assert Version.parse("0. 0.0") == :error
+    assert Version.parse("0.1.0-&&pre") == :error
   end
 
   test "Kernel.to_string/1" do
diff --git a/lib/ex_unit/lib/ex_unit.ex b/lib/ex_unit/lib/ex_unit.ex
index 1211bafdb..abea26d9f 100644
--- a/lib/ex_unit/lib/ex_unit.ex
+++ b/lib/ex_unit/lib/ex_unit.ex
@@ -92,14 +92,14 @@ defmodule ExUnit do
       * `:name` - the test name
       * `:module` - the test module
       * `:state` - the finished test state (see `t:ExUnit.state/0`)
-      * `:time` - the time to run the test
+      * `:time` - the duration in microseconds of the test's runtime
       * `:tags` - the test tags
       * `:logs` - the captured logs
 
     """
     defstruct [:name, :case, :module, :state, time: 0, tags: %{}, logs: ""]
 
-    # TODO: Remove the `:case` field on Elixir v2.0
+    # TODO: Remove the `:case` field on v2.0
     @type t :: %__MODULE__{
             name: atom,
             case: module,
@@ -128,7 +128,7 @@ defmodule ExUnit do
   end
 
   defmodule TestCase do
-    # TODO: Remove this on Elixir v2.0 as well as uses of it.
+    # TODO: Remove this module on v2.0 (it has been replacede by TestModule)
     @moduledoc false
     defstruct [:name, :state, tests: []]
 
diff --git a/lib/ex_unit/lib/ex_unit/assertions.ex b/lib/ex_unit/lib/ex_unit/assertions.ex
index daaf6c5a5..b87e176ec 100644
--- a/lib/ex_unit/lib/ex_unit/assertions.ex
+++ b/lib/ex_unit/lib/ex_unit/assertions.ex
@@ -201,7 +201,7 @@ defmodule ExUnit.Assertions do
   The correct way to write the refutation above is to use
   `Kernel.match?/2`:
 
-      refute match? {:ok, _}, some_function_that_returns_error_tuple()
+      refute match?({:ok, _}, some_function_that_returns_error_tuple())
 
   ## Examples
 
@@ -413,10 +413,10 @@ defmodule ExUnit.Assertions do
 
   ## Examples
 
-      send self(), :hello
+      send(self(), :hello)
       assert_received :hello
 
-      send self(), :bye
+      send(self(), :bye)
       assert_received :hello, "Oh No!"
       ** (ExUnit.AssertionError) Oh No!
       Process mailbox:
@@ -424,7 +424,7 @@ defmodule ExUnit.Assertions do
 
   You can also match against specific patterns:
 
-      send self(), {:hello, "world"}
+      send(self(), {:hello, "world"})
       assert_received {:hello, _}
 
   """
@@ -637,7 +637,7 @@ defmodule ExUnit.Assertions do
     {name, meta, [expr, [do: clauses]]}
   end
 
-  @doc """
+  @doc ~S"""
   Asserts the `exception` is raised during `function` execution with
   the expected `message`, which can be a `Regex` or an exact `String`.
   Returns the rescued exception, fails otherwise.
@@ -758,7 +758,7 @@ defmodule ExUnit.Assertions do
 
   ## Examples
 
-      assert catch_throw(throw 1) == 1
+      assert catch_throw(throw(1)) == 1
 
   """
   defmacro catch_throw(expression) do
@@ -772,7 +772,7 @@ defmodule ExUnit.Assertions do
 
   ## Examples
 
-      assert catch_exit(exit 1) == 1
+      assert catch_exit(exit(1)) == 1
 
   To assert exits from linked processes started from the test, trap exits
   with `Process.flag/2` and assert the exit message with `assert_received/2`.
@@ -793,7 +793,7 @@ defmodule ExUnit.Assertions do
 
   ## Examples
 
-      assert catch_error(error 1) == 1
+      assert catch_error(error(1)) == 1
 
   """
   defmacro catch_error(expression) do
@@ -861,10 +861,10 @@ defmodule ExUnit.Assertions do
 
   ## Examples
 
-      send self(), :hello
+      send(self(), :hello)
       refute_received :bye
 
-      send self(), :hello
+      send(self(), :hello)
       refute_received :hello, "Oh No!"
       ** (ExUnit.AssertionError) Oh No!
       Process mailbox:
@@ -940,7 +940,7 @@ defmodule ExUnit.Assertions do
 
   ## Examples
 
-      flunk "This should raise an error"
+      flunk("This should raise an error")
 
   """
   @spec flunk :: no_return
diff --git a/lib/ex_unit/lib/ex_unit/callbacks.ex b/lib/ex_unit/lib/ex_unit/callbacks.ex
index ac4616785..fc7350c78 100644
--- a/lib/ex_unit/lib/ex_unit/callbacks.ex
+++ b/lib/ex_unit/lib/ex_unit/callbacks.ex
@@ -316,12 +316,12 @@ defmodule ExUnit.Callbacks do
   and the available specification keys.
 
   The advantage of starting a process under the test supervisor is that
-  it is guaranteed to exit before the next test starts. Furthermore,
-  because the child process is supervised, it will be restarted in case
-  of crashes according to the `:restart` strategy in the child
-  specification, even if stopped manually. Therefore, to guarantee a
-  process started with `start_supervised/2` terminates without restarts,
-  see `stop_supervised/1`.
+  it is guaranteed to exit before the next test starts. Therefore, you
+  don't need to remove the process at the end of your tests via
+  `stop_supervised/1`. You only need to use `stop_supervised/1 ` if you
+  want to remove a process from the supervision tree in the middle of a
+  test, as simply shutting down the process would cause it to be restarted
+  according to its `:restart` value.
 
   This function returns `{:ok, pid}` in case of success, otherwise it
   returns `{:error, reason}`.
diff --git a/lib/ex_unit/lib/ex_unit/capture_io.ex b/lib/ex_unit/lib/ex_unit/capture_io.ex
index 963316d6f..15e4ca269 100644
--- a/lib/ex_unit/lib/ex_unit/capture_io.ex
+++ b/lib/ex_unit/lib/ex_unit/capture_io.ex
@@ -10,15 +10,14 @@ defmodule ExUnit.CaptureIO do
         import ExUnit.CaptureIO
 
         test "example" do
-          assert capture_io(fn ->
-            IO.puts "a"
-          end) == "a\n"
+          assert capture_io(fn -> IO.puts("a") end) == "a\n"
         end
 
         test "checking the return value and the IO output" do
           fun = fn ->
-            assert Enum.each(["some", "example"], &(IO.puts &1)) == :ok
+            assert Enum.each(["some", "example"], &IO.puts(&1)) == :ok
           end
+
           assert capture_io(fun) == "some\nexample\n"
           # tip: or use only: "capture_io(fun)" to silence the IO output (so only assert the return value)
         end
@@ -32,18 +31,18 @@ defmodule ExUnit.CaptureIO do
   Returns the binary which is the captured output.
 
   By default, `capture_io` replaces the `group_leader` (`:stdio`)
-  for the current process. However, the capturing of any other
-  named device, such as `:stderr`, is also possible globally by
-  giving the registered device name explicitly as an argument.
+  for the current process. Capturing the group leader is done per
+  process and therefore can be done concurrently.
 
-  Note that when capturing something other than `:stdio`,
-  the test should run with `async: false`.
+  However, the capturing of any other named device, such as `:stderr`,
+  happens globally and requires `async: false`.
 
   When capturing `:stdio`, if the `:capture_prompt` option is `false`,
   prompts (specified as arguments to `IO.get*` functions) are not
   captured.
 
-  A developer can set a string as an input. The default input is `:eof`.
+  A developer can set a string as an input. The default input
+  is an empty string (which is equivalent to `:eof`).
 
   ## Examples
 
@@ -54,14 +53,14 @@ defmodule ExUnit.CaptureIO do
       true
 
       iex> capture_io("this is input", fn ->
-      ...>   input = IO.gets ">"
-      ...>   IO.write input
-      ...> end) == ">this is input"
+      ...>   input = IO.gets("> ")
+      ...>   IO.write(input)
+      ...> end) == "> this is input"
       true
 
       iex> capture_io([input: "this is input", capture_prompt: false], fn ->
-      ...>   input = IO.gets ">"
-      ...>   IO.write input
+      ...>   input = IO.gets("> ")
+      ...>   IO.write(input)
       ...> end) == "this is input"
       true
 
@@ -73,34 +72,36 @@ defmodule ExUnit.CaptureIO do
   and use `ExUnit.Assertions.assert_received/2` to match on the results:
 
       capture_io([input: "this is input", capture_prompt: false], fn ->
-        send self(), {:block_result, 42}
+        send(self(), {:block_result, 42})
         # ...
       end)
 
       assert_received {:block_result, 42}
 
   """
-  def capture_io(fun) do
-    do_capture_io(:standard_io, [], fun)
+  def capture_io(fun) when is_function(fun, 0) do
+    capture_io(:stdio, [], fun)
   end
 
-  def capture_io(device, fun) when is_atom(device) do
+  def capture_io(device, fun) when is_atom(device) and is_function(fun, 0) do
     capture_io(device, [], fun)
   end
 
-  def capture_io(input, fun) when is_binary(input) do
-    capture_io(:standard_io, [input: input], fun)
+  def capture_io(input, fun) when is_binary(input) and is_function(fun, 0) do
+    capture_io(:stdio, [input: input], fun)
   end
 
-  def capture_io(options, fun) when is_list(options) do
-    capture_io(:standard_io, options, fun)
+  def capture_io(options, fun) when is_list(options) and is_function(fun, 0) do
+    capture_io(:stdio, options, fun)
   end
 
-  def capture_io(device, input, fun) when is_binary(input) do
+  def capture_io(device, input, fun)
+      when is_atom(device) and is_binary(input) and is_function(fun, 0) do
     capture_io(device, [input: input], fun)
   end
 
-  def capture_io(device, options, fun) when is_list(options) do
+  def capture_io(device, options, fun)
+      when is_atom(device) and is_list(options) and is_function(fun, 0) do
     do_capture_io(map_dev(device), options, fun)
   end
 
diff --git a/lib/ex_unit/lib/ex_unit/capture_log.ex b/lib/ex_unit/lib/ex_unit/capture_log.ex
index 1db8cb7f2..7608b79a9 100644
--- a/lib/ex_unit/lib/ex_unit/capture_log.ex
+++ b/lib/ex_unit/lib/ex_unit/capture_log.ex
@@ -12,17 +12,19 @@ defmodule ExUnit.CaptureLog do
 
         test "example" do
           assert capture_log(fn ->
-            Logger.error "log msg"
-          end) =~ "log msg"
+                   Logger.error("log msg")
+                 end) =~ "log msg"
         end
 
         test "check multiple captures concurrently" do
           fun = fn ->
             for msg <- ["hello", "hi"] do
-              assert capture_log(fn -> Logger.error msg end) =~ msg
+              assert capture_log(fn -> Logger.error(msg) end) =~ msg
             end
-            Logger.debug "testing"
+
+            Logger.debug("testing")
           end
+
           assert capture_log(fun) =~ "hello"
           assert capture_log(fun) =~ "testing"
         end
diff --git a/lib/ex_unit/lib/ex_unit/case_template.ex b/lib/ex_unit/lib/ex_unit/case_template.ex
index 2f6beb51c..10fa4ecb0 100644
--- a/lib/ex_unit/lib/ex_unit/case_template.ex
+++ b/lib/ex_unit/lib/ex_unit/case_template.ex
@@ -14,7 +14,7 @@ defmodule ExUnit.CaseTemplate do
         use ExUnit.CaseTemplate
 
         setup do
-          IO.puts "This will run before each test that uses this case"
+          IO.puts("This will run before each test that uses this case")
         end
       end
 
diff --git a/lib/ex_unit/lib/ex_unit/doc_test.ex b/lib/ex_unit/lib/ex_unit/doc_test.ex
index 47a6de026..c0a845c04 100644
--- a/lib/ex_unit/lib/ex_unit/doc_test.ex
+++ b/lib/ex_unit/lib/ex_unit/doc_test.ex
@@ -39,9 +39,9 @@ defmodule ExUnit.DocTest do
 
   Expressions on multiple lines are also supported:
 
-      iex> Enum.map [1, 2, 3], fn(x) ->
+      iex> Enum.map([1, 2, 3], fn x ->
       ...>   x * 2
-      ...> end
+      ...> end)
       [2, 4, 6]
 
   Multiple results can be checked within the same test:
@@ -126,7 +126,7 @@ defmodule ExUnit.DocTest do
 
   You can also showcase expressions raising an exception, for example:
 
-      iex(1)> String.to_atom((fn() -> 1 end).())
+      iex(1)> String.to_atom((fn -> 1 end).())
       ** (ArgumentError) argument error
 
   What DocTest will be looking for is a line starting with `** (` and it
@@ -147,6 +147,8 @@ defmodule ExUnit.DocTest do
   suite run.
   """
 
+  @opaque_type_regex ~r/#[\w\.]+</
+
   defmodule Error do
     defexception [:message]
 
@@ -279,14 +281,7 @@ defmodule ExUnit.DocTest do
         test_case_content(expr, expected, location, stack)
       end)
 
-    quote do
-      unquote_splicing(test_import(module, do_import))
-      unquote(gen_code_for_tests(tests, whole_expr(exprs), stack))
-    end
-  end
-
-  defp whole_expr(exprs) do
-    Enum.map_join(exprs, "\n", &elem(&1, 0))
+    {:__block__, [], test_import(module, do_import) ++ tests}
   end
 
   defp multiple_exceptions?(exprs) do
@@ -296,28 +291,6 @@ defmodule ExUnit.DocTest do
     end) > 1
   end
 
-  defp gen_code_for_tests(tests, whole_expr, stack) do
-    quote do
-      stack = unquote(stack)
-
-      try do
-        # Put all tests into one context
-        (unquote_splicing(tests))
-      rescue
-        e in ExUnit.AssertionError ->
-          reraise e, stack
-
-        error ->
-          message =
-            "Doctest failed: got #{inspect(error.__struct__)} with message " <>
-              inspect(Exception.message(error))
-
-          error = [message: message, expr: unquote(String.trim(whole_expr))]
-          reraise ExUnit.AssertionError, error, __STACKTRACE__
-      end
-    end
-  end
-
   defp test_case_content(expr, {:test, expected}, location, stack) do
     expr_ast = string_to_quoted(location, stack, expr)
     expected_ast = string_to_quoted(location, stack, expected)
@@ -419,6 +392,19 @@ defmodule ExUnit.DocTest do
         ex_message = "(#{inspect(e.__struct__)}) #{Exception.message(e)}"
         message = "Doctest did not compile, got: #{ex_message}"
 
+        message =
+          if e.__struct__ == TokenMissingError and expr =~ Regex.recompile!(@opaque_type_regex) do
+            message <>
+              """
+              . If you are planning to assert on the result of an iex> expression \
+              which contains a value inspected as #Name<...>, please make sure \
+              the inspected value is placed at the beginning of the expression; \
+              otherwise Elixir will treat it as a comment due to the leading sign #.\
+              """
+          else
+            message
+          end
+
         opts =
           if String.valid?(expr) do
             [message: message, expr: String.trim(expr)]
diff --git a/lib/ex_unit/lib/ex_unit/event_manager.ex b/lib/ex_unit/lib/ex_unit/event_manager.ex
index c9b9e5953..38ffa4067 100644
--- a/lib/ex_unit/lib/ex_unit/event_manager.ex
+++ b/lib/ex_unit/lib/ex_unit/event_manager.ex
@@ -4,8 +4,6 @@ defmodule ExUnit.EventManager do
 
   @typep manager :: {supervisor_manager :: pid, event_manager :: pid}
 
-  # TODO: Remove support for GenEvent formatters on 2.0
-
   @doc """
   Starts an event manager that publishes events during the suite run.
 
@@ -56,13 +54,13 @@ defmodule ExUnit.EventManager do
   end
 
   def module_started(manager, test_module) do
-    # TODO: Remove case_started in Elixir v2.0
+    # TODO: Remove case_started on v2.0
     notify(manager, {:case_started, Map.put(test_module, :__struct__, ExUnit.TestCase)})
     notify(manager, {:module_started, test_module})
   end
 
   def module_finished(manager, test_module) do
-    # TODO: Remove case_finished in Elixir v2.0
+    # TODO: Remove case_finished on v2.0
     notify(manager, {:case_finished, Map.put(test_module, :__struct__, ExUnit.TestCase)})
     notify(manager, {:module_finished, test_module})
   end
diff --git a/lib/ex_unit/lib/ex_unit/failures_manifest.ex b/lib/ex_unit/lib/ex_unit/failures_manifest.ex
index f570db205..755029bd9 100644
--- a/lib/ex_unit/lib/ex_unit/failures_manifest.ex
+++ b/lib/ex_unit/lib/ex_unit/failures_manifest.ex
@@ -2,7 +2,7 @@ defmodule ExUnit.FailuresManifest do
   @moduledoc false
 
   @type test_id :: {module, name :: atom}
-  @opaque t :: %{test_id => test_file :: Path.t()}
+  @opaque t :: %{optional(test_id) => test_file :: Path.t()}
 
   @manifest_vsn 1
 
diff --git a/lib/ex_unit/lib/ex_unit/filters.ex b/lib/ex_unit/lib/ex_unit/filters.ex
index 21f966316..312533c9e 100644
--- a/lib/ex_unit/lib/ex_unit/filters.ex
+++ b/lib/ex_unit/lib/ex_unit/filters.ex
@@ -14,16 +14,36 @@ defmodule ExUnit.Filters do
   on the command line) includes a line number filter, and if so returns the
   appropriate ExUnit configuration options.
   """
-  @spec parse_path(String.t()) :: {String.t(), any}
+  @spec parse_path(String.t()) :: {String.t(), Keyword.t()}
   def parse_path(file) do
-    {paths, [line]} = file |> String.split(":") |> Enum.split(-1)
+    case extract_line_numbers(file) do
+      {path, []} -> {path, []}
+      {path, line_numbers} -> {path, exclude: [:test], include: line_numbers}
+    end
+  end
+
+  defp extract_line_numbers(file) do
+    case String.split(file, ":") do
+      [part] ->
+        {part, []}
+
+      parts ->
+        {reversed_line_numbers, reversed_path_parts} =
+          parts
+          |> Enum.reverse()
+          |> Enum.split_while(&match?({_, ""}, Integer.parse(&1)))
+
+        line_numbers =
+          reversed_line_numbers
+          |> Enum.reverse()
+          |> Enum.map(&{:line, &1})
 
-    case Integer.parse(line) do
-      {_, ""} ->
-        {Enum.join(paths, ":"), exclude: [:test], include: [line: line]}
+        path =
+          reversed_path_parts
+          |> Enum.reverse()
+          |> Enum.join(":")
 
-      _ ->
-        {file, []}
+        {path, line_numbers}
     end
   end
 
@@ -96,7 +116,7 @@ defmodule ExUnit.Filters do
 
     * A set of files that contain tests that failed the last time they ran.
       The paths are absolute paths.
-    * A set of test ids that failed the last time they ran
+    * A set of test IDs that failed the last time they ran
 
   """
   @spec failure_info(Path.t()) :: {MapSet.t(Path.t()), MapSet.t(FailuresManifest.test_id())}
diff --git a/lib/ex_unit/lib/ex_unit/formatter.ex b/lib/ex_unit/lib/ex_unit/formatter.ex
index 80d53c043..5f1a20734 100644
--- a/lib/ex_unit/lib/ex_unit/formatter.ex
+++ b/lib/ex_unit/lib/ex_unit/formatter.ex
@@ -153,7 +153,6 @@ defmodule ExUnit.Formatter do
     |> make_into_lines(counter_padding)
   end
 
-  # TODO: Remove on 2.0
   @doc false
   @deprecated "Use ExUnit.Formatter.format_test_all_failure/5 instead"
   def format_test_case_failure(test_case, failures, counter, width, formatter) do
diff --git a/lib/ex_unit/test/ex_unit/doc_test_test.exs b/lib/ex_unit/test/ex_unit/doc_test_test.exs
index 0e6913407..d3a5bb684 100644
--- a/lib/ex_unit/test/ex_unit/doc_test_test.exs
+++ b/lib/ex_unit/test/ex_unit/doc_test_test.exs
@@ -196,6 +196,12 @@ defmodule ExUnit.DocTestTest.Invalid do
   false
   """
   def invalid_utf8, do: :ok
+
+  @doc """
+      iex> {:ok, MapSet.new([1, 2, 3])}
+      {:ok, #MapSet<[1, 2, 3]>}
+  """
+  def misplaced_opaque_type, do: :ok
 end
 |> write_beam
 
@@ -440,7 +446,7 @@ defmodule ExUnit.DocTestTest do
       doctest ExUnit.DocTestTest.Invalid
     end
 
-    doctest_line = 440
+    doctest_line = __ENV__.line - 3
 
     ExUnit.configure(seed: 0, colors: [enabled: false])
     ExUnit.Server.modules_loaded()
@@ -477,12 +483,10 @@ defmodule ExUnit.DocTestTest do
                   test/ex_unit/doc_test_test.exs:144: ExUnit.DocTestTest.Invalid (module)
            """
 
-    # The stacktrace points to the cause of the error
     assert output =~ """
              4) doctest module ExUnit.DocTestTest.Invalid (4) (ExUnit.DocTestTest.ActuallyCompiled)
                 test/ex_unit/doc_test_test.exs:#{doctest_line}
-                Doctest failed: got UndefinedFunctionError with message "function Hello.world/0 is undefined (module Hello is not available)"
-                code: Hello.world
+                ** (UndefinedFunctionError) function Hello.world/0 is undefined (module Hello is not available)
                 stacktrace:
                   Hello.world()
                   (for doctest at) test/ex_unit/doc_test_test.exs:147: (test)
@@ -558,7 +562,16 @@ defmodule ExUnit.DocTestTest do
            """
 
     assert output =~ """
-            12) doctest ExUnit.DocTestTest.Invalid.b/0 (12) (ExUnit.DocTestTest.ActuallyCompiled)
+            12) doctest ExUnit.DocTestTest.Invalid.misplaced_opaque_type/0 (12) (ExUnit.DocTestTest.ActuallyCompiled)
+                test/ex_unit/doc_test_test.exs:#{doctest_line}
+                Doctest did not compile, got: (TokenMissingError) test/ex_unit/doc_test_test.exs:201: missing terminator: } (for "{" starting at line 201). If you are planning to assert on the result of an iex> expression which contains a value inspected as #Name<...>, please make sure the inspected value is placed at the beginning of the expression; otherwise Elixir will treat it as a comment due to the leading sign #.
+                code: {:ok, #MapSet<[1, 2, 3]>}
+                stacktrace:
+                  test/ex_unit/doc_test_test.exs:201: ExUnit.DocTestTest.Invalid (module)
+           """
+
+    assert output =~ """
+            13) doctest ExUnit.DocTestTest.Invalid.b/0 (13) (ExUnit.DocTestTest.ActuallyCompiled)
                 test/ex_unit/doc_test_test.exs:#{doctest_line}
                 Doctest did not compile, got: (SyntaxError) test/ex_unit/doc_test_test.exs:165: syntax error before: '*'
                 code: 1 + * 1
diff --git a/lib/ex_unit/test/ex_unit/failures_manifest_test.exs b/lib/ex_unit/test/ex_unit/failures_manifest_test.exs
index 4456e2d5b..49b4d7dfb 100644
--- a/lib/ex_unit/test/ex_unit/failures_manifest_test.exs
+++ b/lib/ex_unit/test/ex_unit/failures_manifest_test.exs
@@ -26,7 +26,7 @@ defmodule ExUnit.FailuresManifestTest do
   end
 
   describe "failed_test_ids/1" do
-    test "returns the set of failed test ids" do
+    test "returns the set of failed test IDs" do
       manifest =
         new()
         |> put_test(failed_1 = new_test(@failed))
diff --git a/lib/ex_unit/test/ex_unit/filters_test.exs b/lib/ex_unit/test/ex_unit/filters_test.exs
index 720ae4021..943b39654 100644
--- a/lib/ex_unit/test/ex_unit/filters_test.exs
+++ b/lib/ex_unit/test/ex_unit/filters_test.exs
@@ -207,5 +207,25 @@ defmodule ExUnit.FiltersTest do
 
     assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123notreallyalinenumber123") ==
              {"C:\\some\\path.exs:123notreallyalinenumber123", []}
+
+    assert ExUnit.Filters.parse_path("test/some/path.exs:123:456") ==
+             {"test/some/path.exs", [exclude: [:test], include: [line: "123", line: "456"]]}
+
+    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123:456") ==
+             {"C:\\some\\path.exs", [exclude: [:test], include: [line: "123", line: "456"]]}
+
+    assert ExUnit.Filters.parse_path("test/some/path.exs:123notalinenumber123:456") ==
+             {"test/some/path.exs:123notalinenumber123",
+              [exclude: [:test], include: [line: "456"]]}
+
+    assert ExUnit.Filters.parse_path("test/some/path.exs:123:456notalinenumber456") ==
+             {"test/some/path.exs:123:456notalinenumber456", []}
+
+    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123notalinenumber123:456") ==
+             {"C:\\some\\path.exs:123notalinenumber123",
+              [exclude: [:test], include: [line: "456"]]}
+
+    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123:456notalinenumber456") ==
+             {"C:\\some\\path.exs:123:456notalinenumber456", []}
   end
 end
diff --git a/lib/ex_unit/test/ex_unit/supervised_test.exs b/lib/ex_unit/test/ex_unit/supervised_test.exs
index 1b22c78ae..0db70019a 100644
--- a/lib/ex_unit/test/ex_unit/supervised_test.exs
+++ b/lib/ex_unit/test/ex_unit/supervised_test.exs
@@ -71,7 +71,7 @@ defmodule ExUnit.SupervisedTest do
     refute Process.whereis(MyAgent)
   end
 
-  test "starts a supervised process with id checks" do
+  test "starts a supervised process with ID checks" do
     {:ok, pid} = start_supervised({MyAgent, 0})
     assert {:error, {:already_started, ^pid}} = start_supervised({MyAgent, 0})
     assert {:error, {{:already_started, ^pid}, _}} = start_supervised({MyAgent, 0}, id: :another)
diff --git a/lib/iex/lib/iex.ex b/lib/iex/lib/iex.ex
index 720560631..7f75be787 100644
--- a/lib/iex/lib/iex.ex
+++ b/lib/iex/lib/iex.ex
@@ -665,12 +665,12 @@ defmodule IEx do
 
     * `IEx.Helpers.break!/2` - sets up a breakpoint for a given `Mod.fun/arity`
     * `IEx.Helpers.break!/4` - sets up a breakpoint for the given module, function, arity
-    * `IEx.Helpers.breaks/0` - prints all breakpoints and their ids
+    * `IEx.Helpers.breaks/0` - prints all breakpoints and their IDs
     * `IEx.Helpers.continue/0` - continues until the next breakpoint in the same shell
     * `IEx.Helpers.open/0` - opens editor on the current breakpoint
     * `IEx.Helpers.remove_breaks/0` - removes all breakpoints in all modules
     * `IEx.Helpers.remove_breaks/1` - removes all breakpoints in a given module
-    * `IEx.Helpers.reset_break/1` - sets the number of stops on the given id to zero
+    * `IEx.Helpers.reset_break/1` - sets the number of stops on the given ID to zero
     * `IEx.Helpers.reset_break/3` - sets the number of stops on the given module, function, arity to zero
     * `IEx.Helpers.respawn/0` - starts a new shell (breakpoints will ask for permission once more)
     * `IEx.Helpers.whereami/1` - shows the current location
@@ -793,30 +793,14 @@ defmodule IEx do
 
   defp set_expand_fun do
     gl = Process.group_leader()
-    glnode = node(gl)
-
-    expand_fun =
-      if glnode != node() do
-        _ = ensure_module_exists(glnode, IEx.Remsh)
-        IEx.Remsh.expand(node())
-      else
-        &IEx.Autocomplete.expand(&1)
-      end
 
     # expand_fun is not supported by a shell variant
     # on Windows, so we do two IO calls, not caring
     # about the result of the expand_fun one.
-    _ = :io.setopts(gl, expand_fun: expand_fun)
+    _ = :io.setopts(gl, expand_fun: &IEx.Autocomplete.expand/1)
     :io.setopts(gl, binary: true, encoding: :unicode)
   end
 
-  defp ensure_module_exists(node, mod) do
-    unless :rpc.call(node, :code, :is_loaded, [mod]) do
-      {m, b, f} = :code.get_object_code(mod)
-      {:module, _} = :rpc.call(node, :code, :load_binary, [m, f, b])
-    end
-  end
-
   defp run_after_spawn do
     _ = for fun <- Enum.reverse(after_spawn()), do: fun.()
     :ok
diff --git a/lib/iex/lib/iex/autocomplete.ex b/lib/iex/lib/iex/autocomplete.ex
index 0e51e546c..9eeb83ff6 100644
--- a/lib/iex/lib/iex/autocomplete.ex
+++ b/lib/iex/lib/iex/autocomplete.ex
@@ -1,6 +1,12 @@
 defmodule IEx.Autocomplete do
   @moduledoc false
 
+  @doc """
+  The expansion logic.
+
+  Some of the expansion has to be use the current shell
+  environemnt, which is found via the broker.
+  """
   def expand(expr, server \\ IEx.Broker)
 
   def expand('', server) do
diff --git a/lib/iex/lib/iex/cli.ex b/lib/iex/lib/iex/cli.ex
index ec70b0cdd..a10ad53c2 100644
--- a/lib/iex/lib/iex/cli.ex
+++ b/lib/iex/lib/iex/cli.ex
@@ -53,6 +53,13 @@ defmodule IEx.CLI do
     if tty_works?() do
       :user_drv.start([:"tty_sl -c -e", tty_args()])
     else
+      if get_remsh(:init.get_plain_arguments()) do
+        IO.puts(
+          :stderr,
+          "warning: the --remsh option will be ignored because IEx is running on limited shell"
+        )
+      end
+
       :application.set_env(:stdlib, :shell_prompt_func, {__MODULE__, :prompt})
       :user.start()
       local_start()
@@ -141,7 +148,7 @@ defmodule IEx.CLI do
   end
 
   defp options do
-    [dot_iex_path: find_dot_iex(:init.get_plain_arguments())]
+    [dot_iex_path: find_dot_iex(:init.get_plain_arguments()), on_eof: :halt]
   end
 
   defp abort(msg) do
diff --git a/lib/iex/lib/iex/evaluator.ex b/lib/iex/lib/iex/evaluator.ex
index 543924826..e22899313 100644
--- a/lib/iex/lib/iex/evaluator.ex
+++ b/lib/iex/lib/iex/evaluator.ex
@@ -192,7 +192,7 @@ defmodule IEx.Evaluator do
       kind, error ->
         io_result("Error while evaluating: #{path}")
         print_error(kind, error, __STACKTRACE__)
-        System.halt(1)
+        state
     end
   end
 
diff --git a/lib/iex/lib/iex/helpers.ex b/lib/iex/lib/iex/helpers.ex
index 12ff3a967..67c5e14b8 100644
--- a/lib/iex/lib/iex/helpers.ex
+++ b/lib/iex/lib/iex/helpers.ex
@@ -246,7 +246,7 @@ defmodule IEx.Helpers do
   end
 
   @doc """
-  Opens the given module, module/function/arity or `{file, line}`.
+  Opens the given `module`, `module.function/arity`, or `{file, line}`.
 
   This function uses the `ELIXIR_EDITOR` environment variable
   and falls back to `EDITOR` if the former is not available.
@@ -299,14 +299,14 @@ defmodule IEx.Helpers do
 
   @doc """
   Prints the documentation for the given module
-  or for the given function/arity pair.
+  or for the given `function/arity` pair.
 
   ## Examples
 
       iex> h(Enum)
 
-  It also accepts functions in the format `fun/arity`
-  and `module.fun/arity`, for example:
+  It also accepts functions in the format `function/arity`
+  and `module.function/arity`, for example:
 
       iex> h(receive/1)
       iex> h(Enum.all?/2)
@@ -949,9 +949,9 @@ defmodule IEx.Helpers do
 
   @doc """
   Sets the number of pending stops in the breakpoint
-  with the given id to zero.
+  with the given `id` to zero.
 
-  Returns `:ok` if there is such breakpoint id. `:not_found`
+  Returns `:ok` if there is such breakpoint ID. `:not_found`
   otherwise.
 
   Note the module remains "instrumented" on reset. If you would
diff --git a/lib/iex/lib/iex/info.ex b/lib/iex/lib/iex/info.ex
index 11b44af99..42b7dcfc1 100644
--- a/lib/iex/lib/iex/info.ex
+++ b/lib/iex/lib/iex/info.ex
@@ -120,7 +120,7 @@ defimpl IEx.Info, for: List do
     specific_info =
       cond do
         list == [] -> info_list(list)
-        List.ascii_printable?(list) -> info_charlist(list)
+        List.ascii_printable?(list) -> info_printable_charlist(list)
         Keyword.keyword?(list) -> info_kw_list(list)
         List.improper?(list) -> info_improper_list(list)
         true -> info_list(list)
@@ -129,13 +129,12 @@ defimpl IEx.Info, for: List do
     [{"Data type", "List"}] ++ specific_info
   end
 
-  defp info_charlist(charlist) do
+  defp info_printable_charlist(charlist) do
     description = """
     This is a list of integers that is printed as a sequence of characters
-    delimited by single quotes because all the integers in it represent valid
-    ASCII characters. Conventionally, such lists of integers are referred to
-    as "charlists" (more precisely, a charlist is a list of Unicode codepoints,
-    and ASCII is a subset of Unicode).
+    delimited by single quotes because all the integers in it represent printable
+    ASCII characters. Conventionally, a list of Unicode codepoints is known as a
+    charlist and a list of ASCII characters is a subset of it.
     """
 
     [
diff --git a/lib/iex/lib/iex/introspection.ex b/lib/iex/lib/iex/introspection.ex
index 0413916cb..d7d305eba 100644
--- a/lib/iex/lib/iex/introspection.ex
+++ b/lib/iex/lib/iex/introspection.ex
@@ -352,7 +352,7 @@ defmodule IEx.Introspection do
     )
 
     puts_error(
-      "If instead of accessing documentation you would like to more information about a value " <>
+      "If instead of accessing documentation you would like more information about a value " <>
         "or about the result of an expression, use the \"i\" helper instead"
     )
 
diff --git a/lib/iex/lib/iex/pry.ex b/lib/iex/lib/iex/pry.ex
index e5c3a4fb0..9ecca13a3 100644
--- a/lib/iex/lib/iex/pry.ex
+++ b/lib/iex/lib/iex/pry.ex
@@ -248,7 +248,7 @@ defmodule IEx.Pry do
   end
 
   @doc """
-  Resets the breaks on a given breakpoint id.
+  Resets the breaks on a given breakpoint ID.
   """
   @spec reset_break(id) :: :ok | :not_found
   def reset_break(id) when is_integer(id) do
diff --git a/lib/iex/lib/iex/remsh.ex b/lib/iex/lib/iex/remsh.ex
deleted file mode 100644
index 529e0d268..000000000
--- a/lib/iex/lib/iex/remsh.ex
+++ /dev/null
@@ -1,16 +0,0 @@
-defmodule IEx.Remsh do
-  @moduledoc false
-
-  @doc """
-  Provides one helper function that is injected into connecting
-  remote nodes to properly handle autocompletion.
-  """
-  def expand(node) do
-    fn e ->
-      case :rpc.call(node, IEx.Autocomplete, :expand, [e]) do
-        {:badrpc, _} -> {:no, '', []}
-        r -> r
-      end
-    end
-  end
-end
diff --git a/lib/iex/lib/iex/server.ex b/lib/iex/lib/iex/server.ex
index df6154c7c..a177f4341 100644
--- a/lib/iex/lib/iex/server.ex
+++ b/lib/iex/lib/iex/server.ex
@@ -2,7 +2,7 @@ defmodule IEx.State do
   @moduledoc false
   # This state is exchanged between IEx.Server and
   # IEx.Evaluator which is why it is a struct.
-  defstruct cache: '', counter: 1, prefix: "iex"
+  defstruct cache: '', counter: 1, prefix: "iex", on_eof: :stop_evaluator
   @type t :: %__MODULE__{}
 end
 
@@ -26,6 +26,7 @@ defmodule IEx.Server do
     * `:prefix` - the IEx prefix
     * `:env` - the `Macro.Env` used for the evaluator
     * `:binding` - an initial set of variables for the evaluator
+    * `:on_eof` - if it should `:stop_evaluator` (default) or `:halt` the system
 
   """
   @doc since: "1.8.0"
@@ -128,7 +129,10 @@ defmodule IEx.Server do
         loop(%{state | cache: ''}, evaluator, evaluator_ref)
 
       {:input, ^input, :eof} ->
-        stop_evaluator(evaluator, evaluator_ref)
+        case state.on_eof do
+          :halt -> System.halt(0)
+          :stop_evaluator -> stop_evaluator(evaluator, evaluator_ref)
+        end
 
       {:input, ^input, {:error, :terminated}} ->
         stop_evaluator(evaluator, evaluator_ref)
@@ -284,7 +288,8 @@ defmodule IEx.Server do
 
   defp iex_state(opts) do
     prefix = Keyword.get(opts, :prefix, "iex")
-    %IEx.State{prefix: prefix}
+    on_eof = Keyword.get(opts, :on_eof, :stop_evaluator)
+    %IEx.State{prefix: prefix, on_eof: on_eof}
   end
 
   ## IO
diff --git a/lib/iex/test/iex/helpers_test.exs b/lib/iex/test/iex/helpers_test.exs
index 49d470a33..41ed813ab 100644
--- a/lib/iex/test/iex/helpers_test.exs
+++ b/lib/iex/test/iex/helpers_test.exs
@@ -52,7 +52,7 @@ defmodule IEx.HelpersTest do
       assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}]
     end
 
-    test "resets breaks on the given id" do
+    test "resets breaks on the given ID" do
       assert break!(URI, :decode_query, 2) == 1
       assert reset_break(1) == :ok
       assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}]
@@ -519,6 +519,12 @@ defmodule IEx.HelpersTest do
       cleanup_modules([Impl, MyBehaviour])
     end
 
+    test "prints protocol function docs" do
+      output = capture_io(fn -> h(Enumerable.reduce()) end)
+      assert output =~ "@spec reduce(t(), acc(), reducer()) :: result()"
+      assert output =~ "Reduces the `enumerable`"
+    end
+
     test "prints documentation for delegates" do
       filename = "delegate.ex"
 
@@ -659,6 +665,12 @@ defmodule IEx.HelpersTest do
              """
     end
 
+    test "prints protocol function docs" do
+      output = capture_io(fn -> b(Enumerable.reduce()) end)
+      assert output =~ "@callback reduce(t(), acc(), reducer()) :: result()"
+      assert output =~ "Reduces the `enumerable`"
+    end
+
     test "lists callback with multiple clauses" do
       filename = "multiple_clauses_callback.ex"
 
@@ -677,6 +689,13 @@ defmodule IEx.HelpersTest do
                @callback test(:foo) :: integer()
                @callback test(:bar) :: [integer()]
                """
+
+        assert capture_io(fn -> b(MultipleClauseCallback.test()) end) =~ """
+               @callback test(:foo) :: integer()
+               @callback test(:bar) :: [integer()]
+
+               callback
+               """
       end)
     after
       cleanup_modules([MultipleClauseCallback])
@@ -1189,8 +1208,8 @@ defmodule IEx.HelpersTest do
     end
   end
 
-  describe "pid" do
-    test "builds a pid from string" do
+  describe "pid/1,3" do
+    test "builds a PID from string" do
       assert inspect(pid("0.32767.3276")) == "#PID<0.32767.3276>"
       assert inspect(pid("0.5.6")) == "#PID<0.5.6>"
 
@@ -1199,7 +1218,7 @@ defmodule IEx.HelpersTest do
       end
     end
 
-    test "builds a pid from integers" do
+    test "builds a PID from integers" do
       assert inspect(pid(0, 32767, 3276)) == "#PID<0.32767.3276>"
       assert inspect(pid(0, 5, 6)) == "#PID<0.5.6>"
 
diff --git a/lib/iex/test/iex/info_test.exs b/lib/iex/test/iex/info_test.exs
index 406088d17..c7730f298 100644
--- a/lib/iex/test/iex/info_test.exs
+++ b/lib/iex/test/iex/info_test.exs
@@ -67,6 +67,7 @@ defmodule IEx.InfoTest do
     end
 
     test "regular lists" do
+      assert get_key(Info.info([]), "Reference modules") == "List"
       assert get_key(Info.info([:foo, :bar, :baz]), "Reference modules") == "List"
     end
   end
diff --git a/lib/iex/test/iex/interaction_test.exs b/lib/iex/test/iex/interaction_test.exs
index c53c0bd92..0c6b36ab2 100644
--- a/lib/iex/test/iex/interaction_test.exs
+++ b/lib/iex/test/iex/interaction_test.exs
@@ -157,28 +157,36 @@ defmodule IEx.InteractionTest do
 
   describe ".iex" do
     test "no .iex" do
-      capture_io(:stderr, fn ->
-        assert "** (CompileError) iex:1: undefined function my_variable/0" <> _ =
-                 capture_iex("my_variable")
-      end)
+      assert "** (CompileError) iex:1: undefined function my_variable/0" <> _ =
+               capture_iex("my_variable")
     end
 
     test "single .iex" do
-      File.write!("dot-iex", "my_variable = 144")
-      assert capture_iex("my_variable", [], dot_iex_path: "dot-iex") == "144"
-    after
-      File.rm("dot-iex")
+      path = write_dot_iex!("dot-iex", "my_variable = 144")
+      assert capture_iex("my_variable", [], dot_iex_path: path) == "144"
     end
 
     test "nested .iex" do
-      File.write!("dot-iex-1", "nested_var = 13\nimport IO")
-      File.write!("dot-iex", "import_file \"dot-iex-1\"\nmy_variable=14")
+      write_dot_iex!("dot-iex-1", "nested_var = 13\nimport IO")
+      path = write_dot_iex!("dot-iex", "import_file \"tmp/dot-iex-1\"\nmy_variable=14")
 
       input = "nested_var\nmy_variable\nputs \"hello\""
-      assert capture_iex(input, [], dot_iex_path: "dot-iex") == "13\n14\nhello\n:ok"
-    after
-      File.rm("dot-iex-1")
-      File.rm("dot-iex")
+      assert capture_iex(input, [], dot_iex_path: path) == "13\n14\nhello\n:ok"
     end
+
+    test "malformed .iex" do
+      path = write_dot_iex!("dot-iex", "malformed")
+
+      assert capture_iex("1 + 2", [], dot_iex_path: path) =~
+               "dot-iex:1: undefined function malformed/0"
+    end
+  end
+
+  defp write_dot_iex!(name, contents) do
+    dir = "#{__DIR__}/../../tmp"
+    File.mkdir_p!(dir)
+    path = Path.join(dir, name)
+    File.write!(path, contents)
+    path
   end
 end
diff --git a/lib/iex/test/iex/pry_test.exs b/lib/iex/test/iex/pry_test.exs
index f044fcdba..dc1862e98 100644
--- a/lib/iex/test/iex/pry_test.exs
+++ b/lib/iex/test/iex/pry_test.exs
@@ -68,13 +68,13 @@ defmodule IEx.PryTest do
       assert [_, _] = IEx.Pry.breaks()
     end
 
-    test "returns id when breakpoint is already set" do
+    test "returns ID when breakpoint is already set" do
       assert IEx.Pry.break(URI, :decode_query, 2) == {:ok, 1}
       assert IEx.Pry.break(URI, :decode_query, 2) == {:ok, 1}
       assert [_] = IEx.Pry.breaks()
     end
 
-    test "returns id even when breakpoint is already set on deinstrument" do
+    test "returns ID even when breakpoint is already set on deinstrument" do
       assert IEx.Pry.break(URI, :decode_query, 2) == {:ok, 1}
       deinstrument!(URI)
       assert IEx.Pry.break(URI, :decode_query, 2) == {:ok, 1}
@@ -121,7 +121,7 @@ defmodule IEx.PryTest do
   end
 
   describe "reset_break" do
-    test "resets break for given id" do
+    test "resets break for given ID" do
       assert IEx.Pry.break(URI, :decode_query, 2) == {:ok, 1}
       assert IEx.Pry.reset_break(1) == :ok
       assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}]
@@ -144,14 +144,14 @@ defmodule IEx.PryTest do
       assert IEx.Pry.reset_break(URI, :decode_query, 2) == :not_found
     end
 
-    test "returns not_found if id is deinstrumented" do
+    test "returns not_found if ID is deinstrumented" do
       assert IEx.Pry.break(URI, :decode_query, 2) == {:ok, 1}
       deinstrument!(URI)
       assert IEx.Pry.reset_break(1) == :not_found
       assert IEx.Pry.breaks() == []
     end
 
-    test "returns not_found if id has no break" do
+    test "returns not_found if ID has no break" do
       assert IEx.Pry.reset_break(1) == :not_found
     end
   end
diff --git a/lib/iex/test/iex/server_test.exs b/lib/iex/test/iex/server_test.exs
index 581066dce..655138ed7 100644
--- a/lib/iex/test/iex/server_test.exs
+++ b/lib/iex/test/iex/server_test.exs
@@ -137,8 +137,7 @@ defmodule IEx.ServerTest do
     task =
       Task.async(fn ->
         capture_iex("""
-        send(#{inspect(name)}, {:running, self()})
-        receive do: (:run -> :ok)
+        send(#{inspect(name)}, {:running, self()}) && receive do: (:run -> :ok)
         #{session}
         """)
       end)
diff --git a/lib/iex/test/test_helper.exs b/lib/iex/test/test_helper.exs
index 137bbd8a0..659b53f86 100644
--- a/lib/iex/test/test_helper.exs
+++ b/lib/iex/test/test_helper.exs
@@ -2,7 +2,7 @@ assert_timeout = String.to_integer(System.get_env("ELIXIR_ASSERT_TIMEOUT") || "5
 
 System.put_env("ELIXIR_EDITOR", "echo")
 
-:ok = Application.start(:iex)
+{:ok, _} = Application.ensure_all_started(:iex)
 IEx.configure(colors: [enabled: false])
 ExUnit.start(trace: "--trace" in System.argv(), assert_receive_timeout: assert_timeout)
 
diff --git a/lib/logger/lib/logger.ex b/lib/logger/lib/logger.ex
index 04ca99520..8afaca85e 100644
--- a/lib/logger/lib/logger.ex
+++ b/lib/logger/lib/logger.ex
@@ -25,7 +25,7 @@ defmodule Logger do
   system. For example, it may be helpful to log whenever a user is deleted.
 
       def delete_user(user) do
-        Logger.info "Deleting user from the system: #{inspect(user)}"
+        Logger.info("Deleting user from the system: #{inspect(user)}")
         # ...
       end
 
@@ -133,15 +133,23 @@ defmodule Logger do
       `:sync_threshold` messages in its queue, `Logger` will change
       to *sync mode*, to apply backpressure to the clients.
       `Logger` will return to *async mode* once the number of messages
-      in the queue is reduced to `sync_threshold * 0.75` messages.
-      Defaults to 20 messages. `:sync_threshold` can be set to `0` to force *sync mode*.
+      in the queue is reduced to one below the `sync_threshold`.
+      Defaults to 20 messages. `:sync_threshold` can be set to `0` to
+      force *sync mode*.
 
     * `:discard_threshold` - if the `Logger` manager has more than
       `:discard_threshold` messages in its queue, `Logger` will change
       to *discard mode* and messages will be discarded directly in the
       clients. `Logger` will return to *sync mode* once the number of
-      messages in the queue is reduced to `discard_threshold * 0.75`
-      messages. Defaults to 500 messages.
+      messages in the queue is reduced to one below the `discard_threshold`.
+      Defaults to 500 messages.
+
+    * `:discard_threshold_periodic_check` - a periodic check that
+      checks and reports if logger is discarding messages. It logs a warn
+      message whenever the system is (or continues) in discard mode and
+      it logs a warn message whenever if the system was discarding messages
+      but stopped doing so after the previous check. By default it runs
+      every `30_000` milliseconds.
 
     * `:translator_inspect_opts` - when translating OTP reports and
       errors, the last message and state must be inspected in the
@@ -501,10 +509,7 @@ defmodule Logger do
   The `Logger` level can be changed via `configure/1`.
   """
   @spec level() :: level
-  def level() do
-    %{level: level} = Logger.Config.__data__()
-    level
-  end
+  defdelegate level(), to: Logger.Config
 
   @doc """
   Compares log levels.
@@ -525,18 +530,7 @@ defmodule Logger do
 
   """
   @spec compare_levels(level, level) :: :lt | :eq | :gt
-  def compare_levels(level, level) do
-    :eq
-  end
-
-  def compare_levels(left, right) do
-    if level_to_number(left) > level_to_number(right), do: :gt, else: :lt
-  end
-
-  defp level_to_number(:debug), do: 0
-  defp level_to_number(:info), do: 1
-  defp level_to_number(:warn), do: 2
-  defp level_to_number(:error), do: 3
+  defdelegate compare_levels(left, right), to: Logger.Config
 
   @doc """
   Configures the logger.
@@ -669,12 +663,9 @@ defmodule Logger do
   def __should_log__(level) when level in @levels do
     case __metadata__() do
       {true, pdict} ->
-        %{mode: mode, level: min_level} = config = Logger.Config.__data__()
-
-        if compare_levels(level, min_level) != :lt and mode != :discard do
-          {level, config, pdict}
-        else
-          :error
+        case Logger.Config.log_data(level) do
+          {:discard, _config} -> :error
+          {mode, config} -> {level, mode, config, pdict}
         end
 
       {false, _} ->
@@ -683,8 +674,9 @@ defmodule Logger do
   end
 
   @doc false
-  def __do_log__({level, config, pdict}, chardata_or_fun, metadata) when is_list(metadata) do
-    %{utc_log: utc_log?, truncate: truncate, mode: mode} = config
+  def __do_log__({level, mode, config, pdict}, chardata_or_fun, metadata)
+      when is_list(metadata) do
+    %{utc_log: utc_log?, truncate: truncate} = config
     metadata = [pid: self()] ++ into_metadata(metadata, pdict)
 
     case normalize_message(chardata_or_fun, metadata) do
@@ -712,9 +704,9 @@ defmodule Logger do
 
   ## Examples
 
-      Logger.warn "knob turned too far to the right"
-      Logger.warn fn -> "dynamically calculated warning" end
-      Logger.warn fn -> {"dynamically calculated warning", [additional: :metadata]} end
+      Logger.warn("knob turned too far to the right")
+      Logger.warn(fn -> "dynamically calculated warning" end)
+      Logger.warn(fn -> {"dynamically calculated warning", [additional: :metadata]} end)
 
   """
   defmacro warn(chardata_or_fun, metadata \\ []) do
@@ -728,9 +720,9 @@ defmodule Logger do
 
   ## Examples
 
-      Logger.info "mission accomplished"
-      Logger.info fn -> "dynamically calculated info" end
-      Logger.info fn -> {"dynamically calculated info", [additional: :metadata]} end
+      Logger.info("mission accomplished")
+      Logger.info(fn -> "dynamically calculated info" end)
+      Logger.info(fn -> {"dynamically calculated info", [additional: :metadata]} end)
 
   """
   defmacro info(chardata_or_fun, metadata \\ []) do
@@ -744,9 +736,9 @@ defmodule Logger do
 
   ## Examples
 
-      Logger.error "oops"
-      Logger.error fn -> "dynamically calculated error" end
-      Logger.error fn -> {"dynamically calculated error", [additional: :metadata]} end
+      Logger.error("oops")
+      Logger.error(fn -> "dynamically calculated error" end)
+      Logger.error(fn -> {"dynamically calculated error", [additional: :metadata]} end)
 
   """
   defmacro error(chardata_or_fun, metadata \\ []) do
@@ -760,9 +752,9 @@ defmodule Logger do
 
   ## Examples
 
-      Logger.debug "hello?"
-      Logger.debug fn -> "dynamically calculated debug" end
-      Logger.debug fn -> {"dynamically calculated debug", [additional: :metadata]} end
+      Logger.debug("hello?")
+      Logger.debug(fn -> "dynamically calculated debug" end)
+      Logger.debug(fn -> {"dynamically calculated debug", [additional: :metadata]} end)
 
   """
   defmacro debug(chardata_or_fun, metadata \\ []) do
@@ -829,7 +821,7 @@ defmodule Logger do
     Enum.any?(matching, fn filter ->
       Enum.all?(filter, fn
         {:level_lower_than, min_level} ->
-          compare_levels(level, min_level) == :lt
+          Logger.Config.compare_levels(level, min_level) == :lt
 
         {k, v} when is_atom(k) ->
           Keyword.fetch(compile_metadata, k) == {:ok, v}
@@ -841,13 +833,11 @@ defmodule Logger do
     end)
   end
 
-  # TODO: Either deprecate compile_time_purge_level in favor of
-  # compile_time_purge_matching or document it again on 1.9 based
-  # on feedback
+  # TODO: Deprecate compile_time_purge_level on v1.9
   defp maybe_log(level, data, metadata, caller) do
     min_level = Application.get_env(:logger, :compile_time_purge_level, :debug)
 
-    if compare_levels(level, min_level) != :lt do
+    if Logger.Config.compare_levels(level, min_level) != :lt do
       macro_log(level, data, metadata, caller)
     else
       no_log(data, metadata)
diff --git a/lib/logger/lib/logger/app.ex b/lib/logger/lib/logger/app.ex
index 020322f39..90fc85f55 100644
--- a/lib/logger/lib/logger/app.ex
+++ b/lib/logger/lib/logger/app.ex
@@ -17,19 +17,19 @@ defmodule Logger.App do
         [%{id: Logger.ErrorHandler, start: {Logger.Watcher, :start_link, [arg]}}]
       end
 
+    config = Logger.Config.new()
+
     children = [
       %{
         id: :gen_event,
         start: {:gen_event, :start_link, [{:local, Logger}]},
         modules: :dynamic
       },
-      {Logger.Watcher, {Logger, Logger.Config, []}},
+      {Logger.Watcher, {Logger, Logger.Config, config}},
       Logger.BackendSupervisor
       | otp_children
     ]
 
-    config = Logger.Config.new()
-
     case Supervisor.start_link(children, strategy: :rest_for_one, name: Logger.Supervisor) do
       {:ok, sup} ->
         if otp_logger?() do
diff --git a/lib/logger/lib/logger/config.ex b/lib/logger/lib/logger/config.ex
index e15f8c03f..1ae09fa4c 100644
--- a/lib/logger/lib/logger/config.ex
+++ b/lib/logger/lib/logger/config.ex
@@ -4,10 +4,8 @@ defmodule Logger.Config do
   @behaviour :gen_event
   @name __MODULE__
   @table __MODULE__
-
-  def start_link do
-    GenServer.start_link(__MODULE__, :ok, name: @name)
-  end
+  @counter_pos 1
+  @update_counter_message {__MODULE__, :update_counter}
 
   def configure(options) do
     :gen_event.call(Logger, @name, {:configure, options})
@@ -21,27 +19,48 @@ defmodule Logger.Config do
     :gen_event.call(Logger, @name, {:remove_translator, translator})
   end
 
-  def __data__() do
-    try do
-      :ets.lookup_element(@table, :data, 2)
-    rescue
-      ArgumentError ->
-        raise "cannot use Logger, the :logger application is not running"
-    else
-      nil ->
-        raise "cannot use Logger, the :logger application is not running"
+  def level do
+    %{level: level} = read_data!(:translation_data)
+    level
+  end
 
-      data ->
-        data
+  def compare_levels(level, level), do: :eq
+
+  def compare_levels(left, right) do
+    if level_to_number(left) > level_to_number(right), do: :gt, else: :lt
+  end
+
+  defp level_to_number(:debug), do: 0
+  defp level_to_number(:info), do: 1
+  defp level_to_number(:warn), do: 2
+  defp level_to_number(:error), do: 3
+
+  def translation_data do
+    read_data!(:translation_data)
+  end
+
+  def log_data(level) do
+    %{level: min_level} = config = read_data!(:log_data)
+
+    if compare_levels(level, min_level) != :lt do
+      %{thresholds: {counter, sync, discard}} = config
+      value = bump_counter(counter)
+
+      cond do
+        value >= discard -> {:discard, config}
+        value >= sync -> {:sync, config}
+        true -> {:async, config}
+      end
+    else
+      {:discard, config}
     end
   end
 
-  def deleted_handlers() do
+  def deleted_handlers do
     try do
       :ets.lookup_element(@table, :deleted_handlers, 2)
     rescue
-      ArgumentError ->
-        []
+      ArgumentError -> []
     end
   end
 
@@ -49,22 +68,25 @@ defmodule Logger.Config do
     :gen_event.call(Logger, @name, {:deleted_handlers, handlers})
   end
 
-  def new() do
-    tab = :ets.new(@table, [:named_table, :public, {:read_concurrency, true}])
-    true = :ets.insert_new(@table, [{:data, nil}, {:deleted_handlers, []}])
-    tab
+  def new do
+    {new_data(), new_counter()}
   end
 
-  def delete(@table) do
-    :ets.delete(@table)
+  def delete({data, counter}) do
+    delete_data(data)
+    delete_counter(counter)
   end
 
   ## Callbacks
 
-  def init(_) do
-    thresholds = compute_thresholds()
-    state = :ets.lookup_element(@table, :data, 2) || compute_state(:async, thresholds)
-    {:ok, {state, thresholds}}
+  def init({@table, counter}) do
+    state =
+      {counter, :log, Application.fetch_env!(:logger, :discard_threshold),
+       Application.fetch_env!(:logger, :discard_threshold_periodic_check)}
+
+    :ets.lookup_element(@table, :log_data, 2) || compute_data(state)
+    state = update_counter(state, false)
+    {:ok, state}
   end
 
   def handle_event({_type, gl, _msg} = event, state) when node(gl) != node() do
@@ -75,50 +97,27 @@ defmodule Logger.Config do
     {:ok, state}
   end
 
-  def handle_event(_event, {state, thresholds}) do
-    %{mode: mode} = state
-
-    case compute_mode(mode, thresholds) do
-      ^mode ->
-        {:ok, {state, thresholds}}
-
-      new_mode ->
-        if new_mode == :discard do
-          message =
-            "Logger has #{message_queue_length()} messages in its queue, " <>
-              "which is above :discard_threshold. Messages will be discarded " <>
-              "until the message queue goes back to 75% of the threshold size"
-
-          log(:warn, message, state)
-        end
-
-        if mode == :discard do
-          log(:warn, "Logger has stopped discarding messages", state)
-        end
-
-        state = persist(%{state | mode: new_mode})
-        {:ok, {state, thresholds}}
-    end
+  def handle_event(_event, state) do
+    state = update_counter(state, false)
+    {:ok, state}
   end
 
-  def handle_call({:configure, options}, {%{mode: mode}, _}) do
+  def handle_call({:configure, options}, state) do
     Enum.each(options, fn {key, value} ->
       Application.put_env(:logger, key, value)
     end)
 
-    thresholds = compute_thresholds()
-    state = compute_state(mode, thresholds)
-    {:ok, :ok, {state, thresholds}}
+    {:ok, :ok, compute_data(state)}
   end
 
-  def handle_call({:add_translator, translator}, {state, thresholds}) do
-    state = update_translators(state, fn t -> [translator | List.delete(t, translator)] end)
-    {:ok, :ok, {state, thresholds}}
+  def handle_call({:add_translator, translator}, state) do
+    update_translators(fn t -> [translator | List.delete(t, translator)] end)
+    {:ok, :ok, state}
   end
 
-  def handle_call({:remove_translator, translator}, {state, thresholds}) do
-    state = update_translators(state, &List.delete(&1, translator))
-    {:ok, :ok, {state, thresholds}}
+  def handle_call({:remove_translator, translator}, state) do
+    update_translators(&List.delete(&1, translator))
+    {:ok, :ok, state}
   end
 
   def handle_call({:deleted_handlers, new}, state) do
@@ -127,7 +126,13 @@ defmodule Logger.Config do
     {:ok, old, state}
   end
 
-  def handle_info(_msg, state) do
+  def handle_info(@update_counter_message, state) do
+    state = update_counter(state, true)
+    schedule_update_counter(state)
+    {:ok, state}
+  end
+
+  def handle_info(_, state) do
     {:ok, state}
   end
 
@@ -139,69 +144,147 @@ defmodule Logger.Config do
     {:ok, state}
   end
 
-  ## Helpers
+  defp update_counter({counter, log, discard_threshold, discard_period}, periodic_check?) do
+    # If length is more than the total, it means the counter is behind,
+    # due to non-log messages, so we need to increase the counter.
+    #
+    # If length is less than the total, then we either have a spike or
+    # the counter drifted due to failures.
+    #
+    # Because we always bump the counter and then we send the message,
+    # there is a chance clients have bumped the counter but they did not
+    # deliver the message yet. Those bumps will be lost. At the same time,
+    # we are careful to read the counter first here, so if the counter is
+    # bumped after we read from it, those bumps won't be lost.
+    total = read_counter(counter)
+    {:message_queue_len, length} = Process.info(self(), :message_queue_len)
+    add_counter(counter, length - total)
+
+    # In case we are logging but we reached the threshold, we log that we
+    # started discarding messages. This can only be reverted by the periodic
+    # discard check.
+    cond do
+      total >= discard_threshold ->
+        if log == :log or periodic_check? do
+          warn("Attempted to log #{total} messages, which is above :discard_threshold")
+        end
+
+        {counter, :discard, discard_threshold, discard_period}
+
+      log == :discard and periodic_check? ->
+        warn("Attempted to log #{total} messages, which is below :discard_threshold")
+        {counter, :log, discard_threshold, discard_period}
+
+      true ->
+        {counter, log, discard_threshold, discard_period}
+    end
+  end
+
+  defp warn(message) do
+    utc_log = Application.fetch_env!(:logger, :utc_log)
+    event = {Logger, message, Logger.Utils.timestamp(utc_log), pid: self()}
+    :gen_event.notify(self(), {:warn, Process.group_leader(), event})
+  end
+
+  defp schedule_update_counter({_, _, _, discard_period}) do
+    Process.send_after(self(), @update_counter_message, discard_period)
+  end
+
+  ## Counter Helpers
+
+  # TODO: Use counters exclusively when we require Erlang/OTP 22+.
+  defp new_counter() do
+    if Code.ensure_loaded?(:counters) do
+      {:counters, :counters.new(1, [:atomics])}
+    else
+      table = :ets.new(@table.Counter, [:public])
+      :ets.insert(table, [{@counter_pos, 0}])
+      {:ets, table}
+    end
+  end
+
+  defp delete_counter({:ets, counter}), do: :ets.delete(counter)
+  defp delete_counter({:counters, _}), do: :ok
+
+  defp read_counter({:ets, counter}), do: :ets.lookup_element(counter, @counter_pos, 2)
+  defp read_counter({:counters, counter}), do: :counters.get(counter, @counter_pos)
+
+  defp add_counter({:ets, counter}, value),
+    do: :ets.update_counter(counter, @counter_pos, {2, value})
 
-  defp log(level, message, state) do
-    event = {Logger, message, Logger.Utils.timestamp(state.utc_log), pid: self()}
-    :gen_event.notify(self(), {level, Process.group_leader(), event})
+  defp add_counter({:counters, counter}, value),
+    do: :counters.add(counter, @counter_pos, value)
+
+  defp bump_counter({:ets, counter}),
+    do: :ets.update_counter(counter, @counter_pos, {2, 1})
+
+  defp bump_counter({:counters, counter}) do
+    :counters.add(counter, @counter_pos, 1)
+    :counters.get(counter, @counter_pos)
   end
 
-  defp message_queue_length() do
-    {:message_queue_len, messages} = Process.info(self(), :message_queue_len)
-    messages
+  ## Data helpers
+
+  defp new_data do
+    entries = [
+      {:log_data, nil},
+      {:translation_data, nil},
+      {:deleted_handlers, []}
+    ]
+
+    table = :ets.new(@table, [:named_table, :public, {:read_concurrency, true}])
+    true = :ets.insert_new(@table, entries)
+    table
   end
 
-  defp update_translators(%{translators: translators} = state, fun) do
-    translators = fun.(translators)
+  defp delete_data(@table), do: :ets.delete(@table)
+
+  defp update_translators(fun) do
+    translation_data = read_data!(:translation_data)
+    translators = fun.(translation_data.translators)
     Application.put_env(:logger, :translators, translators)
-    persist(%{state | translators: translators})
-  end
-
-  defp compute_state(mode, thresholds) do
-    persist(%{
-      mode: compute_mode(mode, thresholds),
-      level: Application.get_env(:logger, :level),
-      translators: Application.get_env(:logger, :translators),
-      truncate: Application.get_env(:logger, :truncate),
-      utc_log: Application.get_env(:logger, :utc_log)
-    })
-  end
-
-  defp compute_mode(mode, thresholds) do
-    %{
-      async_threshold: async_threshold,
-      sync_threshold: sync_threshold,
-      keep_threshold: keep_threshold,
-      discard_threshold: discard_threshold
-    } = thresholds
-
-    Logger.Utils.compute_mode(
-      mode,
-      message_queue_length(),
-      async_threshold,
-      sync_threshold,
-      keep_threshold,
-      discard_threshold
-    )
-  end
-
-  defp compute_thresholds() do
-    sync_threshold = Application.get_env(:logger, :sync_threshold)
-    async_threshold = trunc(sync_threshold * 0.75)
-
-    discard_threshold = Application.get_env(:logger, :discard_threshold)
-    keep_threshold = trunc(discard_threshold * 0.75)
-
-    %{
-      async_threshold: async_threshold,
-      sync_threshold: sync_threshold,
-      keep_threshold: keep_threshold,
-      discard_threshold: discard_threshold
+    update_data(:translation_data, %{translation_data | translators: translators})
+  end
+
+  defp compute_data({counter, _mode, _discard_threshold, _discard_period}) do
+    sync_threshold = Application.fetch_env!(:logger, :sync_threshold)
+    discard_threshold = Application.fetch_env!(:logger, :discard_threshold)
+    discard_period = Application.fetch_env!(:logger, :discard_threshold_periodic_check)
+
+    log_data = %{
+      level: Application.fetch_env!(:logger, :level),
+      utc_log: Application.fetch_env!(:logger, :utc_log),
+      truncate: Application.fetch_env!(:logger, :truncate),
+      thresholds: {counter, sync_threshold, discard_threshold}
+    }
+
+    translation_data = %{
+      level: Application.fetch_env!(:logger, :level),
+      translators: Application.fetch_env!(:logger, :translators),
+      truncate: Application.fetch_env!(:logger, :truncate)
     }
+
+    update_data(:log_data, log_data)
+    update_data(:translation_data, translation_data)
+    {counter, :log, discard_threshold, discard_period}
+  end
+
+  defp read_data!(key) do
+    try do
+      :ets.lookup_element(@table, key, 2)
+    rescue
+      ArgumentError ->
+        raise "cannot use Logger, the :logger application is not running"
+    else
+      nil ->
+        raise "cannot use Logger, the :logger application is not running"
+
+      data ->
+        data
+    end
   end
 
-  defp persist(state) do
-    :ets.update_element(@table, :data, {2, state})
-    state
+  defp update_data(key, value) do
+    :ets.update_element(@table, key, {2, value})
   end
 end
diff --git a/lib/logger/lib/logger/erlang_handler.ex b/lib/logger/lib/logger/erlang_handler.ex
index a0b8f4a5a..d53d80b22 100644
--- a/lib/logger/lib/logger/erlang_handler.ex
+++ b/lib/logger/lib/logger/erlang_handler.ex
@@ -92,7 +92,7 @@ defmodule Logger.ErlangHandler do
       level: min_level,
       truncate: truncate,
       translators: translators
-    } = Logger.Config.__data__()
+    } = Logger.Config.translation_data()
 
     case translate(translators, min_level, level, kind, data, meta) do
       :none -> {translate_fallback(kind, data, erl_meta, truncate), meta}
diff --git a/lib/logger/lib/logger/error_handler.ex b/lib/logger/lib/logger/error_handler.ex
index e6f8cc173..789617b57 100644
--- a/lib/logger/lib/logger/error_handler.ex
+++ b/lib/logger/lib/logger/error_handler.ex
@@ -2,7 +2,7 @@ defmodule Logger.ErrorHandler do
   @moduledoc false
   @behaviour :gen_event
 
-  # TODO: Remove this when we require Erlang/OTP 21+.
+  # TODO: Remove this module when we require Erlang/OTP 21+.
 
   def init({otp?, sasl?, threshold}) do
     # We store the Logger PID in the state because when we are shutting
@@ -82,13 +82,9 @@ defmodule Logger.ErrorHandler do
   defp log_event(_, _state), do: :ok
 
   defp log_event(level, kind, gl, pid, {type, _} = data, state) do
-    %{
-      mode: mode,
-      level: min_level,
-      utc_log: utc_log?
-    } = Logger.Config.__data__()
+    {mode, %{utc_log: utc_log?}} = Logger.Config.log_data(level)
 
-    with true <- Logger.compare_levels(level, min_level) != :lt and mode != :discard,
+    with true <- mode != :discard,
          meta = [pid: ensure_pid(pid), error_logger: ensure_type(type)],
          {message, meta} <- Logger.ErlangHandler.translate(level, kind, data, meta, %{}) do
       # Mode is always async to avoid clogging the error_logger
@@ -126,9 +122,12 @@ defmodule Logger.ErrorHandler do
           "its inbox, exceeding the amount of :discard_threshold #{discard_threshold} messages. " <>
           "The number of messages was reduced to #{keep_threshold} (75% of the threshold)"
 
-      %{utc_log: utc_log?} = Logger.Config.__data__()
-      event = {Logger, message, Logger.Utils.timestamp(utc_log?), pid: self()}
-      :gen_event.notify(state.logger, {:warn, Process.group_leader(), event})
+      {mode, %{utc_log: utc_log?}} = Logger.Config.log_data(:warn)
+
+      if mode != :discard do
+        event = {Logger, message, Logger.Utils.timestamp(utc_log?), pid: self()}
+        :gen_event.notify(state.logger, {:warn, Process.group_leader(), event})
+      end
 
       # We won't check the threshold for the next 10% of the threshold messages
       %{state | skip: trunc(discard_threshold * 0.1)}
diff --git a/lib/logger/lib/logger/translator.ex b/lib/logger/lib/logger/translator.ex
index cb4678a09..01a3b0a64 100644
--- a/lib/logger/lib/logger/translator.ex
+++ b/lib/logger/lib/logger/translator.ex
@@ -80,7 +80,7 @@ defmodule Logger.Translator do
   end
 
   ## Erlang/OTP 20 and before
-  # TODO: This clauses can be removed when we support only Erlang/OTP 21+.
+  # TODO: These clauses can be removed when we support only Erlang/OTP 21+.
 
   def translate(min_level, :error, :format, message) do
     opts = Application.get_env(:logger, :translator_inspect_opts)
diff --git a/lib/logger/lib/logger/utils.ex b/lib/logger/lib/logger/utils.ex
index 948420eed..50225bf38 100644
--- a/lib/logger/lib/logger/utils.ex
+++ b/lib/logger/lib/logger/utils.ex
@@ -1,37 +1,6 @@
 defmodule Logger.Utils do
   @moduledoc false
 
-  @doc """
-  Computes the logging mode.
-
-  The result may be either :sync, :async or :discard.
-  """
-  @spec compute_mode(
-          mode,
-          non_neg_integer(),
-          non_neg_integer(),
-          non_neg_integer(),
-          non_neg_integer(),
-          non_neg_integer()
-        ) :: mode
-        when mode: :sync | :async | :discard
-  def compute_mode(
-        mode,
-        messages,
-        async_threshold,
-        sync_threshold,
-        keep_threshold,
-        discard_threshold
-      ) do
-    case mode do
-      _ when messages >= discard_threshold -> :discard
-      :discard when messages > keep_threshold -> :discard
-      _ when messages >= sync_threshold -> :sync
-      :sync when messages > async_threshold -> :sync
-      _ -> :async
-    end
-  end
-
   @doc """
   Truncates a `chardata` into `n` bytes.
 
diff --git a/lib/logger/lib/logger/watcher.ex b/lib/logger/lib/logger/watcher.ex
index fd4a1feda..920a40b73 100644
--- a/lib/logger/lib/logger/watcher.ex
+++ b/lib/logger/lib/logger/watcher.ex
@@ -4,8 +4,8 @@ defmodule Logger.Watcher do
   require Logger
   use GenServer
 
-  # TODO: Once we remove :error_logger, there is no reason to pass
-  # the `mod` argument in, as we will only ever watch Logger handlers
+  # TODO: Once we remove :error_logger in Erlang/OTP 21+, there is no reason
+  # to pass the `mod` argument in, as we will only ever watch Logger handlers
 
   @doc """
   Starts a watcher server.
diff --git a/lib/logger/mix.exs b/lib/logger/mix.exs
index 583b011d1..ed457242e 100644
--- a/lib/logger/mix.exs
+++ b/lib/logger/mix.exs
@@ -23,6 +23,7 @@ defmodule Logger.MixProject do
         discard_threshold: 500,
         handle_otp_reports: true,
         handle_sasl_reports: false,
+        discard_threshold_periodic_check: 30_000,
         discard_threshold_for_error_logger: 500,
         compile_time_purge_level: :debug,
         compile_time_purge_matching: [],
diff --git a/lib/logger/test/logger/config_test.exs b/lib/logger/test/logger/config_test.exs
deleted file mode 100644
index 4100f0446..000000000
--- a/lib/logger/test/logger/config_test.exs
+++ /dev/null
@@ -1,40 +0,0 @@
-defmodule Logger.ConfigTest do
-  use Logger.Case
-  require Logger
-
-  test "log/2 relies on sync_threshold" do
-    Logger.remove_backend(:console)
-    Logger.configure(sync_threshold: 0)
-    for _ <- 1..1000, do: Logger.log(:info, "some message")
-  after
-    Logger.configure(sync_threshold: 20)
-    Logger.add_backend(:console)
-  end
-
-  test "log/2 relies on discard_threshold" do
-    Logger.remove_backend(:console)
-    Logger.configure(discard_threshold: 0)
-    for _ <- 1..1000, do: Logger.log(:info, "some message")
-  after
-    Logger.configure(discard_threshold: 10000)
-    Logger.add_backend(:console)
-  end
-
-  test "restarts Logger.Config on Logger exits" do
-    Process.whereis(Logger) |> Process.exit(:kill)
-    wait_for_logger()
-    wait_for_handler(Logger, Logger.Config)
-  end
-
-  test "Logger.Config updates config on config_change/3" do
-    :ok = Logger.configure(level: :debug)
-
-    try do
-      Application.put_env(:logger, :level, :error)
-      assert Logger.App.config_change([level: :error], [], []) === :ok
-      assert Logger.level() === :error
-    after
-      Logger.configure(level: :debug)
-    end
-  end
-end
diff --git a/lib/logger/test/logger/utils_test.exs b/lib/logger/test/logger/utils_test.exs
index 4c6899cdd..c75988aa7 100644
--- a/lib/logger/test/logger/utils_test.exs
+++ b/lib/logger/test/logger/utils_test.exs
@@ -11,57 +11,6 @@ defmodule Logger.UtilsTest do
     |> :io_lib.unscan_format()
   end
 
-  describe "compute_mode/2" do
-    test "starting async" do
-      assert compute_mode(:async, 0, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:async, 10, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:async, 18, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:async, 20, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:async, 30, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:async, 8000, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:async, 0, 0, 0, 7500, 10000) == :sync
-      assert compute_mode(:async, 8000, 0, 0, 7500, 10000) == :sync
-      assert compute_mode(:async, 10000, 0, 0, 7500, 10000) == :discard
-      assert compute_mode(:async, 10000, 15, 20, 7500, 10000) == :discard
-    end
-
-    test "starting sync" do
-      assert compute_mode(:sync, 0, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:sync, 10, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:sync, 18, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:sync, 20, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:sync, 30, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:sync, 8000, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:sync, 0, 0, 0, 7500, 10000) == :sync
-      assert compute_mode(:sync, 8000, 0, 0, 7500, 10000) == :sync
-      assert compute_mode(:sync, 10000, 0, 0, 7500, 10000) == :discard
-      assert compute_mode(:sync, 10000, 15, 20, 7500, 10000) == :discard
-    end
-
-    test "starting discard (with sync)" do
-      assert compute_mode(:discard, 0, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:discard, 10, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:discard, 18, 15, 20, 7500, 10000) == :async
-      assert compute_mode(:discard, 20, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:discard, 30, 15, 20, 7500, 10000) == :sync
-      assert compute_mode(:discard, 0, 0, 0, 7500, 10000) == :sync
-      assert compute_mode(:discard, 8000, 0, 0, 7500, 10000) == :discard
-      assert compute_mode(:discard, 10000, 0, 0, 7500, 10000) == :discard
-      assert compute_mode(:discard, 8000, 15, 20, 7500, 10000) == :discard
-      assert compute_mode(:discard, 10000, 15, 20, 7500, 10000) == :discard
-    end
-
-    test "starting discard (without sync)" do
-      assert compute_mode(:discard, 0, 75000, 100_000, 7500, 10000) == :async
-      assert compute_mode(:discard, 10, 75000, 100_000, 7500, 10000) == :async
-      assert compute_mode(:discard, 18, 75000, 100_000, 7500, 10000) == :async
-      assert compute_mode(:discard, 20, 75000, 100_000, 7500, 10000) == :async
-      assert compute_mode(:discard, 30, 75000, 100_000, 7500, 10000) == :async
-      assert compute_mode(:discard, 8000, 75000, 100_000, 7500, 10000) == :discard
-      assert compute_mode(:discard, 10000, 75000, 100_000, 7500, 10000) == :discard
-    end
-  end
-
   test "truncate/2" do
     # ASCII binaries
     assert truncate("foo", 4) == "foo"
diff --git a/lib/logger/test/logger_test.exs b/lib/logger/test/logger_test.exs
index f4e083ac6..328b142e3 100644
--- a/lib/logger/test/logger_test.exs
+++ b/lib/logger/test/logger_test.exs
@@ -382,9 +382,9 @@ defmodule LoggerTest do
   end
 
   test "logging something that is not a binary or chardata fails right away" do
-    assert_raise Protocol.UndefinedError, "protocol String.Chars not implemented for %{}", fn ->
-      Logger.log(:debug, %{})
-    end
+    assert_raise Protocol.UndefinedError,
+                 "protocol String.Chars not implemented for %{} of type Map",
+                 fn -> Logger.log(:debug, %{}) end
 
     message =
       "cannot truncate chardata because it contains something that is not valid chardata: %{}"
@@ -426,9 +426,11 @@ defmodule LoggerTest do
     assert Application.get_env(:logger, :discard_threshold) == 10_000
     assert Application.get_env(:logger, :translator_inspect_opts) == [limit: 3]
 
-    logger_data = Logger.Config.__data__()
-    assert logger_data.truncate == 4048
-    assert logger_data.utc_log == true
+    {_, log_data} = Logger.Config.log_data(:debug)
+    assert log_data.utc_log == true
+
+    translation_data = Logger.Config.translation_data()
+    assert translation_data.truncate == 4048
   after
     Logger.configure(sync_threshold: 20)
     Logger.configure(truncate: 8096)
@@ -436,4 +438,47 @@ defmodule LoggerTest do
     Logger.configure(discard_threshold: 500)
     Logger.configure(translator_inspect_opts: [])
   end
+
+  test "logs when discarding messages" do
+    assert :ok = Logger.configure(discard_threshold: 5)
+
+    log =
+      capture_log(fn ->
+        :sys.suspend(Logger)
+        for _ <- 1..10, do: Logger.warn("warning!")
+        :sys.resume(Logger)
+        Logger.flush()
+
+        # This is a private message but the simplest way to test this functionality
+        send(Logger, {Logger.Config, :update_counter})
+
+        # We need to flush twice. Once for the send and another for the log in send
+        Logger.flush()
+        Logger.flush()
+      end)
+
+    assert log =~ ~r"\[warn\]  Attempted to log \d+ messages, which is above :discard_threshold"
+    assert log =~ ~r"\[warn\]  Attempted to log \d+ messages, which is below :discard_threshold"
+  after
+    :sys.resume(Logger)
+    assert :ok = Logger.configure(discard_threshold: 500)
+  end
+
+  test "restarts Logger.Config on Logger exits" do
+    Process.whereis(Logger) |> Process.exit(:kill)
+    wait_for_logger()
+    wait_for_handler(Logger, Logger.Config)
+  end
+
+  test "updates config on config_change/3" do
+    :ok = Logger.configure(level: :debug)
+
+    try do
+      Application.put_env(:logger, :level, :error)
+      assert Logger.App.config_change([level: :error], [], []) === :ok
+      assert Logger.level() === :error
+    after
+      Logger.configure(level: :debug)
+    end
+  end
 end
diff --git a/lib/mix/lib/mix.ex b/lib/mix/lib/mix.ex
index f798ccf5a..50a9ec70a 100644
--- a/lib/mix/lib/mix.ex
+++ b/lib/mix/lib/mix.ex
@@ -116,6 +116,8 @@ defmodule Mix do
   is `:host` but it can be set via the `MIX_TARGET` environment variable.
   The target can be read via `Mix.target/0`.
 
+  This feature is considered experimental and may change in future releases.
+
   ## Aliases
 
   Aliases are shortcuts or tasks specific to the current project.
diff --git a/lib/mix/lib/mix/compilers/elixir.ex b/lib/mix/lib/mix/compilers/elixir.ex
index 5b1860379..d254f71c6 100644
--- a/lib/mix/lib/mix/compilers/elixir.ex
+++ b/lib/mix/lib/mix/compilers/elixir.ex
@@ -468,7 +468,7 @@ defmodule Mix.Compilers.Elixir do
       file = Path.absname(source)
 
       for {line, message} <- warnings do
-        :elixir_errors.warn(line, file, message)
+        :elixir_errors.erl_warn(line, file, message)
       end
     end
   end
diff --git a/lib/mix/lib/mix/compilers/erlang.ex b/lib/mix/lib/mix/compilers/erlang.ex
index 683c682c6..5af388f96 100644
--- a/lib/mix/lib/mix/compilers/erlang.ex
+++ b/lib/mix/lib/mix/compilers/erlang.ex
@@ -56,7 +56,6 @@ defmodule Mix.Compilers.Erlang do
 
   def compile(manifest, mappings, src_ext, dest_ext, force, callback)
       when is_boolean(force) or is_nil(force) do
-    # TODO: Remove this on v2.0
     IO.warn(
       "Mix.Compilers.Erlang.compile/6 with a boolean or nil as 5th argument is deprecated, " <>
         "please pass [force: true] or [] instead"
@@ -227,7 +226,6 @@ defmodule Mix.Compilers.Erlang do
   end
 
   defp do_compile({input, output}, callback, timestamp, verbose) do
-    # TODO: Remove deprecated {:ok, _} and :error on 2.0
     case callback.(input, output) do
       {:ok, _, warnings} ->
         File.touch!(output, timestamp)
diff --git a/lib/mix/lib/mix/config.ex b/lib/mix/lib/mix/config.ex
index a7de950be..998299a01 100644
--- a/lib/mix/lib/mix/config.ex
+++ b/lib/mix/lib/mix/config.ex
@@ -1,39 +1,56 @@
 defmodule Mix.Config do
   @moduledoc ~S"""
-  Module for defining, reading and merging app configurations.
+  A simple configuration API and functions for managing config files.
+
+  ## Setting configuration
 
   Most commonly, this module is used to define your own configuration:
 
       use Mix.Config
 
-      config :plug,
+      config :root_key,
         key1: "value1",
         key2: "value2"
 
-      import_config "#{Mix.env}.exs"
+      import_config "#{Mix.env()}.exs"
+
+  `use Mix.Config` will import the functions `config/2`, `config/3`
+  and `import_config/1` to help you manage your configuration.
+
+  ## Evaluating configuration
+
+  Once a configuration is written to a file, the functions in this
+  module can be used to read and merge said configuration. The `eval!/2`
+  function allows you evaluate a given configuration file and `merge/2`
+  allows to deep merge the results of multiple configurations. Those
+  functions should not be invoked by users writing configurations but
+  rather by library authors.
+
+  ## Examples
 
-  All `config/*` macros, including `import_config/1`, are used
-  to help define such configuration files.
+  The most common use of `Mix.Config` is to define application
+  configuration so that `Application.get_env/3` and other `Application`
+  functions can be used to retrieve or further change them.
 
-  Furthermore, this module provides functions like `read!/1`,
-  `merge/2` and friends which help manipulate configurations
-  in general.
+  Application config files are typically placed in the `config/`
+  directory of your Mix projects. For example, the following config
 
-  Configuration set using `Mix.Config` will set the application environment, so
-  that `Application.get_env/3` and other `Application` functions can be used
-  at run or compile time to retrieve or change the configuration.
+      # config/config.exs
+      config :my_app, :key, "value"
 
-  For example, the `:key1` value from the application `:plug` (see example above) can be
-  retrieved with:
+  will be automatically loaded by Mix and persisted into the
+  `:my_app`'s application environment, which can be accessed in
+  its source code as follows:
 
-      "value1" = Application.fetch_env!(:plug, :key1)
+      "value" = Application.fetch_env!(:my_app, :key1)
 
   """
 
+  # TODO: Break the user API and the reader API apart.
+
   @doc false
   defmacro __using__(_) do
     quote do
-      # TODO: If we split User API from Mix API, we no longer need to use Mix.Config.
       import Mix.Config, only: [config: 2, config: 3, import_config: 1]
     end
   end
@@ -74,50 +91,47 @@ defmodule Mix.Config do
   ## User API
 
   @doc """
-  Configures the given application.
+  Configures the given `root_key`.
 
   Keyword lists are always deep merged.
 
   ## Examples
 
   The given `opts` are merged into the existing configuration
-  for the given `app`. Conflicting keys are overridden by the
-  ones specified in `opts`. For example, the declaration below:
-
-      config :lager,
-        log_level: :warn,
-        mode: :truncate
-
-      config :lager,
-        log_level: :info,
-        threshold: 1024
+  for the given `root_key`. Conflicting keys are overridden by the
+  ones specified in `opts`. For example, the application
+  configuration below
 
-  Will have a final configuration of:
+      config :logger,
+        level: :warn,
+        backends: [:console]
 
-      [log_level: :info, mode: :truncate, threshold: 1024]
+      config :logger,
+        level: :info,
+        truncate: 1024
 
-  This final configuration can be retrieved at run or compile time:
+  will have a final configuration for `:logger` of:
 
-      Application.get_all_env(:lager)
+      [level: :info, backends: [:console], truncate: 1024]
 
   """
-  def config(app, opts) when is_atom(app) and is_list(opts) do
+  def config(root_key, opts) when is_atom(root_key) and is_list(opts) do
     get_config!()
-    |> merge([{app, opts}])
+    |> merge([{root_key, opts}])
     |> put_config()
   end
 
   @doc """
-  Configures the given key for the given application.
+  Configures the given `key` for the given `root_key`.
 
   Keyword lists are always deep merged.
 
   ## Examples
 
   The given `opts` are merged into the existing values for `key`
-  in the given `app`. Conflicting keys are overridden by the
-  ones specified in `opts`. For example, given the two configurations
-  below:
+  in the given `root_key`. Conflicting keys are overridden by the
+  ones specified in `opts`. For example, the application
+  configuration below
 
       config :ecto, Repo,
         log_level: :warn,
@@ -127,19 +141,15 @@ defmodule Mix.Config do
         log_level: :info,
         pool_size: 10
 
-  the final value of the configuration for the `Repo` key in the `:ecto`
-  application will be:
+  will have a final value of the configuration for the `Repo`
+  key in the `:ecto` application of:
 
       [log_level: :info, pool_size: 10, adapter: Ecto.Adapters.Postgres]
 
-  This final value can be retrieved at runtime or compile time with:
-
-      Application.get_env(:ecto, Repo)
-
   """
-  def config(app, key, opts) when is_atom(app) do
+  def config(root_key, key, opts) when is_atom(root_key) do
     get_config!()
-    |> merge([{app, [{key, opts}]}])
+    |> merge([{root_key, [{key, opts}]}])
     |> put_config()
   end
 
@@ -159,7 +169,7 @@ defmodule Mix.Config do
 
   This is often used to emulate configuration across environments:
 
-      import_config "#{Mix.env}.exs"
+      import_config "#{Mix.env()}.exs"
 
   Or to import files from children in umbrella projects:
 
@@ -212,6 +222,7 @@ defmodule Mix.Config do
 
   It returns a tuple with the configuration and the imported paths.
   """
+  @spec eval!(Path.t(), [Path.t()]) :: {keyword, [Path.t()]}
   def eval!(file, imported_paths \\ []) do
     previous_config = put_config([])
     previous_files = put_files(imported_paths)
@@ -232,10 +243,22 @@ defmodule Mix.Config do
     end
   end
 
-  @doc false
-  @deprecated "Use eval!/2 instead"
-  def read!(file, loaded_paths \\ []) do
-    eval!(file, loaded_paths) |> elem(0)
+  @doc """
+  Reads the configuration file.
+
+  The same as `eval!/2` but only returns the configuration
+  in the given file, without returning the imported paths.
+
+  It exists for convenience purposes. For example, you could
+  invoke it inside your `mix.exs` to read some external data
+  you decided to move to a configuration file:
+
+      releases: Mix.Config.read!("rel/releases.exs")
+
+  """
+  @spec read!(Path.t(), [Path.t()]) :: keyword
+  def read!(file, imported_paths \\ []) do
+    eval!(file, imported_paths) |> elem(0)
   end
 
   @doc false
@@ -298,6 +321,7 @@ defmodule Mix.Config do
       #=> [:logger, :my_app]
 
   """
+  # TODO: Deprecate this once merge_env is added to Application
   def persist(config) do
     for {app, kw} <- config do
       for {k, v} <- kw do
@@ -311,15 +335,19 @@ defmodule Mix.Config do
   @doc """
   Merges two configurations.
 
-  The configuration of each application is merged together
-  with the values in the second one having higher preference
-  than the first in case of conflicts.
+  The configurations are merged together with the values in
+  the second one having higher preference than the first in
+  case of conflicts. In case both values are set to keyword
+  lists, it deep merges them.
 
   ## Examples
 
       iex> Mix.Config.merge([app: [k: :v1]], [app: [k: :v2]])
       [app: [k: :v2]]
 
+      iex> Mix.Config.merge([app: [k: [v1: 1, v2: 2]]], [app: [k: [v2: :a, v3: :b]]])
+      [app: [k: [v1: 1, v2: :a, v3: :b]]]
+
       iex> Mix.Config.merge([app1: []], [app2: []])
       [app1: [], app2: []]
 
diff --git a/lib/mix/lib/mix/dep.ex b/lib/mix/lib/mix/dep.ex
index 9952b9dcf..4b1d832cf 100644
--- a/lib/mix/lib/mix/dep.ex
+++ b/lib/mix/lib/mix/dep.ex
@@ -165,13 +165,6 @@ defmodule Mix.Dep do
     end
   end
 
-  # TODO: Remove this on v1.8
-  @doc false
-  @deprecated "Mix.Dep.loaded/1 was private API and you should not use it"
-  def loaded(opts) do
-    load_on_environment(opts)
-  end
-
   @doc """
   Returns loaded dependencies recursively on the given environment.
 
diff --git a/lib/mix/lib/mix/project.ex b/lib/mix/lib/mix/project.ex
index a3e576091..ee3bd03bf 100644
--- a/lib/mix/lib/mix/project.ex
+++ b/lib/mix/lib/mix/project.ex
@@ -231,7 +231,7 @@ defmodule Mix.Project do
   When called with no arguments, tells whether the current project is
   an umbrella project.
   """
-  @spec umbrella?() :: boolean
+  @spec umbrella?(keyword) :: boolean
   def umbrella?(config \\ config()) do
     config[:apps_path] != nil
   end
@@ -255,7 +255,7 @@ defmodule Mix.Project do
 
   """
   @doc since: "1.4.0"
-  @spec apps_paths() :: %{optional(atom) => Path.t()} | nil
+  @spec apps_paths(keyword) :: %{optional(atom) => Path.t()} | nil
   def apps_paths(config \\ config()) do
     if apps_path = config[:apps_path] do
       key = {:apps_paths, Mix.Project.get!()}
@@ -326,7 +326,7 @@ defmodule Mix.Project do
   ## Examples
 
       Mix.Project.in_project(:my_app, "/path/to/my_app", fn module ->
-        "Mix project is: #{inspect module}"
+        "Mix project is: #{inspect(module)}"
       end)
       #=> "Mix project is: MyApp.MixProject"
 
@@ -380,7 +380,7 @@ defmodule Mix.Project do
 
   ## Options
 
-    * `:depth` - only return dependencies to the depth level,
+    * `:depth` - only returns dependencies to the depth level,
       a depth of 1 will only return top-level dependencies
     * `:parents` - starts the dependency traversal from the
       given parents instead of the application root
@@ -665,10 +665,7 @@ defmodule Mix.Project do
     end
   end
 
-  @doc """
-  Returns all load paths for the given project.
-  """
-  @spec load_paths(keyword) :: [Path.t()]
+  @deprecated "Use Mix.Project.compile_path/1 instead"
   def load_paths(config \\ config()) do
     if umbrella?(config) do
       []
diff --git a/lib/mix/lib/mix/tasks/cmd.ex b/lib/mix/lib/mix/tasks/cmd.ex
index f4cf0ed38..a1652309c 100644
--- a/lib/mix/lib/mix/tasks/cmd.ex
+++ b/lib/mix/lib/mix/tasks/cmd.ex
@@ -22,7 +22,7 @@ defmodule Mix.Tasks.Cmd do
   This task is automatically reenabled, so it can be called multiple times
   with different arguments.
 
-  ## Zombie OS processes
+  ## Zombie operating system processes
 
   Beware that the Erlang VM does not terminate child processes
   when it shuts down. Therefore, if you use `mix cmd` to start
@@ -31,7 +31,7 @@ defmodule Mix.Tasks.Cmd do
 
   A solution is to make sure the child processes listen to the
   standard input and terminate when standard input is closed.
-  We discuss this topic at length in the "Zombie OS processes"
+  We discuss this topic at length in the "Zombie operating system processes"
   of the `Port` module documentation.
   """
 
diff --git a/lib/mix/lib/mix/tasks/compile.app.ex b/lib/mix/lib/mix/tasks/compile.app.ex
index 943cf9e3d..c123bd6c9 100644
--- a/lib/mix/lib/mix/tasks/compile.app.ex
+++ b/lib/mix/lib/mix/tasks/compile.app.ex
@@ -230,7 +230,7 @@ defmodule Mix.Tasks.Compile.App do
 
       {:id, value} ->
         unless is_list(value) do
-          Mix.raise("Application id (:id) is not a character list, got: " <> inspect(value))
+          Mix.raise("Application ID (:id) is not a character list, got: " <> inspect(value))
         end
 
       {:vsn, value} ->
diff --git a/lib/mix/lib/mix/tasks/compile.xref.ex b/lib/mix/lib/mix/tasks/compile.xref.ex
index 8c9fa3450..26f40e587 100644
--- a/lib/mix/lib/mix/tasks/compile.xref.ex
+++ b/lib/mix/lib/mix/tasks/compile.xref.ex
@@ -10,7 +10,7 @@ defmodule Mix.Tasks.Compile.Xref do
   Performs remote dispatch checking.
 
   It uses `mix xref` to check if any remote call does not exist or is
-  deprecated, and emits a warnings in such cases. This tasks does not show
+  deprecated, and emits warnings in such cases. This tasks does not show
   deprecated local calls (a call to a deprecated function or macro in the
   same module) nor calls to deprecated functionality in Elixir itself.
 
diff --git a/lib/mix/lib/mix/tasks/deps.compile.ex b/lib/mix/lib/mix/tasks/deps.compile.ex
index e59973c02..31e0bd038 100644
--- a/lib/mix/lib/mix/tasks/deps.compile.ex
+++ b/lib/mix/lib/mix/tasks/deps.compile.ex
@@ -12,10 +12,11 @@ defmodule Mix.Tasks.Deps.Compile do
   This task attempts to detect if the project contains one of
   the following files and act accordingly:
 
-    * `mix.exs`      - invokes `mix compile`
+    * `mix.exs` - invokes `mix compile`
     * `rebar.config` - invokes `rebar compile`
-    * `Makefile.win` - invokes `nmake /F Makefile.win` (only on Windows)
-    * `Makefile`     - invokes `gmake` on FreeBSD and OpenBSD, invokes `make` on any other OS (except on Windows)
+    * `Makefile.win`- invokes `nmake /F Makefile.win` (only on Windows)
+    * `Makefile` - invokes `gmake` on FreeBSD and OpenBSD, invokes `make`
+      on any other operating system (except on Windows)
 
   The compilation can be customized by passing a `compile` option
   in the dependency:
diff --git a/lib/mix/lib/mix/tasks/deps.ex b/lib/mix/lib/mix/tasks/deps.ex
index 3972bf1cb..f6ce1a739 100644
--- a/lib/mix/lib/mix/tasks/deps.ex
+++ b/lib/mix/lib/mix/tasks/deps.ex
@@ -33,7 +33,8 @@ defmodule Mix.Tasks.Deps do
 
   By specifying such dependencies, Mix will automatically install
   Hex (if it wasn't previously installed) and download a package
-  suitable to your project.
+  suitable to your project. Note Hex expects the dependency
+  requirement to always be given and it will warn otherwise.
 
   Mix also supports Git and path dependencies:
 
@@ -82,8 +83,7 @@ defmodule Mix.Tasks.Deps do
     * `:targets` - the dependency is made available only for the given targets.
       By default the dependency will be available in all environments. The value
       of this option can either be a single target (like `:host`) or a list of
-      environments (like `[:host, :rpi3]`). This option is **experimental**
-      and it may change behaviour or be removed in future releases.
+      environments (like `[:host, :rpi3]`)
 
     * `:override` - if set to `true` the dependency will override any other
       definitions of itself by other dependencies
diff --git a/lib/mix/lib/mix/tasks/deps.loadpaths.ex b/lib/mix/lib/mix/tasks/deps.loadpaths.ex
index 1735494d8..178fc9844 100644
--- a/lib/mix/lib/mix/tasks/deps.loadpaths.ex
+++ b/lib/mix/lib/mix/tasks/deps.loadpaths.ex
@@ -16,7 +16,7 @@ defmodule Mix.Tasks.Deps.Loadpaths do
 
     * `--no-deps-check` - does not check or compile deps, only load available ones
     * `--no-compile` - does not compile dependencies
-    * `--no-load-deps` - do not load deps from the code path
+    * `--no-load-deps` - does not load deps from the code path
 
   """
 
diff --git a/lib/mix/lib/mix/tasks/format.ex b/lib/mix/lib/mix/tasks/format.ex
index 17ec01121..38d64d61e 100644
--- a/lib/mix/lib/mix/tasks/format.ex
+++ b/lib/mix/lib/mix/tasks/format.ex
@@ -211,7 +211,7 @@ defmodule Mix.Tasks.Format do
     end
   end
 
-  def read_manifest(manifest) do
+  defp read_manifest(manifest) do
     with {:ok, binary} <- File.read(manifest),
          {:ok, {@manifest_vsn, entry, sources}} <- safe_binary_to_term(binary),
          expanded_sources = Enum.flat_map(sources, &Path.wildcard(&1, match_dot: true)),
diff --git a/lib/mix/lib/mix/tasks/loadpaths.ex b/lib/mix/lib/mix/tasks/loadpaths.ex
index e400fcb54..b8a101429 100644
--- a/lib/mix/lib/mix/tasks/loadpaths.ex
+++ b/lib/mix/lib/mix/tasks/loadpaths.ex
@@ -73,7 +73,7 @@ defmodule Mix.Tasks.Loadpaths do
       _ -> :ok
     end
 
-    Enum.each(Mix.Project.load_paths(config), &Code.prepend_path(&1))
+    Code.prepend_path(Mix.Project.compile_path(config))
   end
 
   defp rm_rf_app(config) do
diff --git a/lib/mix/lib/mix/tasks/new.ex b/lib/mix/lib/mix/tasks/new.ex
index 13c60d352..4abdf306f 100644
--- a/lib/mix/lib/mix/tasks/new.ex
+++ b/lib/mix/lib/mix/tasks/new.ex
@@ -370,6 +370,7 @@ defmodule Mix.Tasks.New do
     def project do
       [
         apps_path: "apps",
+        version: "0.1.0",
         start_permanent: Mix.env() == :prod,
         deps: deps()
       ]
@@ -472,7 +473,6 @@ defmodule Mix.Tasks.New do
     use Application
 
     def start(_type, _args) do
-      # List all child processes to be supervised
       children = [
         # Starts a worker by calling: <%= @mod %>.Worker.start_link(arg)
         # {<%= @mod %>.Worker, arg}
diff --git a/lib/mix/lib/mix/tasks/profile.eprof.ex b/lib/mix/lib/mix/tasks/profile.eprof.ex
index ed9ee6642..251c43502 100644
--- a/lib/mix/lib/mix/tasks/profile.eprof.ex
+++ b/lib/mix/lib/mix/tasks/profile.eprof.ex
@@ -320,7 +320,7 @@ defmodule Mix.Tasks.Profile.Eprof do
     :io.format(@format, to_print)
   end
 
-  def print_function_count(count) do
+  defp print_function_count(count) do
     IO.puts("Profile done over #{count} matching functions")
   end
 end
diff --git a/lib/mix/lib/mix/tasks/run.ex b/lib/mix/lib/mix/tasks/run.ex
index ee89d86aa..83c4da81d 100644
--- a/lib/mix/lib/mix/tasks/run.ex
+++ b/lib/mix/lib/mix/tasks/run.ex
@@ -98,7 +98,6 @@ defmodule Mix.Tasks.Run do
           (String.t() -> term())
         ) :: :ok
   def run(args, opts, head, expr_evaluator, file_evaluator) do
-    # TODO: Remove on v2.0
     opts =
       Enum.flat_map(opts, fn
         {:parallel_require, value} ->
diff --git a/lib/mix/lib/mix/tasks/test.ex b/lib/mix/lib/mix/tasks/test.ex
index e4e8431f1..18d0bb346 100644
--- a/lib/mix/lib/mix/tasks/test.ex
+++ b/lib/mix/lib/mix/tasks/test.ex
@@ -216,21 +216,25 @@ defmodule Mix.Tasks.Test do
 
   It differs in that the test suite will fail if no tests are executed when the `--only` option is used.
 
-  In case a single file is being tested, it is possible to pass a specific
-  line number:
+  In case a single file is being tested, it is possible to pass one or more specific
+  line numbers to run only those given tests:
 
       mix test test/some/particular/file_test.exs:12
 
   Which is equivalent to:
 
-      mix test --only line:12 test/some/particular/file_test.exs
+      mix test --exclude test --include line:12 test/some/particular/file_test.exs
 
-  If the given line starts a `describe` block, the line filter runs all tests in it.
-  Otherwise, it runs the closest test on or before the given line number.
+  Or:
+
+      mix test test/some/particular/file_test.exs:12:24
+
+  Which is equivalent to:
 
-  Note that in the case where a single file contains more than one test module (test case),
-  the line filter applies to every test case before the given line number. Thus, more
-  than one test might be executed for the run.
+      mix test --exclude test --include line:12 --include line:24 test/some/particular/file_test.exs
+
+  If a given line starts a `describe` block, that line filter runs all tests in it.
+  Otherwise, it runs the closest test on or before the given line number.
 
   ## Configuration
 
@@ -505,7 +509,11 @@ defmodule Mix.Tasks.Test do
   end
 
   defp parse_files(files, _test_paths) do
-    files
+    if Enum.any?(files, &match?({_, [_ | _]}, ExUnit.Filters.parse_path(&1))) do
+      Mix.raise("Line numbers can only be used when running a single test file")
+    else
+      files
+    end
   end
 
   defp parse_filters(opts, key) do
@@ -532,7 +540,7 @@ defmodule Mix.Tasks.Test do
     end
   end
 
-  def formatter_opts(opts) do
+  defp formatter_opts(opts) do
     if Keyword.has_key?(opts, :formatter) do
       formatters =
         opts
diff --git a/lib/mix/test/mix/config_test.exs b/lib/mix/test/mix/config_test.exs
index 1cb196890..4dc95b822 100644
--- a/lib/mix/test/mix/config_test.exs
+++ b/lib/mix/test/mix/config_test.exs
@@ -121,6 +121,17 @@ defmodule Mix.ConfigTest do
                  fn -> Mix.Config.eval!(fixture_path("configs/bad_import.exs")) end
   end
 
+  test "read!/2" do
+    assert Mix.Config.read!(fixture_path("configs/good_kw.exs")) ==
+             [my_app: [key: :value]]
+
+    assert Mix.Config.read!(fixture_path("configs/good_config.exs")) ==
+             [my_app: [key: :value]]
+
+    assert Mix.Config.read!(fixture_path("configs/good_import.exs")) ==
+             [my_app: [key: :value]]
+  end
+
   test "persist/1" do
     assert Application.get_env(:my_app, :key) == nil
     Mix.Config.persist(my_app: [key: :value])
diff --git a/lib/mix/test/mix/tasks/compile.elixir_test.exs b/lib/mix/test/mix/tasks/compile.elixir_test.exs
index 38a47f294..37f080699 100644
--- a/lib/mix/test/mix/tasks/compile.elixir_test.exs
+++ b/lib/mix/test/mix/tasks/compile.elixir_test.exs
@@ -123,7 +123,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
       Mix.shell().flush
       purge([A, B])
 
-      future = {{2020, 1, 1}, {0, 0, 0}}
+      future = {{2038, 1, 1}, {0, 0, 0}}
       File.touch!("lib/a.ex", future)
       Mix.Tasks.Compile.Elixir.run(["--verbose"])
 
@@ -176,7 +176,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
       Mix.shell().flush
       purge([A, B])
 
-      future = {{2020, 1, 1}, {0, 0, 0}}
+      future = {{2038, 1, 1}, {0, 0, 0}}
       File.touch!("lib/b.ex", future)
       Mix.Tasks.Compile.Elixir.run(["--verbose"])
 
@@ -225,7 +225,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
       purge([A, B])
 
       # Update local existing resource
-      File.touch!("lib/a.eex", {{2030, 1, 1}, {0, 0, 0}})
+      File.touch!("lib/a.eex", {{2038, 1, 1}, {0, 0, 0}})
       assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
       assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
       refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
@@ -237,7 +237,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
       purge([A, B])
 
       # Update external existing resource
-      File.touch!(tmp, {{2030, 1, 1}, {0, 0, 0}})
+      File.touch!(tmp, {{2038, 1, 1}, {0, 0, 0}})
       assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
       assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
       refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
@@ -327,7 +327,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
       assert_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
       purge([A, B])
 
-      future = {{2020, 1, 1}, {0, 0, 0}}
+      future = {{2038, 1, 1}, {0, 0, 0}}
       File.touch!("lib/a.ex", future)
 
       assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
@@ -432,7 +432,7 @@ defmodule Mix.Tasks.Compile.ElixirTest do
       Mix.shell().flush
       purge([A, B])
 
-      future = {{2020, 1, 1}, {0, 0, 0}}
+      future = {{2038, 1, 1}, {0, 0, 0}}
       File.touch!("lib/a.ex", future)
       Mix.Tasks.Compile.Elixir.run(["--verbose"])
 
diff --git a/lib/mix/test/mix/tasks/compile.xref_test.exs b/lib/mix/test/mix/tasks/compile.xref_test.exs
index b2f28e3cb..29737d152 100644
--- a/lib/mix/test/mix/tasks/compile.xref_test.exs
+++ b/lib/mix/test/mix/tasks/compile.xref_test.exs
@@ -44,7 +44,7 @@ defmodule Mix.Tasks.Compile.XrefTest do
       end)
 
       [manifest] = Mix.Tasks.Compile.Elixir.manifests()
-      future = {{2020, 1, 1}, {0, 0, 0}}
+      future = {{2038, 1, 1}, {0, 0, 0}}
       File.touch!(manifest, future)
 
       Mix.Task.reenable("xref")
diff --git a/lib/mix/test/mix/tasks/format_test.exs b/lib/mix/test/mix/tasks/format_test.exs
index 87f17d89d..60b5ba904 100644
--- a/lib/mix/test/mix/tasks/format_test.exs
+++ b/lib/mix/test/mix/tasks/format_test.exs
@@ -368,7 +368,7 @@ defmodule Mix.Tasks.FormatTest do
       [inputs: "a.ex"]
       """)
 
-      File.touch!("lib/sub/.formatter.exs", {{2030, 1, 1}, {0, 0, 0}})
+      File.touch!("lib/sub/.formatter.exs", {{2038, 1, 1}, {0, 0, 0}})
       Mix.Tasks.Format.run([])
 
       assert File.read!("lib/sub/a.ex") == """
@@ -388,7 +388,7 @@ defmodule Mix.Tasks.FormatTest do
       other_fun :baz
       """)
 
-      File.touch!("lib/extra/.formatter.exs", {{2030, 1, 1}, {0, 0, 0}})
+      File.touch!("lib/extra/.formatter.exs", {{2038, 1, 1}, {0, 0, 0}})
       Mix.Tasks.Format.run([])
 
       formatter_opts = Mix.Tasks.Format.formatter_opts_for_file("lib/extra/a.ex")
diff --git a/lib/mix/test/mix/tasks/loadconfig_test.exs b/lib/mix/test/mix/tasks/loadconfig_test.exs
index 639ae2feb..2216c2e4a 100644
--- a/lib/mix/test/mix/tasks/loadconfig_test.exs
+++ b/lib/mix/test/mix/tasks/loadconfig_test.exs
@@ -63,7 +63,7 @@ defmodule Mix.Tasks.LoadconfigTest do
       assert Mix.Project.config_mtime() > mtime
 
       # Touching it should not have any deadlocks
-      File.touch!(config, {{2030, 1, 1}, {0, 0, 0}})
+      File.touch!(config, {{2038, 1, 1}, {0, 0, 0}})
       Mix.Task.run("loadconfig", [config])
       assert config in Mix.Project.config_files()
       assert Mix.Project.config_mtime() > mtime
diff --git a/lib/mix/test/mix/tasks/test_test.exs b/lib/mix/test/mix/tasks/test_test.exs
index 064c9da5e..5466c0b1b 100644
--- a/lib/mix/test/mix/tasks/test_test.exs
+++ b/lib/mix/test/mix/tasks/test_test.exs
@@ -257,6 +257,38 @@ defmodule Mix.Tasks.TestTest do
     end)
   end
 
+  test "raises an exception if line numbers are given with multiple files" do
+    Mix.env(:test)
+
+    in_fixture("test_failed", fn ->
+      Mix.Project.in_project(:test_only_failures, ".", fn _ ->
+        # fails if a line number is given for one file
+        assert_raise(
+          Mix.Error,
+          "Line numbers can only be used when running a single test file",
+          fn ->
+            Mix.Tasks.Test.run([
+              "test/only_failing_test_failed.exs",
+              "test/passing_and_failing_test_failed.exs:4"
+            ])
+          end
+        )
+
+        # fails if a line number is given for both files
+        assert_raise(
+          Mix.Error,
+          "Line numbers can only be used when running a single test file",
+          fn ->
+            Mix.Tasks.Test.run([
+              "test/only_failing_test_failed.exs:4",
+              "test/passing_and_failing_test_failed.exs:4"
+            ])
+          end
+        )
+      end)
+    end)
+  end
+
   defp receive_until_match(port, expected, acc) do
     receive do
       {^port, {:data, output}} ->
diff --git a/lib/mix/test/mix/utils_test.exs b/lib/mix/test/mix/utils_test.exs
index a8c098a29..aaac490b4 100644
--- a/lib/mix/test/mix/utils_test.exs
+++ b/lib/mix/test/mix/utils_test.exs
@@ -38,8 +38,8 @@ defmodule Mix.UtilsTest do
   end
 
   test "extract stale" do
-    # 2030-01-01 00:00:00
-    time = 1_893_456_000
+    # 2038-01-01 00:00:00
+    time = 2_145_916_800
     assert Mix.Utils.extract_stale([__ENV__.file], [time]) == []
 
     # 2000-01-01 00:00:00