From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Wed, 17 Apr 2024 14:02:58 +0200
Subject: Adding upstream version 1.64.0+dfsg1.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 .../auxiliary/unboxed-closures-cross-crate.rs      |  16 +++
 src/test/ui/unboxed-closures/issue-18652.rs        |  10 ++
 src/test/ui/unboxed-closures/issue-18661.rs        |  19 ++++
 src/test/ui/unboxed-closures/issue-30906.rs        |  22 ++++
 src/test/ui/unboxed-closures/issue-30906.stderr    |  11 ++
 src/test/ui/unboxed-closures/issue-53448.rs        |  16 +++
 .../ui/unboxed-closures/type-id-higher-rank.rs     |  72 +++++++++++++
 .../unboxed-closure-feature-gate.rs                |  20 ++++
 .../unboxed-closure-feature-gate.stderr            |  12 +++
 .../unboxed-closure-illegal-move.rs                |  38 +++++++
 .../unboxed-closure-illegal-move.stderr            |  43 ++++++++
 .../unboxed-closure-immutable-capture.rs           |  17 +++
 .../unboxed-closure-immutable-capture.stderr       |  75 +++++++++++++
 .../unboxed-closure-no-cyclic-sig.rs               |   9 ++
 .../unboxed-closure-no-cyclic-sig.stderr           |  14 +++
 .../ui/unboxed-closures/unboxed-closure-region.rs  |  11 ++
 .../unboxed-closures/unboxed-closure-region.stderr |  16 +++
 .../unboxed-closure-sugar-default.rs               |  28 +++++
 .../unboxed-closure-sugar-default.stderr           |  15 +++
 .../unboxed-closure-sugar-equiv.rs                 |  48 +++++++++
 .../unboxed-closure-sugar-equiv.stderr             |  15 +++
 .../unboxed-closure-sugar-lifetime-elision.rs      |  27 +++++
 .../unboxed-closure-sugar-lifetime-elision.stderr  |  24 +++++
 .../unboxed-closure-sugar-not-used-on-fn.rs        |  11 ++
 .../unboxed-closure-sugar-not-used-on-fn.stderr    |  21 ++++
 .../unboxed-closure-sugar-region.rs                |  36 +++++++
 .../unboxed-closure-sugar-region.stderr            |  15 +++
 .../unboxed-closure-sugar-used-on-struct-1.rs      |  13 +++
 .../unboxed-closure-sugar-used-on-struct-1.stderr  |  26 +++++
 .../unboxed-closure-sugar-used-on-struct-3.rs      |  18 ++++
 .../unboxed-closure-sugar-used-on-struct-3.stderr  |  14 +++
 .../unboxed-closure-sugar-used-on-struct.rs        |  12 +++
 .../unboxed-closure-sugar-used-on-struct.stderr    |  26 +++++
 ...-sugar-wrong-number-number-type-parameters-1.rs |   8 ++
 ...ar-wrong-number-number-type-parameters-1.stderr |   9 ++
 ...-sugar-wrong-number-number-type-parameters-3.rs |  10 ++
 ...ar-wrong-number-number-type-parameters-3.stderr |  24 +++++
 ...re-sugar-wrong-number-number-type-parameters.rs |  28 +++++
 ...ugar-wrong-number-number-type-parameters.stderr |  92 ++++++++++++++++
 .../unboxed-closure-sugar-wrong-trait.rs           |   9 ++
 .../unboxed-closure-sugar-wrong-trait.stderr       |  24 +++++
 .../unboxed-closures-all-traits.rs                 |  21 ++++
 .../unboxed-closures-blanket-fn-mut.rs             |  27 +++++
 .../unboxed-closures-blanket-fn.rs                 |  27 +++++
 .../unboxed-closures-borrow-conflict.rs            |  11 ++
 .../unboxed-closures-borrow-conflict.stderr        |  15 +++
 .../ui/unboxed-closures/unboxed-closures-boxed.rs  |  15 +++
 .../ui/unboxed-closures/unboxed-closures-by-ref.rs |  24 +++++
 .../unboxed-closures-call-fn-autoderef.rs          |  18 ++++
 .../unboxed-closures-call-sugar-autoderef.rs       |  15 +++
 ...unboxed-closures-call-sugar-object-autoderef.rs |  15 +++
 .../unboxed-closures-call-sugar-object.rs          |  13 +++
 .../unboxed-closures-counter-not-moved.rs          |  28 +++++
 .../unboxed-closures-counter-not-moved.stderr      |  27 +++++
 .../unboxed-closures-cross-crate.rs                |  14 +++
 .../unboxed-closures-direct-sugary-call.rs         |   8 ++
 .../ui/unboxed-closures/unboxed-closures-drop.rs   | 117 +++++++++++++++++++++
 .../unboxed-closures-extern-fn-hr.rs               |  31 ++++++
 .../unboxed-closures/unboxed-closures-extern-fn.rs |  27 +++++
 .../unboxed-closures-failed-recursive-fn-1.rs      |  37 +++++++
 .../unboxed-closures-failed-recursive-fn-1.stderr  |  60 +++++++++++
 .../unboxed-closures-failed-recursive-fn-2.rs      |  29 +++++
 .../unboxed-closures-failed-recursive-fn-2.stderr  |  17 +++
 .../unboxed-closures-fn-as-fnmut-and-fnonce.rs     |  44 ++++++++
 .../unboxed-closures-fnmut-as-fn.rs                |  29 +++++
 .../unboxed-closures-fnmut-as-fn.stderr            |  19 ++++
 .../unboxed-closures-fnmut-as-fnonce.rs            |  33 ++++++
 .../unboxed-closures/unboxed-closures-generic.rs   |  13 +++
 ...closures-infer-arg-types-from-expected-bound.rs |  23 ++++
 ...es-infer-arg-types-from-expected-object-type.rs |  19 ++++
 ...r-arg-types-w-bound-regs-from-expected-bound.rs |  23 ++++
 ...res-infer-argument-types-two-region-pointers.rs |  20 ++++
 ...infer-argument-types-two-region-pointers.stderr |  12 +++
 .../unboxed-closures-infer-explicit-call-early.rs  |   8 ++
 ...-closures-infer-fn-once-move-from-projection.rs |  16 +++
 ...sures-infer-fn-once-move-from-projection.stderr |  13 +++
 ...ed-closures-infer-fnmut-calling-fnmut-no-mut.rs |  20 ++++
 ...losures-infer-fnmut-calling-fnmut-no-mut.stderr |  25 +++++
 .../unboxed-closures-infer-fnmut-calling-fnmut.rs  |  19 ++++
 .../unboxed-closures-infer-fnmut-missing-mut.rs    |   8 ++
 ...unboxed-closures-infer-fnmut-missing-mut.stderr |  13 +++
 ...nboxed-closures-infer-fnmut-move-missing-mut.rs |   8 ++
 ...ed-closures-infer-fnmut-move-missing-mut.stderr |  13 +++
 .../unboxed-closures-infer-fnmut-move.rs           |  16 +++
 .../unboxed-closures-infer-fnmut.rs                |  15 +++
 .../unboxed-closures-infer-fnonce-call-twice.rs    |  11 ++
 ...unboxed-closures-infer-fnonce-call-twice.stderr |  22 ++++
 ...nboxed-closures-infer-fnonce-move-call-twice.rs |  11 ++
 ...ed-closures-infer-fnonce-move-call-twice.stderr |  22 ++++
 .../unboxed-closures-infer-fnonce-move.rs          |  25 +++++
 .../unboxed-closures-infer-fnonce.rs               |  25 +++++
 .../unboxed-closures-infer-kind.rs                 |  27 +++++
 .../unboxed-closures-infer-recursive-fn.rs         |  45 ++++++++
 .../unboxed-closures-infer-upvar.rs                |  13 +++
 .../unboxed-closures-manual-impl.rs                |  31 ++++++
 .../unboxed-closures-monomorphization.rs           |  26 +++++
 ...ed-closures-move-from-projection-issue-30046.rs |  26 +++++
 .../unboxed-closures-move-mutable.rs               |  31 ++++++
 .../unboxed-closures-move-mutable.stderr           |  19 ++++
 ...-closures-move-some-upvars-in-by-ref-closure.rs |  23 ++++
 .../unboxed-closures-mutate-upvar.rs               |  57 ++++++++++
 .../unboxed-closures-mutate-upvar.stderr           |  43 ++++++++
 ...boxed-closures-mutated-upvar-from-fn-closure.rs |  14 +++
 ...d-closures-mutated-upvar-from-fn-closure.stderr |  16 +++
 .../unboxed-closures/unboxed-closures-prelude.rs   |  18 ++++
 .../unboxed-closures-recursive-fn-using-fn-mut.rs  |  43 ++++++++
 ...boxed-closures-recursive-fn-using-fn-mut.stderr |  12 +++
 .../ui/unboxed-closures/unboxed-closures-simple.rs |  10 ++
 .../unboxed-closures-single-word-env.rs            |  22 ++++
 .../unboxed-closures-static-call-fn-once.rs        |   7 ++
 .../unboxed-closures-static-call-wrong-trait.rs    |   8 ++
 ...unboxed-closures-static-call-wrong-trait.stderr |  11 ++
 .../unboxed-closures-sugar-object.rs               |  25 +++++
 .../unboxed-closures-type-mismatch.rs              |   7 ++
 .../unboxed-closures-type-mismatch.stderr          |  21 ++++
 .../unboxed-closures-unique-type-id.rs             |  26 +++++
 .../unboxed-closures-unsafe-extern-fn.rs           |  34 ++++++
 .../unboxed-closures-unsafe-extern-fn.stderr       |  51 +++++++++
 .../unboxed-closures/unboxed-closures-wrong-abi.rs |  34 ++++++
 .../unboxed-closures-wrong-abi.stderr              |  48 +++++++++
 .../unboxed-closures-wrong-arg-type-extern-fn.rs   |  35 ++++++
 ...nboxed-closures-wrong-arg-type-extern-fn.stderr |  51 +++++++++
 .../unboxed-closures/unboxed-closures-zero-args.rs |   8 ++
 123 files changed, 2937 insertions(+)
 create mode 100644 src/test/ui/unboxed-closures/auxiliary/unboxed-closures-cross-crate.rs
 create mode 100644 src/test/ui/unboxed-closures/issue-18652.rs
 create mode 100644 src/test/ui/unboxed-closures/issue-18661.rs
 create mode 100644 src/test/ui/unboxed-closures/issue-30906.rs
 create mode 100644 src/test/ui/unboxed-closures/issue-30906.stderr
 create mode 100644 src/test/ui/unboxed-closures/issue-53448.rs
 create mode 100644 src/test/ui/unboxed-closures/type-id-higher-rank.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-feature-gate.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-illegal-move.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-region.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-region.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-default.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-region.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-all-traits.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-blanket-fn-mut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-blanket-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-boxed.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-by-ref.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-call-fn-autoderef.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-call-sugar-autoderef.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object-autoderef.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-cross-crate.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-direct-sugary-call.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-drop.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-extern-fn-hr.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-extern-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-fn-as-fnmut-and-fnonce.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fnonce.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-generic.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-bound.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-object-type.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-w-bound-regs-from-expected-bound.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-explicit-call-early.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-kind.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-infer-upvar.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-manual-impl.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-monomorphization.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-move-from-projection-issue-30046.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-move-some-upvars-in-by-ref-closure.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-prelude.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-simple.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-single-word-env.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-static-call-fn-once.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-sugar-object.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-zero-args.rs

(limited to 'src/test/ui/unboxed-closures')

diff --git a/src/test/ui/unboxed-closures/auxiliary/unboxed-closures-cross-crate.rs b/src/test/ui/unboxed-closures/auxiliary/unboxed-closures-cross-crate.rs
new file mode 100644
index 000000000..ac0a74eeb
--- /dev/null
+++ b/src/test/ui/unboxed-closures/auxiliary/unboxed-closures-cross-crate.rs
@@ -0,0 +1,16 @@
+use std::ops::Add;
+
+#[inline]
+pub fn has_closures() -> usize {
+    let x = 1;
+    let mut f = move || x;
+    let y = 1;
+    let g = || y;
+    f() + g()
+}
+
+pub fn has_generic_closures<T: Add<Output=T> + Copy>(x: T, y: T) -> T {
+    let mut f = move || x;
+    let g = || y;
+    f() + g()
+}
diff --git a/src/test/ui/unboxed-closures/issue-18652.rs b/src/test/ui/unboxed-closures/issue-18652.rs
new file mode 100644
index 000000000..59aa01568
--- /dev/null
+++ b/src/test/ui/unboxed-closures/issue-18652.rs
@@ -0,0 +1,10 @@
+// run-pass
+// Tests multiple free variables being passed by value into an unboxed
+// once closure as an optimization by codegen.  This used to hit an
+// incorrect assert.
+
+fn main() {
+    let x = 2u8;
+    let y = 3u8;
+    assert_eq!((move || x + y)(), 5);
+}
diff --git a/src/test/ui/unboxed-closures/issue-18661.rs b/src/test/ui/unboxed-closures/issue-18661.rs
new file mode 100644
index 000000000..e24272432
--- /dev/null
+++ b/src/test/ui/unboxed-closures/issue-18661.rs
@@ -0,0 +1,19 @@
+// run-pass
+// Test that param substitutions from the correct environment are
+// used when codegenning unboxed closure calls.
+
+// pretty-expanded FIXME #23616
+
+pub fn inside<F: Fn()>(c: F) {
+    c();
+}
+
+// Use different number of type parameters and closure type to trigger
+// an obvious ICE when param environments are mixed up
+pub fn outside<A,B>() {
+    inside(|| {});
+}
+
+fn main() {
+    outside::<(),()>();
+}
diff --git a/src/test/ui/unboxed-closures/issue-30906.rs b/src/test/ui/unboxed-closures/issue-30906.rs
new file mode 100644
index 000000000..e2d219e47
--- /dev/null
+++ b/src/test/ui/unboxed-closures/issue-30906.rs
@@ -0,0 +1,22 @@
+#![feature(fn_traits, unboxed_closures)]
+
+fn test<F: for<'x> FnOnce<(&'x str,)>>(_: F) {}
+
+struct Compose<F, G>(F, G);
+impl<T, F, G> FnOnce<(T,)> for Compose<F, G>
+where
+    F: FnOnce<(T,)>,
+    G: FnOnce<(F::Output,)>,
+{
+    type Output = G::Output;
+    extern "rust-call" fn call_once(self, (x,): (T,)) -> G::Output {
+        (self.1)((self.0)(x))
+    }
+}
+
+fn bad<T>(f: fn(&'static str) -> T) {
+    test(Compose(f, |_| {}));
+    //~^ ERROR: implementation of `FnOnce` is not general enough
+}
+
+fn main() {}
diff --git a/src/test/ui/unboxed-closures/issue-30906.stderr b/src/test/ui/unboxed-closures/issue-30906.stderr
new file mode 100644
index 000000000..147a20974
--- /dev/null
+++ b/src/test/ui/unboxed-closures/issue-30906.stderr
@@ -0,0 +1,11 @@
+error: implementation of `FnOnce` is not general enough
+  --> $DIR/issue-30906.rs:18:5
+   |
+LL |     test(Compose(f, |_| {}));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+   |
+   = note: `fn(&'2 str) -> T` must implement `FnOnce<(&'1 str,)>`, for any lifetime `'1`...
+   = note: ...but it actually implements `FnOnce<(&'2 str,)>`, for some specific lifetime `'2`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/unboxed-closures/issue-53448.rs b/src/test/ui/unboxed-closures/issue-53448.rs
new file mode 100644
index 000000000..ea1edf7d4
--- /dev/null
+++ b/src/test/ui/unboxed-closures/issue-53448.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+#![feature(unboxed_closures)]
+
+trait Lt<'a> {
+    type T;
+}
+impl<'a> Lt<'a> for () {
+    type T = ();
+}
+
+fn main() {
+    let v: <() as Lt<'_>>::T = ();
+    let f: &mut dyn FnMut<(_,), Output = ()> = &mut |_: <() as Lt<'_>>::T| {};
+    f(v);
+}
diff --git a/src/test/ui/unboxed-closures/type-id-higher-rank.rs b/src/test/ui/unboxed-closures/type-id-higher-rank.rs
new file mode 100644
index 000000000..1f8aec205
--- /dev/null
+++ b/src/test/ui/unboxed-closures/type-id-higher-rank.rs
@@ -0,0 +1,72 @@
+// run-pass
+// Test that type IDs correctly account for higher-rank lifetimes
+// Also acts as a regression test for an ICE (issue #19791)
+
+use std::any::{Any, TypeId};
+
+struct Struct<'a>(#[allow(unused_tuple_struct_fields)] &'a ());
+trait Trait<'a> {}
+
+fn main() {
+    // Bare fns
+    {
+        let a = TypeId::of::<fn(&'static isize, &'static isize)>();
+        let b = TypeId::of::<for<'a> fn(&'static isize, &'a isize)>();
+        let c = TypeId::of::<for<'a, 'b> fn(&'a isize, &'b isize)>();
+        let d = TypeId::of::<for<'a, 'b> fn(&'b isize, &'a isize)>();
+        assert!(a != b);
+        assert!(a != c);
+        assert!(a != d);
+        assert!(b != c);
+        assert!(b != d);
+        assert_eq!(c, d);
+
+        // Make sure De Bruijn indices are handled correctly
+        let e = TypeId::of::<for<'a> fn(fn(&'a isize) -> &'a isize)>();
+        let f = TypeId::of::<fn(for<'a> fn(&'a isize) -> &'a isize)>();
+        assert!(e != f);
+
+        // Make sure lifetime parameters of items are not ignored.
+        let g = TypeId::of::<for<'a> fn(&'a dyn Trait<'a>) -> Struct<'a>>();
+        let h = TypeId::of::<for<'a> fn(&'a dyn Trait<'a>) -> Struct<'static>>();
+        let i = TypeId::of::<for<'a, 'b> fn(&'a dyn Trait<'b>) -> Struct<'b>>();
+        assert!(g != h);
+        assert!(g != i);
+        assert!(h != i);
+
+        // Make sure lifetime anonymization handles nesting correctly
+        let j = TypeId::of::<fn(for<'a> fn(&'a isize) -> &'a usize)>();
+        let k = TypeId::of::<fn(for<'b> fn(&'b isize) -> &'b usize)>();
+        assert_eq!(j, k);
+    }
+    // Boxed unboxed closures
+    {
+        let a = TypeId::of::<Box<dyn Fn(&'static isize, &'static isize)>>();
+        let b = TypeId::of::<Box<dyn for<'a> Fn(&'static isize, &'a isize)>>();
+        let c = TypeId::of::<Box<dyn for<'a, 'b> Fn(&'a isize, &'b isize)>>();
+        let d = TypeId::of::<Box<dyn for<'a, 'b> Fn(&'b isize, &'a isize)>>();
+        assert!(a != b);
+        assert!(a != c);
+        assert!(a != d);
+        assert!(b != c);
+        assert!(b != d);
+        assert_eq!(c, d);
+
+        // Make sure De Bruijn indices are handled correctly
+        let e = TypeId::of::<Box<dyn for<'a> Fn(Box<dyn Fn(&'a isize) -> &'a isize>)>>();
+        let f = TypeId::of::<Box<dyn Fn(Box<dyn for<'a> Fn(&'a isize) -> &'a isize>)>>();
+        assert!(e != f);
+    }
+    // Raw unboxed closures
+    // Note that every unboxed closure has its own anonymous type,
+    // so no two IDs should equal each other, even when compatible
+    {
+        let a = id(|_: &isize, _: &isize| {});
+        let b = id(|_: &isize, _: &isize| {});
+        assert!(a != b);
+    }
+
+    fn id<T:Any>(_: T) -> TypeId {
+        TypeId::of::<T>()
+    }
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.rs b/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.rs
new file mode 100644
index 000000000..d8b201bf8
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.rs
@@ -0,0 +1,20 @@
+// Check that parenthetical notation is feature-gated except with the
+// `Fn` traits.
+
+use std::marker;
+
+trait Foo<A> {
+    type Output;
+
+    fn dummy(&self, a: A) { }
+}
+
+fn main() {
+    let x: Box<dyn Foo(isize)>;
+    //~^ ERROR parenthetical notation is only stable when used with `Fn`-family
+
+    // No errors with these:
+    let x: Box<dyn Fn(isize)>;
+    let x: Box<dyn FnMut(isize)>;
+    let x: Box<dyn FnOnce(isize)>;
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr b/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr
new file mode 100644
index 000000000..b824d160d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-feature-gate.stderr
@@ -0,0 +1,12 @@
+error[E0658]: parenthetical notation is only stable when used with `Fn`-family traits
+  --> $DIR/unboxed-closure-feature-gate.rs:13:20
+   |
+LL |     let x: Box<dyn Foo(isize)>;
+   |                    ^^^^^^^^^^
+   |
+   = note: see issue #29625 <https://github.com/rust-lang/rust/issues/29625> for more information
+   = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.rs b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.rs
new file mode 100644
index 000000000..ed8d72114
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.rs
@@ -0,0 +1,38 @@
+#![feature(unboxed_closures)]
+
+// Tests that we can't move out of an unboxed closure environment
+// if the upvar is captured by ref or the closure takes self by
+// reference.
+
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
+
+fn main() {
+    // By-ref cases
+    {
+        let x = Box::new(0);
+        let f = to_fn(|| drop(x)); //~ ERROR cannot move
+    }
+    {
+        let x = Box::new(0);
+        let f = to_fn_mut(|| drop(x)); //~ ERROR cannot move
+    }
+    {
+        let x = Box::new(0);
+        let f = to_fn_once(|| drop(x)); // OK -- FnOnce
+    }
+    // By-value cases
+    {
+        let x = Box::new(0);
+        let f = to_fn(move || drop(x)); //~ ERROR cannot move
+    }
+    {
+        let x = Box::new(0);
+        let f = to_fn_mut(move || drop(x)); //~ ERROR cannot move
+    }
+    {
+        let x = Box::new(0);
+        let f = to_fn_once(move || drop(x)); // this one is ok
+    }
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr
new file mode 100644
index 000000000..bfa3061de
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-illegal-move.stderr
@@ -0,0 +1,43 @@
+error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure
+  --> $DIR/unboxed-closure-illegal-move.rs:15:31
+   |
+LL |         let x = Box::new(0);
+   |             - captured outer variable
+LL |         let f = to_fn(|| drop(x));
+   |                       --      ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+   |                       |
+   |                       captured by this `Fn` closure
+
+error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure
+  --> $DIR/unboxed-closure-illegal-move.rs:19:35
+   |
+LL |         let x = Box::new(0);
+   |             - captured outer variable
+LL |         let f = to_fn_mut(|| drop(x));
+   |                           --      ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+   |                           |
+   |                           captured by this `FnMut` closure
+
+error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure
+  --> $DIR/unboxed-closure-illegal-move.rs:28:36
+   |
+LL |         let x = Box::new(0);
+   |             - captured outer variable
+LL |         let f = to_fn(move || drop(x));
+   |                       -------      ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+   |                       |
+   |                       captured by this `Fn` closure
+
+error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure
+  --> $DIR/unboxed-closure-illegal-move.rs:32:40
+   |
+LL |         let x = Box::new(0);
+   |             - captured outer variable
+LL |         let f = to_fn_mut(move || drop(x));
+   |                           -------      ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+   |                           |
+   |                           captured by this `FnMut` closure
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.rs b/src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.rs
new file mode 100644
index 000000000..3eba9c4d4
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.rs
@@ -0,0 +1,17 @@
+// Test that even unboxed closures that are capable of mutating their
+// environment cannot mutate captured variables that have not been
+// declared mutable (#18335)
+
+fn set(x: &mut usize) { *x = 0; }
+
+fn main() {
+    let x = 0;
+    move || x = 1; //~ ERROR cannot assign
+    move || set(&mut x); //~ ERROR cannot borrow
+    move || x = 1; //~ ERROR cannot assign
+    move || set(&mut x); //~ ERROR cannot borrow
+    || x = 1; //~ ERROR cannot assign
+    || set(&mut x); //~ ERROR cannot borrow
+    || x = 1; //~ ERROR cannot assign
+    || set(&mut x); //~ ERROR cannot borrow
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.stderr b/src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.stderr
new file mode 100644
index 000000000..ad5451ced
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-immutable-capture.stderr
@@ -0,0 +1,75 @@
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:9:13
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     move || x = 1;
+   |             ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:10:17
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     move || x = 1;
+LL |     move || set(&mut x);
+   |                 ^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:11:13
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     move || x = 1;
+   |             ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:12:17
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     move || set(&mut x);
+   |                 ^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:13:8
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     || x = 1;
+   |        ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:14:12
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     || set(&mut x);
+   |            ^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:15:8
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     || x = 1;
+   |        ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closure-immutable-capture.rs:16:12
+   |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
+LL |     || set(&mut x);
+   |            ^^^^^^ cannot borrow as mutable
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0594, E0596.
+For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.rs b/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.rs
new file mode 100644
index 000000000..9d0aa4132
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.rs
@@ -0,0 +1,9 @@
+// Test that unboxed closures cannot capture their own type.
+//
+// Also regression test for issue #21410.
+
+fn g<F>(_: F) where F: FnOnce(Option<F>) {}
+
+fn main() {
+    g(|_| {  }); //~ ERROR closure/generator type that references itself
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr b/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr
new file mode 100644
index 000000000..167479270
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr
@@ -0,0 +1,14 @@
+error[E0644]: closure/generator type that references itself
+  --> $DIR/unboxed-closure-no-cyclic-sig.rs:8:7
+   |
+LL |     g(|_| {  });
+   |       ^^^^^^^^ cyclic type of infinite size
+   |
+   = note: closures cannot capture themselves or take themselves as argument;
+           this error may be the result of a recent compiler bug-fix,
+           see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
+           for more information
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0644`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-region.rs b/src/test/ui/unboxed-closures/unboxed-closure-region.rs
new file mode 100644
index 000000000..f202492ed
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-region.rs
@@ -0,0 +1,11 @@
+// Test that an unboxed closure that captures a free variable by
+// reference cannot escape the region of that variable.
+
+
+fn main() {
+    let _f = {
+        let x = 0;
+        || x //~ ERROR `x` does not live long enough
+    };
+    _f;
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-region.stderr b/src/test/ui/unboxed-closures/unboxed-closure-region.stderr
new file mode 100644
index 000000000..b40b2f67d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-region.stderr
@@ -0,0 +1,16 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/unboxed-closure-region.rs:8:12
+   |
+LL |     let _f = {
+   |         -- borrow later stored here
+LL |         let x = 0;
+LL |         || x
+   |         -- ^ borrowed value does not live long enough
+   |         |
+   |         value captured here
+LL |     };
+   |     - `x` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.rs
new file mode 100644
index 000000000..f1c83f060
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.rs
@@ -0,0 +1,28 @@
+// Test interaction between unboxed closure sugar and default type
+// parameters (should be exactly as if angle brackets were used).
+
+#![feature(unboxed_closures)]
+#![allow(dead_code)]
+
+trait Foo<T,V=T> {
+    type Output;
+    fn dummy(&self, t: T, v: V);
+}
+
+trait Eq<X: ?Sized> { fn same_types(&self, x: &X) -> bool { true } }
+impl<X: ?Sized> Eq<X> for X { }
+fn eq<A: ?Sized,B: ?Sized>() where A : Eq<B> { }
+
+fn test<'a,'b>() {
+    // Parens are equivalent to omitting default in angle.
+    eq::<dyn Foo<(isize,), Output=()>, dyn Foo(isize)>();
+
+    // In angle version, we supply something other than the default
+    eq::<dyn Foo<(isize,), isize, Output=()>, dyn Foo(isize)>();
+    //~^ ERROR E0277
+
+    // Supply default explicitly.
+    eq::<dyn Foo<(isize,), (isize,), Output=()>, dyn Foo(isize)>();
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr
new file mode 100644
index 000000000..a3b32d2c1
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-default.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `dyn Foo<(isize,), isize, Output = ()>: Eq<dyn Foo<(isize,), Output = ()>>` is not satisfied
+  --> $DIR/unboxed-closure-sugar-default.rs:21:10
+   |
+LL |     eq::<dyn Foo<(isize,), isize, Output=()>, dyn Foo(isize)>();
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq<dyn Foo<(isize,), Output = ()>>` is not implemented for `dyn Foo<(isize,), isize, Output = ()>`
+   |
+note: required by a bound in `eq`
+  --> $DIR/unboxed-closure-sugar-default.rs:14:40
+   |
+LL | fn eq<A: ?Sized,B: ?Sized>() where A : Eq<B> { }
+   |                                        ^^^^^ required by this bound in `eq`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.rs
new file mode 100644
index 000000000..acf0227a7
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.rs
@@ -0,0 +1,48 @@
+// Test that the unboxed closure sugar can be used with an arbitrary
+// struct type and that it is equivalent to the same syntax using
+// angle brackets. This test covers only simple types and in
+// particular doesn't test bound regions.
+
+#![feature(unboxed_closures)]
+#![allow(dead_code)]
+
+trait Foo<T> {
+    type Output;
+    fn dummy(&self, t: T, u: Self::Output);
+}
+
+trait Eq<X: ?Sized> { }
+impl<X: ?Sized> Eq<X> for X { }
+fn eq<A: ?Sized,B: ?Sized +Eq<A>>() { }
+
+fn test<'a,'b>() {
+    // No errors expected:
+    eq::< dyn Foo<(),Output=()>,                       dyn Foo()                         >();
+    eq::< dyn Foo<(isize,),Output=()>,                 dyn Foo(isize)                      >();
+    eq::< dyn Foo<(isize,usize),Output=()>,            dyn Foo(isize,usize)                 >();
+    eq::< dyn Foo<(isize,usize),Output=usize>,         dyn Foo(isize,usize) -> usize         >();
+    eq::< dyn Foo<(&'a isize,&'b usize),Output=usize>, dyn Foo(&'a isize,&'b usize) -> usize >();
+
+    // Test that anonymous regions in `()` form are equivalent
+    // to fresh bound regions, and that we can intermingle
+    // named and anonymous as we choose:
+    eq::< dyn for<'x,'y> Foo<(&'x isize,&'y usize),Output=usize>,
+          dyn for<'x,'y> Foo(&'x isize,&'y usize) -> usize            >();
+    eq::< dyn for<'x,'y> Foo<(&'x isize,&'y usize),Output=usize>,
+          dyn for<'x> Foo(&'x isize,&usize) -> usize                  >();
+    eq::< dyn for<'x,'y> Foo<(&'x isize,&'y usize),Output=usize>,
+          dyn for<'y> Foo(&isize,&'y usize) -> usize                  >();
+    eq::< dyn for<'x,'y> Foo<(&'x isize,&'y usize),Output=usize>,
+          dyn Foo(&isize,&usize) -> usize                             >();
+
+    // lifetime elision
+    eq::< dyn for<'x> Foo<(&'x isize,), Output=&'x isize>,
+          dyn Foo(&isize) -> &isize                                   >();
+
+    // Errors expected:
+    eq::< dyn Foo<(),Output=()>,
+          dyn Foo(char)                                               >();
+    //~^ ERROR E0277
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr
new file mode 100644
index 000000000..bccbf307a
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-equiv.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `dyn Foo<(char,), Output = ()>: Eq<dyn Foo<(), Output = ()>>` is not satisfied
+  --> $DIR/unboxed-closure-sugar-equiv.rs:44:11
+   |
+LL |           dyn Foo(char)                                               >();
+   |           ^^^^^^^^^^^^^ the trait `Eq<dyn Foo<(), Output = ()>>` is not implemented for `dyn Foo<(char,), Output = ()>`
+   |
+note: required by a bound in `eq`
+  --> $DIR/unboxed-closure-sugar-equiv.rs:16:28
+   |
+LL | fn eq<A: ?Sized,B: ?Sized +Eq<A>>() { }
+   |                            ^^^^^ required by this bound in `eq`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.rs
new file mode 100644
index 000000000..d11d663f1
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.rs
@@ -0,0 +1,27 @@
+// Test that the unboxed closure sugar can be used with an arbitrary
+// struct type and that it is equivalent to the same syntax using
+// angle brackets. This test covers only simple types and in
+// particular doesn't test bound regions.
+
+#![feature(unboxed_closures)]
+#![allow(dead_code)]
+
+use std::marker;
+
+trait Foo<T> {
+    type Output;
+    fn dummy(&self, t: T);
+}
+
+trait Eq<X: ?Sized> { }
+impl<X: ?Sized> Eq<X> for X { }
+fn eq<A: ?Sized,B: ?Sized +Eq<A>>() { }
+
+fn main() {
+    eq::< dyn for<'a> Foo<(&'a isize,), Output=&'a isize>,
+          dyn Foo(&isize) -> &isize                                   >();
+    eq::< dyn for<'a> Foo<(&'a isize,), Output=(&'a isize, &'a isize)>,
+          dyn Foo(&isize) -> (&isize, &isize)                           >();
+
+    let _: dyn Foo(&isize, &usize) -> &usize; //~ ERROR missing lifetime specifier
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr
new file mode 100644
index 000000000..2b8fec86c
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-lifetime-elision.stderr
@@ -0,0 +1,24 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/unboxed-closure-sugar-lifetime-elision.rs:26:39
+   |
+LL |     let _: dyn Foo(&isize, &usize) -> &usize;
+   |                    ------  ------     ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
+   = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider making the bound lifetime-generic with a new `'a` lifetime
+   |
+LL |     let _: dyn for<'a> Foo(&'a isize, &'a usize) -> &'a usize;
+   |                +++++++      ++         ++            ++
+help: consider introducing a named lifetime parameter
+   |
+LL ~ fn main<'a>() {
+LL |     eq::< dyn for<'a> Foo<(&'a isize,), Output=&'a isize>,
+ ...
+LL | 
+LL ~     let _: dyn Foo(&'a isize, &'a usize) -> &'a usize;
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.rs
new file mode 100644
index 000000000..6d6ed4b56
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.rs
@@ -0,0 +1,11 @@
+// Test that the `Fn` traits require `()` form without a feature gate.
+
+fn bar1(x: &dyn Fn<(), Output=()>) {
+    //~^ ERROR of `Fn`-family traits' type parameters is subject to change
+}
+
+fn bar2<T>(x: &T) where T: Fn<()> {
+    //~^ ERROR of `Fn`-family traits' type parameters is subject to change
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr
new file mode 100644
index 000000000..9da36906d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-not-used-on-fn.stderr
@@ -0,0 +1,21 @@
+error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change
+  --> $DIR/unboxed-closure-sugar-not-used-on-fn.rs:3:17
+   |
+LL | fn bar1(x: &dyn Fn<(), Output=()>) {
+   |                 ^^^^^^^^^^^^^^^^^ help: use parenthetical notation instead: `Fn() -> ()`
+   |
+   = note: see issue #29625 <https://github.com/rust-lang/rust/issues/29625> for more information
+   = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable
+
+error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change
+  --> $DIR/unboxed-closure-sugar-not-used-on-fn.rs:7:28
+   |
+LL | fn bar2<T>(x: &T) where T: Fn<()> {
+   |                            ^^^^^^ help: use parenthetical notation instead: `Fn() -> ()`
+   |
+   = note: see issue #29625 <https://github.com/rust-lang/rust/issues/29625> for more information
+   = help: add `#![feature(unboxed_closures)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.rs
new file mode 100644
index 000000000..65f40075b
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.rs
@@ -0,0 +1,36 @@
+// Test interaction between unboxed closure sugar and region
+// parameters (should be exactly as if angle brackets were used
+// and regions omitted).
+
+#![feature(unboxed_closures)]
+#![allow(dead_code)]
+
+use std::marker;
+
+trait Foo<'a,T> {
+    type Output;
+    fn dummy(&'a self) -> &'a (T,Self::Output);
+}
+
+trait Eq<X: ?Sized> { fn is_of_eq_type(&self, x: &X) -> bool { true } }
+impl<X: ?Sized> Eq<X> for X { }
+fn eq<A: ?Sized,B: ?Sized +Eq<A>>() { }
+
+fn same_type<A,B:Eq<A>>(a: A, b: B) { }
+
+fn test<'a,'b>() {
+    // Parens are equivalent to omitting default in angle.
+    eq::< dyn Foo<(isize,),Output=()>,               dyn Foo(isize)                      >();
+
+    // Here we specify 'static explicitly in angle-bracket version.
+    // Parenthesized winds up getting inferred.
+    eq::< dyn Foo<'static, (isize,),Output=()>,      dyn Foo(isize)                      >();
+}
+
+fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) {
+    //~^ ERROR this trait takes 1 lifetime argument but 0 lifetime arguments were supplied
+    // Here, the omitted lifetimes are expanded to distinct things.
+    same_type(x, y)
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr
new file mode 100644
index 000000000..016fc4dfb
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-region.stderr
@@ -0,0 +1,15 @@
+error[E0107]: this trait takes 1 lifetime argument but 0 lifetime arguments were supplied
+  --> $DIR/unboxed-closure-sugar-region.rs:30:51
+   |
+LL | fn test2(x: &dyn Foo<(isize,),Output=()>, y: &dyn Foo(isize)) {
+   |                                                   ^^^ expected 1 lifetime argument
+   |
+note: trait defined here, with 1 lifetime parameter: `'a`
+  --> $DIR/unboxed-closure-sugar-region.rs:10:7
+   |
+LL | trait Foo<'a,T> {
+   |       ^^^ --
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0107`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.rs
new file mode 100644
index 000000000..462f6fb7b
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.rs
@@ -0,0 +1,13 @@
+// Test that parentheses form doesn't work with struct types appearing in local variables.
+
+struct Bar<A> {
+    f: A
+}
+
+fn bar() {
+    let x: Box<Bar()> = panic!();
+    //~^ ERROR parenthesized type parameters may only be used with a `Fn` trait
+    //~| ERROR this struct takes 1 generic argument but 0 generic arguments
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.stderr
new file mode 100644
index 000000000..29ea5735c
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-1.stderr
@@ -0,0 +1,26 @@
+error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
+  --> $DIR/unboxed-closure-sugar-used-on-struct-1.rs:8:16
+   |
+LL |     let x: Box<Bar()> = panic!();
+   |                ^^^^^ only `Fn` traits may use parentheses
+
+error[E0107]: this struct takes 1 generic argument but 0 generic arguments were supplied
+  --> $DIR/unboxed-closure-sugar-used-on-struct-1.rs:8:16
+   |
+LL |     let x: Box<Bar()> = panic!();
+   |                ^^^ expected 1 generic argument
+   |
+note: struct defined here, with 1 generic parameter: `A`
+  --> $DIR/unboxed-closure-sugar-used-on-struct-1.rs:3:8
+   |
+LL | struct Bar<A> {
+   |        ^^^ -
+help: add missing generic argument
+   |
+LL |     let x: Box<Bar(A)> = panic!();
+   |                    +
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0107, E0214.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.rs
new file mode 100644
index 000000000..79ced1ecf
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.rs
@@ -0,0 +1,18 @@
+// Test that parentheses form parses in expression paths.
+
+struct Bar<A,R> {
+    f: A, r: R
+}
+
+impl<A,B> Bar<A,B> {
+    fn new() -> Bar<A,B> { panic!() }
+}
+
+fn bar() {
+    let b = Bar::<isize, usize>::new(); // OK
+
+    let b = Bar::(isize, usize)::new(); // OK too (for the parser)
+    //~^ ERROR parenthesized type parameters may only be used with a `Fn` trait
+}
+
+fn main() {}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr
new file mode 100644
index 000000000..4df404e81
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct-3.stderr
@@ -0,0 +1,14 @@
+error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
+  --> $DIR/unboxed-closure-sugar-used-on-struct-3.rs:14:13
+   |
+LL |     let b = Bar::(isize, usize)::new(); // OK too (for the parser)
+   |             ^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses
+   |
+help: use angle brackets instead
+   |
+LL |     let b = Bar::<isize, usize>::new(); // OK too (for the parser)
+   |                  ~            ~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0214`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.rs
new file mode 100644
index 000000000..bd61cbd80
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.rs
@@ -0,0 +1,12 @@
+// Test that parentheses form doesn't work with struct types appearing in argument types.
+
+struct Bar<A> {
+    f: A
+}
+
+fn foo(b: Box<Bar()>) {
+    //~^ ERROR parenthesized type parameters may only be used with a `Fn` trait
+    //~| ERROR this struct takes 1 generic argument but 0 generic arguments
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.stderr
new file mode 100644
index 000000000..427ba3414
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-used-on-struct.stderr
@@ -0,0 +1,26 @@
+error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
+  --> $DIR/unboxed-closure-sugar-used-on-struct.rs:7:15
+   |
+LL | fn foo(b: Box<Bar()>) {
+   |               ^^^^^ only `Fn` traits may use parentheses
+
+error[E0107]: this struct takes 1 generic argument but 0 generic arguments were supplied
+  --> $DIR/unboxed-closure-sugar-used-on-struct.rs:7:15
+   |
+LL | fn foo(b: Box<Bar()>) {
+   |               ^^^ expected 1 generic argument
+   |
+note: struct defined here, with 1 generic parameter: `A`
+  --> $DIR/unboxed-closure-sugar-used-on-struct.rs:3:8
+   |
+LL | struct Bar<A> {
+   |        ^^^ -
+help: add missing generic argument
+   |
+LL | fn foo(b: Box<Bar(A)>) {
+   |                   +
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0107, E0214.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs
new file mode 100644
index 000000000..a6c86311b
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs
@@ -0,0 +1,8 @@
+#![feature(unboxed_closures)]
+
+trait One<A> { fn foo(&self) -> A; }
+
+fn foo(_: &dyn One()) //~ ERROR associated type `Output` not found for `One<()>`
+{}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.stderr
new file mode 100644
index 000000000..59e7bc8c8
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-1.stderr
@@ -0,0 +1,9 @@
+error[E0220]: associated type `Output` not found for `One<()>`
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters-1.rs:5:16
+   |
+LL | fn foo(_: &dyn One())
+   |                ^^^^^ associated type `Output` not found
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs
new file mode 100644
index 000000000..f26ad8e93
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs
@@ -0,0 +1,10 @@
+#![feature(unboxed_closures)]
+
+trait Three<A,B,C> { fn dummy(&self) -> (A,B,C); }
+
+fn foo(_: &dyn Three())
+//~^ ERROR this trait takes 3 generic arguments but 1 generic argument
+//~| ERROR associated type `Output` not found
+{}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr
new file mode 100644
index 000000000..ebaacf0a6
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters-3.stderr
@@ -0,0 +1,24 @@
+error[E0107]: this trait takes 3 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs:5:16
+   |
+LL | fn foo(_: &dyn Three())
+   |                ^^^^^-- supplied 1 generic argument
+   |                |
+   |                expected 3 generic arguments
+   |
+note: trait defined here, with 3 generic parameters: `A`, `B`, `C`
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs:3:7
+   |
+LL | trait Three<A,B,C> { fn dummy(&self) -> (A,B,C); }
+   |       ^^^^^ - - -
+
+error[E0220]: associated type `Output` not found for `Three<(), [type error], [type error]>`
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters-3.rs:5:16
+   |
+LL | fn foo(_: &dyn Three())
+   |                ^^^^^^^ associated type `Output` not found
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0107, E0220.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs
new file mode 100644
index 000000000..4465b43a7
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.rs
@@ -0,0 +1,28 @@
+#![feature(unboxed_closures)]
+
+trait Zero { fn dummy(&self); }
+
+fn foo1(_: dyn Zero()) {
+    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
+    //~| ERROR associated type `Output` not found for `Zero`
+}
+
+fn foo2(_: dyn Zero<usize>) {
+    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
+}
+
+fn foo3(_: dyn Zero <   usize   >) {
+    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
+}
+
+fn foo4(_: dyn Zero(usize)) {
+    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
+    //~| ERROR associated type `Output` not found for `Zero`
+}
+
+fn foo5(_: dyn Zero (   usize   )) {
+    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
+    //~| ERROR associated type `Output` not found for `Zero`
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr
new file mode 100644
index 000000000..9601e64c1
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-number-number-type-parameters.stderr
@@ -0,0 +1,92 @@
+error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:5:16
+   |
+LL | fn foo1(_: dyn Zero()) {
+   |                ^^^^-- help: remove these parenthetical generics
+   |                |
+   |                expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7
+   |
+LL | trait Zero { fn dummy(&self); }
+   |       ^^^^
+
+error[E0220]: associated type `Output` not found for `Zero`
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:5:16
+   |
+LL | fn foo1(_: dyn Zero()) {
+   |                ^^^^^^ associated type `Output` not found
+
+error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:10:16
+   |
+LL | fn foo2(_: dyn Zero<usize>) {
+   |                ^^^^------- help: remove these generics
+   |                |
+   |                expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7
+   |
+LL | trait Zero { fn dummy(&self); }
+   |       ^^^^
+
+error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:14:16
+   |
+LL | fn foo3(_: dyn Zero <   usize   >) {
+   |                ^^^^-------------- help: remove these generics
+   |                |
+   |                expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7
+   |
+LL | trait Zero { fn dummy(&self); }
+   |       ^^^^
+
+error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:18:16
+   |
+LL | fn foo4(_: dyn Zero(usize)) {
+   |                ^^^^------- help: remove these parenthetical generics
+   |                |
+   |                expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7
+   |
+LL | trait Zero { fn dummy(&self); }
+   |       ^^^^
+
+error[E0220]: associated type `Output` not found for `Zero`
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:18:16
+   |
+LL | fn foo4(_: dyn Zero(usize)) {
+   |                ^^^^^^^^^^^ associated type `Output` not found
+
+error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:23:16
+   |
+LL | fn foo5(_: dyn Zero (   usize   )) {
+   |                ^^^^-------------- help: remove these parenthetical generics
+   |                |
+   |                expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:3:7
+   |
+LL | trait Zero { fn dummy(&self); }
+   |       ^^^^
+
+error[E0220]: associated type `Output` not found for `Zero`
+  --> $DIR/unboxed-closure-sugar-wrong-number-number-type-parameters.rs:23:16
+   |
+LL | fn foo5(_: dyn Zero (   usize   )) {
+   |                ^^^^^^^^^^^^^^^^^^ associated type `Output` not found
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0107, E0220.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.rs b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.rs
new file mode 100644
index 000000000..4bcf90552
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.rs
@@ -0,0 +1,9 @@
+#![feature(unboxed_closures)]
+
+trait Trait {}
+
+fn f<F:Trait(isize) -> isize>(x: F) {}
+//~^ ERROR this trait takes 0 generic arguments but 1 generic argument
+//~| ERROR associated type `Output` not found for `Trait`
+
+fn main() {}
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr
new file mode 100644
index 000000000..3ff05fb23
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closure-sugar-wrong-trait.stderr
@@ -0,0 +1,24 @@
+error[E0107]: this trait takes 0 generic arguments but 1 generic argument was supplied
+  --> $DIR/unboxed-closure-sugar-wrong-trait.rs:5:8
+   |
+LL | fn f<F:Trait(isize) -> isize>(x: F) {}
+   |        ^^^^^------- help: remove these parenthetical generics
+   |        |
+   |        expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/unboxed-closure-sugar-wrong-trait.rs:3:7
+   |
+LL | trait Trait {}
+   |       ^^^^^
+
+error[E0220]: associated type `Output` not found for `Trait`
+  --> $DIR/unboxed-closure-sugar-wrong-trait.rs:5:24
+   |
+LL | fn f<F:Trait(isize) -> isize>(x: F) {}
+   |                        ^^^^^ associated type `Output` not found
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0107, E0220.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-all-traits.rs b/src/test/ui/unboxed-closures/unboxed-closures-all-traits.rs
new file mode 100644
index 000000000..dfccb0200
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-all-traits.rs
@@ -0,0 +1,21 @@
+// run-pass
+#![feature(lang_items)]
+
+fn a<F:Fn(isize, isize) -> isize>(f: F) -> isize {
+    f(1, 2)
+}
+
+fn b<F:FnMut(isize, isize) -> isize>(mut f: F) -> isize {
+    f(3, 4)
+}
+
+fn c<F:FnOnce(isize, isize) -> isize>(f: F) -> isize {
+    f(5, 6)
+}
+
+fn main() {
+    let z: isize = 7;
+    assert_eq!(a(move |x: isize, y| x + y + z), 10);
+    assert_eq!(b(move |x: isize, y| x + y + z), 14);
+    assert_eq!(c(move |x: isize, y| x + y + z), 18);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-blanket-fn-mut.rs b/src/test/ui/unboxed-closures/unboxed-closures-blanket-fn-mut.rs
new file mode 100644
index 000000000..a10016735
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-blanket-fn-mut.rs
@@ -0,0 +1,27 @@
+// run-pass
+#![allow(unused_variables)]
+// Test that you can supply `&F` where `F: FnMut()`.
+
+#![feature(lang_items)]
+
+fn a<F:FnMut() -> i32>(mut f: F) -> i32 {
+    f()
+}
+
+fn b(f: &mut dyn FnMut() -> i32) -> i32 {
+    a(f)
+}
+
+fn c<F:FnMut() -> i32>(f: &mut F) -> i32 {
+    a(f)
+}
+
+fn main() {
+    let z: isize = 7;
+
+    let x = b(&mut || 22);
+    assert_eq!(x, 22);
+
+    let x = c(&mut || 22);
+    assert_eq!(x, 22);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-blanket-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-blanket-fn.rs
new file mode 100644
index 000000000..ca1d31ca5
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-blanket-fn.rs
@@ -0,0 +1,27 @@
+// run-pass
+#![allow(unused_variables)]
+// Test that you can supply `&F` where `F: Fn()`.
+
+#![feature(lang_items)]
+
+fn a<F:Fn() -> i32>(f: F) -> i32 {
+    f()
+}
+
+fn b(f: &dyn Fn() -> i32) -> i32 {
+    a(f)
+}
+
+fn c<F:Fn() -> i32>(f: &F) -> i32 {
+    a(f)
+}
+
+fn main() {
+    let z: isize = 7;
+
+    let x = b(&|| 22);
+    assert_eq!(x, 22);
+
+    let x = c(&|| 22);
+    assert_eq!(x, 22);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.rs b/src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.rs
new file mode 100644
index 000000000..835a1f598
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.rs
@@ -0,0 +1,11 @@
+// Test that an unboxed closure that mutates a free variable will
+// cause borrow conflicts.
+
+
+
+fn main() {
+    let mut x = 0;
+    let f = || x += 1;
+    let _y = x; //~ ERROR cannot use `x` because it was mutably borrowed
+    f;
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.stderr b/src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.stderr
new file mode 100644
index 000000000..21d6b4fde
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-borrow-conflict.stderr
@@ -0,0 +1,15 @@
+error[E0503]: cannot use `x` because it was mutably borrowed
+  --> $DIR/unboxed-closures-borrow-conflict.rs:9:14
+   |
+LL |     let f = || x += 1;
+   |             -- - borrow occurs due to use of `x` in closure
+   |             |
+   |             borrow of `x` occurs here
+LL |     let _y = x;
+   |              ^ use of borrowed `x`
+LL |     f;
+   |     - borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0503`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-boxed.rs b/src/test/ui/unboxed-closures/unboxed-closures-boxed.rs
new file mode 100644
index 000000000..3f550fd04
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-boxed.rs
@@ -0,0 +1,15 @@
+// run-pass
+
+use std::ops::FnMut;
+
+ fn make_adder(x: i32) -> Box<dyn FnMut(i32)->i32+'static> {
+    Box::new(move |y: i32| -> i32 { x + y }) as
+        Box<dyn FnMut(i32)->i32+'static>
+}
+
+pub fn main() {
+    let mut adder = make_adder(3);
+    let z = adder(2);
+    println!("{}", z);
+    assert_eq!(z, 5);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-by-ref.rs b/src/test/ui/unboxed-closures/unboxed-closures-by-ref.rs
new file mode 100644
index 000000000..cf4d4d3e1
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-by-ref.rs
@@ -0,0 +1,24 @@
+// run-pass
+// Test by-ref capture of environment in unboxed closure types
+
+fn call_fn<F: Fn()>(f: F) {
+    f()
+}
+
+fn call_fn_mut<F: FnMut()>(mut f: F) {
+    f()
+}
+
+fn call_fn_once<F: FnOnce()>(f: F) {
+    f()
+}
+
+fn main() {
+    let mut x = 0_usize;
+    let y = 2_usize;
+
+    call_fn(|| assert_eq!(x, 0));
+    call_fn_mut(|| x += y);
+    call_fn_once(|| x += y);
+    assert_eq!(x, y * 2);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-call-fn-autoderef.rs b/src/test/ui/unboxed-closures/unboxed-closures-call-fn-autoderef.rs
new file mode 100644
index 000000000..e23a75ab3
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-call-fn-autoderef.rs
@@ -0,0 +1,18 @@
+// run-pass
+#![allow(unused_imports)]
+// Test that the call operator autoderefs when calling a bounded type parameter.
+
+use std::ops::FnMut;
+
+fn call_with_2(x: &fn(isize) -> isize) -> isize
+{
+    x(2) // look ma, no `*`
+}
+
+fn subtract_22(x: isize) -> isize { x - 22 }
+
+pub fn main() {
+    let subtract_22: fn(isize) -> isize = subtract_22;
+    let z = call_with_2(&subtract_22);
+    assert_eq!(z, -20);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-autoderef.rs b/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-autoderef.rs
new file mode 100644
index 000000000..9b8a3f409
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-autoderef.rs
@@ -0,0 +1,15 @@
+// run-pass
+// Test that the call operator autoderefs when calling a bounded type parameter.
+
+use std::ops::FnMut;
+
+fn call_with_2<F>(x: &mut F) -> isize
+    where F : FnMut(isize) -> isize
+{
+    x(2) // look ma, no `*`
+}
+
+pub fn main() {
+    let z = call_with_2(&mut |x| x - 22);
+    assert_eq!(z, -20);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object-autoderef.rs b/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object-autoderef.rs
new file mode 100644
index 000000000..d47ceea0f
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object-autoderef.rs
@@ -0,0 +1,15 @@
+// run-pass
+// Test that the call operator autoderefs when calling to an object type.
+
+use std::ops::FnMut;
+
+fn make_adder(x: isize) -> Box<dyn FnMut(isize)->isize + 'static> {
+    Box::new(move |y| { x + y })
+}
+
+pub fn main() {
+    let mut adder = make_adder(3);
+    let z = adder(2);
+    println!("{}", z);
+    assert_eq!(z, 5);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object.rs b/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object.rs
new file mode 100644
index 000000000..f77733d10
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-call-sugar-object.rs
@@ -0,0 +1,13 @@
+// run-pass
+use std::ops::FnMut;
+
+fn make_adder(x: isize) -> Box<dyn FnMut(isize)->isize + 'static> {
+    Box::new(move |y| { x + y })
+}
+
+pub fn main() {
+    let mut adder = make_adder(3);
+    let z = (*adder)(2);
+    println!("{}", z);
+    assert_eq!(z, 5);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs
new file mode 100644
index 000000000..390386e57
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs
@@ -0,0 +1,28 @@
+// run-pass
+// Test that we mutate a counter on the stack only when we expect to.
+
+fn call<F>(f: F) where F : FnOnce() {
+    f();
+}
+
+fn main() {
+    let y = vec![format!("Hello"), format!("World")];
+    let mut counter = 22_u32;
+
+    call(|| {
+        // Move `y`, but do not move `counter`, even though it is read
+        // by value (note that it is also mutated).
+        for item in y { //~ WARN unused variable: `item`
+            let v = counter;
+            counter += v;
+        }
+    });
+    assert_eq!(counter, 88);
+
+    call(move || {
+        // this mutates a moved copy, and hence doesn't affect original
+        counter += 1; //~  WARN value assigned to `counter` is never read
+                      //~| WARN unused variable: `counter`
+    });
+    assert_eq!(counter, 88);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr
new file mode 100644
index 000000000..ba4b3dac6
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr
@@ -0,0 +1,27 @@
+warning: unused variable: `item`
+  --> $DIR/unboxed-closures-counter-not-moved.rs:15:13
+   |
+LL |         for item in y {
+   |             ^^^^ help: if this is intentional, prefix it with an underscore: `_item`
+   |
+   = note: `#[warn(unused_variables)]` on by default
+
+warning: value assigned to `counter` is never read
+  --> $DIR/unboxed-closures-counter-not-moved.rs:24:9
+   |
+LL |         counter += 1;
+   |         ^^^^^^^
+   |
+   = note: `#[warn(unused_assignments)]` on by default
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `counter`
+  --> $DIR/unboxed-closures-counter-not-moved.rs:24:9
+   |
+LL |         counter += 1;
+   |         ^^^^^^^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-cross-crate.rs b/src/test/ui/unboxed-closures/unboxed-closures-cross-crate.rs
new file mode 100644
index 000000000..39cc26072
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-cross-crate.rs
@@ -0,0 +1,14 @@
+// run-pass
+#![allow(non_camel_case_types)]
+
+// Test that unboxed closures work with cross-crate inlining
+// Acts as a regression test for #16790, #18378 and #18543
+
+// aux-build:unboxed-closures-cross-crate.rs
+
+extern crate unboxed_closures_cross_crate as ubcc;
+
+fn main() {
+    assert_eq!(ubcc::has_closures(), 2_usize);
+    assert_eq!(ubcc::has_generic_closures(2_usize, 3_usize), 5_usize);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-direct-sugary-call.rs b/src/test/ui/unboxed-closures/unboxed-closures-direct-sugary-call.rs
new file mode 100644
index 000000000..1c5e74e59
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-direct-sugary-call.rs
@@ -0,0 +1,8 @@
+// run-pass
+#![allow(unused_mut)]
+// pretty-expanded FIXME #23616
+
+fn main() {
+    let mut unboxed = || {};
+    unboxed();
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-drop.rs b/src/test/ui/unboxed-closures/unboxed-closures-drop.rs
new file mode 100644
index 000000000..ba3c61ca2
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-drop.rs
@@ -0,0 +1,117 @@
+// run-pass
+#![allow(path_statements)]
+#![allow(dead_code)]
+// A battery of tests to ensure destructors of unboxed closure environments
+// run at the right times.
+
+static mut DROP_COUNT: usize = 0;
+
+fn drop_count() -> usize {
+    unsafe {
+        DROP_COUNT
+    }
+}
+
+struct Droppable {
+    x: isize,
+}
+
+impl Droppable {
+    fn new() -> Droppable {
+        Droppable {
+            x: 1
+        }
+    }
+}
+
+impl Drop for Droppable {
+    fn drop(&mut self) {
+        unsafe {
+            DROP_COUNT += 1
+        }
+    }
+}
+
+fn a<F:Fn(isize, isize) -> isize>(f: F) -> isize {
+    f(1, 2)
+}
+
+fn b<F:FnMut(isize, isize) -> isize>(mut f: F) -> isize {
+    f(3, 4)
+}
+
+fn c<F:FnOnce(isize, isize) -> isize>(f: F) -> isize {
+    f(5, 6)
+}
+
+fn test_fn() {
+    {
+        a(move |a: isize, b| { a + b });
+    }
+    assert_eq!(drop_count(), 0);
+
+    {
+        let z = &Droppable::new();
+        a(move |a: isize, b| { z; a + b });
+        assert_eq!(drop_count(), 0);
+    }
+    assert_eq!(drop_count(), 1);
+
+    {
+        let z = &Droppable::new();
+        let zz = &Droppable::new();
+        a(move |a: isize, b| { z; zz; a + b });
+        assert_eq!(drop_count(), 1);
+    }
+    assert_eq!(drop_count(), 3);
+}
+
+fn test_fn_mut() {
+    {
+        b(move |a: isize, b| { a + b });
+    }
+    assert_eq!(drop_count(), 3);
+
+    {
+        let z = &Droppable::new();
+        b(move |a: isize, b| { z; a + b });
+        assert_eq!(drop_count(), 3);
+    }
+    assert_eq!(drop_count(), 4);
+
+    {
+        let z = &Droppable::new();
+        let zz = &Droppable::new();
+        b(move |a: isize, b| { z; zz; a + b });
+        assert_eq!(drop_count(), 4);
+    }
+    assert_eq!(drop_count(), 6);
+}
+
+fn test_fn_once() {
+    {
+        c(move |a: isize, b| { a + b });
+    }
+    assert_eq!(drop_count(), 6);
+
+    {
+        let z = Droppable::new();
+        c(move |a: isize, b| { z; a + b });
+        assert_eq!(drop_count(), 7);
+    }
+    assert_eq!(drop_count(), 7);
+
+    {
+        let z = Droppable::new();
+        let zz = Droppable::new();
+        c(move |a: isize, b| { z; zz; a + b });
+        assert_eq!(drop_count(), 9);
+    }
+    assert_eq!(drop_count(), 9);
+}
+
+fn main() {
+    test_fn();
+    test_fn_mut();
+    test_fn_once();
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-extern-fn-hr.rs b/src/test/ui/unboxed-closures/unboxed-closures-extern-fn-hr.rs
new file mode 100644
index 000000000..3ee1aeb10
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-extern-fn-hr.rs
@@ -0,0 +1,31 @@
+// run-pass
+// Checks that higher-ranked extern fn pointers implement the full range of Fn traits.
+
+fn square(x: &isize) -> isize { (*x) * (*x) }
+
+fn call_it<F:Fn(&isize)->isize>(f: &F, x: isize) -> isize {
+    (*f)(&x)
+}
+
+fn call_it_boxed(f: &dyn Fn(&isize) -> isize, x: isize) -> isize {
+    f(&x)
+}
+
+fn call_it_mut<F:FnMut(&isize)->isize>(f: &mut F, x: isize) -> isize {
+    (*f)(&x)
+}
+
+fn call_it_once<F:FnOnce(&isize)->isize>(f: F, x: isize) -> isize {
+    f(&x)
+}
+
+fn main() {
+    let x = call_it(&square, 22);
+    let x1 = call_it_boxed(&square, 22);
+    let y = call_it_mut(&mut square, 22);
+    let z = call_it_once(square, 22);
+    assert_eq!(x, square(&22));
+    assert_eq!(x1, square(&22));
+    assert_eq!(y, square(&22));
+    assert_eq!(z, square(&22));
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-extern-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-extern-fn.rs
new file mode 100644
index 000000000..677cd259a
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-extern-fn.rs
@@ -0,0 +1,27 @@
+// run-pass
+// Checks that extern fn pointers implement the full range of Fn traits.
+
+use std::ops::{Fn,FnMut,FnOnce};
+
+fn square(x: isize) -> isize { x * x }
+
+fn call_it<F:Fn(isize)->isize>(f: &F, x: isize) -> isize {
+    f(x)
+}
+
+fn call_it_mut<F:FnMut(isize)->isize>(f: &mut F, x: isize) -> isize {
+    f(x)
+}
+
+fn call_it_once<F:FnOnce(isize)->isize>(f: F, x: isize) -> isize {
+    f(x)
+}
+
+fn main() {
+    let x = call_it(&square, 22);
+    let y = call_it_mut(&mut square, 22);
+    let z = call_it_once(square, 22);
+    assert_eq!(x, square(22));
+    assert_eq!(y, square(22));
+    assert_eq!(z, square(22));
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.rs b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.rs
new file mode 100644
index 000000000..1358ba0f9
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.rs
@@ -0,0 +1,37 @@
+// Various unsuccessful attempts to put the unboxed closure kind
+// inference into an awkward position that might require fixed point
+// iteration (basically where inferring the kind of a closure `c`
+// would require knowing the kind of `c`). I currently believe this is
+// impossible.
+
+fn a() {
+    // This case of recursion wouldn't even require fixed-point
+    // iteration, but it still doesn't work. The weird structure with
+    // the `Option` is to avoid giving any useful hints about the `Fn`
+    // kind via the expected type.
+    let mut factorial: Option<Box<dyn Fn(u32) -> u32>> = None;
+
+    let f = |x: u32| -> u32 {
+        let g = factorial.as_ref().unwrap();
+        //~^ ERROR `factorial` does not live long enough
+        if x == 0 {1} else {x * g(x-1)}
+    };
+
+    factorial = Some(Box::new(f));
+    //~^ ERROR cannot assign to `factorial` because it is borrowed
+}
+
+fn b() {
+    let mut factorial: Option<Box<dyn Fn(u32) -> u32 + 'static>> = None;
+
+    let f = |x: u32| -> u32 {
+        let g = factorial.as_ref().unwrap();
+        //~^ ERROR `factorial` does not live long enough
+        if x == 0 {1} else {x * g(x-1)}
+    };
+
+    factorial = Some(Box::new(f));
+    //~^ ERROR cannot assign to `factorial` because it is borrowed
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr
new file mode 100644
index 000000000..cbdb4dd0f
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.stderr
@@ -0,0 +1,60 @@
+error[E0597]: `factorial` does not live long enough
+  --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:15:17
+   |
+LL |     let f = |x: u32| -> u32 {
+   |             --------------- value captured here
+LL |         let g = factorial.as_ref().unwrap();
+   |                 ^^^^^^^^^ borrowed value does not live long enough
+...
+LL | }
+   | -
+   | |
+   | `factorial` dropped here while still borrowed
+   | borrow might be used here, when `factorial` is dropped and runs the destructor for type `Option<Box<dyn Fn(u32) -> u32>>`
+
+error[E0506]: cannot assign to `factorial` because it is borrowed
+  --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:20:5
+   |
+LL |     let f = |x: u32| -> u32 {
+   |             --------------- borrow of `factorial` occurs here
+LL |         let g = factorial.as_ref().unwrap();
+   |                 --------- borrow occurs due to use in closure
+...
+LL |     factorial = Some(Box::new(f));
+   |     ^^^^^^^^^
+   |     |
+   |     assignment to borrowed `factorial` occurs here
+   |     borrow later used here
+
+error[E0597]: `factorial` does not live long enough
+  --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:28:17
+   |
+LL |     let mut factorial: Option<Box<dyn Fn(u32) -> u32 + 'static>> = None;
+   |                        ----------------------------------------- type annotation requires that `factorial` is borrowed for `'static`
+LL |
+LL |     let f = |x: u32| -> u32 {
+   |             --------------- value captured here
+LL |         let g = factorial.as_ref().unwrap();
+   |                 ^^^^^^^^^ borrowed value does not live long enough
+...
+LL | }
+   | - `factorial` dropped here while still borrowed
+
+error[E0506]: cannot assign to `factorial` because it is borrowed
+  --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:33:5
+   |
+LL |     let mut factorial: Option<Box<dyn Fn(u32) -> u32 + 'static>> = None;
+   |                        ----------------------------------------- type annotation requires that `factorial` is borrowed for `'static`
+LL |
+LL |     let f = |x: u32| -> u32 {
+   |             --------------- borrow of `factorial` occurs here
+LL |         let g = factorial.as_ref().unwrap();
+   |                 --------- borrow occurs due to use in closure
+...
+LL |     factorial = Some(Box::new(f));
+   |     ^^^^^^^^^ assignment to borrowed `factorial` occurs here
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0506, E0597.
+For more information about an error, try `rustc --explain E0506`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.rs b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.rs
new file mode 100644
index 000000000..25c2dbe19
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.rs
@@ -0,0 +1,29 @@
+// Various unsuccessful attempts to put the unboxed closure kind
+// inference into an awkward position that might require fixed point
+// iteration (basically where inferring the kind of a closure `c`
+// would require knowing the kind of `c`). I currently believe this is
+// impossible.
+
+fn a() {
+    let mut closure0 = None;
+    //~^ ERROR type annotations needed
+    let vec = vec![1, 2, 3];
+
+    loop {
+        {
+            let closure1 = || {
+                match closure0.take() {
+                    Some(c) => {
+                        return c();
+                    }
+                    None => { }
+                }
+            };
+            closure1();
+        }
+
+        closure0 = || vec;
+    }
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr
new file mode 100644
index 000000000..ff2a597be
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr
@@ -0,0 +1,17 @@
+error[E0282]: type annotations needed for `Option<T>`
+  --> $DIR/unboxed-closures-failed-recursive-fn-2.rs:8:9
+   |
+LL |     let mut closure0 = None;
+   |         ^^^^^^^^^^^^
+...
+LL |                         return c();
+   |                                --- type must be known at this point
+   |
+help: consider giving `closure0` an explicit type, where the placeholders `_` are specified
+   |
+LL |     let mut closure0: Option<T> = None;
+   |                     +++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fn-as-fnmut-and-fnonce.rs b/src/test/ui/unboxed-closures/unboxed-closures-fn-as-fnmut-and-fnonce.rs
new file mode 100644
index 000000000..851f3d2fe
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-fn-as-fnmut-and-fnonce.rs
@@ -0,0 +1,44 @@
+// run-pass
+// Checks that the Fn trait hierarchy rules permit
+// any Fn trait to be used where Fn is implemented.
+
+#![feature(unboxed_closures, fn_traits)]
+
+use std::ops::{Fn,FnMut,FnOnce};
+
+struct S;
+
+impl Fn<(i32,)> for S {
+    extern "rust-call" fn call(&self, (x,): (i32,)) -> i32 {
+        x * x
+    }
+}
+
+impl FnMut<(i32,)> for S {
+    extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> i32 { self.call(args) }
+}
+
+impl FnOnce<(i32,)> for S {
+    type Output = i32;
+    extern "rust-call" fn call_once(self, args: (i32,)) -> i32 { self.call(args) }
+}
+
+fn call_it<F:Fn(i32)->i32>(f: &F, x: i32) -> i32 {
+    f(x)
+}
+
+fn call_it_mut<F:FnMut(i32)->i32>(f: &mut F, x: i32) -> i32 {
+    f(x)
+}
+
+fn call_it_once<F:FnOnce(i32)->i32>(f: F, x: i32) -> i32 {
+    f(x)
+}
+
+fn main() {
+    let x = call_it(&S, 22);
+    let y = call_it_mut(&mut S, 22);
+    let z = call_it_once(S, 22);
+    assert_eq!(x, y);
+    assert_eq!(y, z);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.rs
new file mode 100644
index 000000000..867e5fb1d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.rs
@@ -0,0 +1,29 @@
+// Checks that the Fn trait hierarchy rules do not permit
+// Fn to be used where FnMut is implemented.
+
+#![feature(fn_traits, unboxed_closures)]
+
+use std::ops::{Fn,FnMut,FnOnce};
+
+struct S;
+
+impl FnMut<(isize,)> for S {
+    extern "rust-call" fn call_mut(&mut self, (x,): (isize,)) -> isize {
+        x * x
+    }
+}
+
+impl FnOnce<(isize,)> for S {
+    type Output = isize;
+
+    extern "rust-call" fn call_once(mut self, args: (isize,)) -> isize { self.call_mut(args) }
+}
+
+fn call_it<F:Fn(isize)->isize>(f: &F, x: isize) -> isize {
+    f.call((x,))
+}
+
+fn main() {
+    let x = call_it(&S, 22);
+    //~^ ERROR E0277
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr
new file mode 100644
index 000000000..0ea1c1dcd
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fn.stderr
@@ -0,0 +1,19 @@
+error[E0277]: expected a `Fn<(isize,)>` closure, found `S`
+  --> $DIR/unboxed-closures-fnmut-as-fn.rs:27:21
+   |
+LL |     let x = call_it(&S, 22);
+   |             ------- ^^ expected an `Fn<(isize,)>` closure, found `S`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `Fn<(isize,)>` is not implemented for `S`
+   = note: `S` implements `FnMut`, but it must implement `Fn`, which is more general
+note: required by a bound in `call_it`
+  --> $DIR/unboxed-closures-fnmut-as-fn.rs:22:14
+   |
+LL | fn call_it<F:Fn(isize)->isize>(f: &F, x: isize) -> isize {
+   |              ^^^^^^^^^^^^^^^^ required by this bound in `call_it`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fnonce.rs b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fnonce.rs
new file mode 100644
index 000000000..bd577f7c4
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-fnmut-as-fnonce.rs
@@ -0,0 +1,33 @@
+// run-pass
+// Checks that the Fn trait hierarchy rules permit
+// FnMut or FnOnce to be used where FnMut is implemented.
+
+#![feature(unboxed_closures, fn_traits)]
+
+struct S;
+
+impl FnMut<(i32,)> for S {
+    extern "rust-call" fn call_mut(&mut self, (x,): (i32,)) -> i32 {
+        x * x
+    }
+}
+
+impl FnOnce<(i32,)> for S {
+    type Output = i32;
+
+    extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 { self.call_mut(args) }
+}
+
+fn call_it_mut<F:FnMut(i32)->i32>(f: &mut F, x: i32) -> i32 {
+    f(x)
+}
+
+fn call_it_once<F:FnOnce(i32)->i32>(f: F, x: i32) -> i32 {
+    f(x)
+}
+
+fn main() {
+    let y = call_it_mut(&mut S, 22);
+    let z = call_it_once(S, 22);
+    assert_eq!(y, z);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-generic.rs b/src/test/ui/unboxed-closures/unboxed-closures-generic.rs
new file mode 100644
index 000000000..740b8b2a7
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-generic.rs
@@ -0,0 +1,13 @@
+// run-pass
+use std::ops::FnMut;
+
+fn call_it<F:FnMut(i32,i32)->i32>(y: i32, mut f: F) -> i32 {
+    f(2, y)
+}
+
+pub fn main() {
+    let f = |x: i32, y: i32| -> i32 { x + y };
+    let z = call_it(3, f);
+    println!("{}", z);
+    assert_eq!(z, 5);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-bound.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-bound.rs
new file mode 100644
index 000000000..e0c910576
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-bound.rs
@@ -0,0 +1,23 @@
+// run-pass
+// Test that we are able to infer that the type of `x` is `isize` based
+// on the expected type from the object.
+
+// pretty-expanded FIXME #23616
+
+pub trait ToPrimitive {
+    fn to_int(&self) {}
+}
+
+impl ToPrimitive for isize {}
+impl ToPrimitive for i32 {}
+impl ToPrimitive for usize {}
+
+fn doit<T,F>(val: T, f: &F)
+    where F : Fn(T)
+{
+    f(val)
+}
+
+pub fn main() {
+    doit(0, &|x /*: isize*/ | { x.to_int(); });
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-object-type.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-object-type.rs
new file mode 100644
index 000000000..d2eaee304
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-from-expected-object-type.rs
@@ -0,0 +1,19 @@
+// run-pass
+// Test that we are able to infer that the type of `x` is `isize` based
+// on the expected type from the object.
+
+// pretty-expanded FIXME #23616
+
+pub trait ToPrimitive {
+    fn to_int(&self) {}
+}
+
+impl ToPrimitive for isize {}
+impl ToPrimitive for i32 {}
+impl ToPrimitive for usize {}
+
+fn doit<T>(val: T, f: &dyn Fn(T)) { f(val) }
+
+pub fn main() {
+    doit(0, &|x /*: isize*/ | { x.to_int(); });
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-w-bound-regs-from-expected-bound.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-w-bound-regs-from-expected-bound.rs
new file mode 100644
index 000000000..c3abdd8aa
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-arg-types-w-bound-regs-from-expected-bound.rs
@@ -0,0 +1,23 @@
+// run-pass
+// Test that we are able to infer that the type of `x` is `isize` based
+// on the expected type from the object.
+
+// pretty-expanded FIXME #23616
+
+pub trait ToPrimitive {
+    fn to_int(&self) {}
+}
+
+impl ToPrimitive for isize {}
+impl ToPrimitive for i32 {}
+impl ToPrimitive for usize {}
+
+fn doit<T,F>(val: T, f: &F)
+    where F : Fn(&T)
+{
+    f(&val)
+}
+
+pub fn main() {
+    doit(0, &|x /*: isize*/ | { x.to_int(); });
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs
new file mode 100644
index 000000000..6765da421
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.rs
@@ -0,0 +1,20 @@
+#![feature(fn_traits)]
+
+// That a closure whose expected argument types include two distinct
+// bound regions.
+
+use std::cell::Cell;
+
+fn doit<T,F>(val: T, f: &F)
+    where F : Fn(&Cell<&T>, &T)
+{
+    let x = Cell::new(&val);
+    f.call((&x,&val))
+}
+
+pub fn main() {
+    doit(0, &|x, y| {
+        x.set(y);
+        //~^ lifetime may not live long enough
+    });
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr
new file mode 100644
index 000000000..e97157b83
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-argument-types-two-region-pointers.stderr
@@ -0,0 +1,12 @@
+error: lifetime may not live long enough
+  --> $DIR/unboxed-closures-infer-argument-types-two-region-pointers.rs:17:9
+   |
+LL |     doit(0, &|x, y| {
+   |               -  - has type `&'1 i32`
+   |               |
+   |               has type `&Cell<&'2 i32>`
+LL |         x.set(y);
+   |         ^^^^^^^^ argument requires that `'1` must outlive `'2`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-explicit-call-early.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-explicit-call-early.rs
new file mode 100644
index 000000000..9135c82b4
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-explicit-call-early.rs
@@ -0,0 +1,8 @@
+// run-pass
+#![feature(fn_traits)]
+
+fn main() {
+    let mut zero = || 0;
+    let x = zero.call_mut(());
+    assert_eq!(x, 0);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.rs
new file mode 100644
index 000000000..6e404c616
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.rs
@@ -0,0 +1,16 @@
+#![allow(unused)]
+
+fn foo<F>(f: F)
+    where F: Fn()
+{
+}
+
+fn main() {
+    // Test that this closure is inferred to `FnOnce` because it moves
+    // from `y.0`. This affects the error output (the error is that
+    // the closure implements `FnOnce`, not that it moves from inside
+    // a `Fn` closure.)
+    let y = (vec![1, 2, 3], 0);
+    let c = || drop(y.0); //~ ERROR expected a closure that implements the `Fn` trait
+    foo(c);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr
new file mode 100644
index 000000000..85ff49d61
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fn-once-move-from-projection.stderr
@@ -0,0 +1,13 @@
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+  --> $DIR/unboxed-closures-infer-fn-once-move-from-projection.rs:14:13
+   |
+LL |     let c = || drop(y.0);
+   |             ^^      --- closure is `FnOnce` because it moves the variable `y` out of its environment
+   |             |
+   |             this closure implements `FnOnce`, not `Fn`
+LL |     foo(c);
+   |     --- the requirement to implement `Fn` derives from here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs
new file mode 100644
index 000000000..6401b5e01
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs
@@ -0,0 +1,20 @@
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    // Here this must be inferred to FnMut so that it can mutate counter,
+    // but we forgot the mut.
+    let tick1 = || {
+        counter += 1;
+    };
+
+    // In turn, tick2 must be inferred to FnMut so that it can call
+    // tick1, but we forgot the mut.
+    let tick2 = || {
+        tick1(); //~ ERROR cannot borrow `tick1` as mutable
+    };
+
+    tick2(); //~ ERROR cannot borrow
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr
new file mode 100644
index 000000000..a0ed56d4b
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr
@@ -0,0 +1,25 @@
+error[E0596]: cannot borrow `tick1` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs:16:9
+   |
+LL |     let tick1 = || {
+   |         ----- help: consider changing this to be mutable: `mut tick1`
+LL |         counter += 1;
+   |         ------- calling `tick1` requires mutable binding due to mutable borrow of `counter`
+...
+LL |         tick1();
+   |         ^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow `tick2` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.rs:19:5
+   |
+LL |     let tick2 = || {
+   |         ----- help: consider changing this to be mutable: `mut tick2`
+LL |         tick1();
+   |         ----- calling `tick2` requires mutable binding due to mutable borrow of `tick1`
+...
+LL |     tick2();
+   |     ^^^^^ cannot borrow as mutable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut.rs
new file mode 100644
index 000000000..73f488a4f
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut.rs
@@ -0,0 +1,19 @@
+// run-pass
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        // Here this must be inferred to FnMut so that it can mutate counter:
+        let mut tick1 = || counter += 1;
+
+        // In turn, tick2 must be inferred to FnMut so that it can call tick1:
+        let mut tick2 = || { tick1(); tick1(); };
+
+        tick2();
+    }
+
+    assert_eq!(counter, 2);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.rs
new file mode 100644
index 000000000..5c0ceb23d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.rs
@@ -0,0 +1,8 @@
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+    let tick = || counter += 1;
+    tick(); //~ ERROR cannot borrow `tick` as mutable, as it is not declared as mutable
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr
new file mode 100644
index 000000000..27d23e3fa
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr
@@ -0,0 +1,13 @@
+error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closures-infer-fnmut-missing-mut.rs:7:5
+   |
+LL |     let tick = || counter += 1;
+   |         ----      ------- calling `tick` requires mutable binding due to mutable borrow of `counter`
+   |         |
+   |         help: consider changing this to be mutable: `mut tick`
+LL |     tick();
+   |     ^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.rs
new file mode 100644
index 000000000..144a674ac
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.rs
@@ -0,0 +1,8 @@
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+    let tick = move || counter += 1;
+    tick(); //~ ERROR cannot borrow `tick` as mutable, as it is not declared as mutable
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr
new file mode 100644
index 000000000..c00f986c3
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr
@@ -0,0 +1,13 @@
+error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable
+  --> $DIR/unboxed-closures-infer-fnmut-move-missing-mut.rs:7:5
+   |
+LL |     let tick = move || counter += 1;
+   |         ----           ------- calling `tick` requires mutable binding due to possible mutation of `counter`
+   |         |
+   |         help: consider changing this to be mutable: `mut tick`
+LL |     tick();
+   |     ^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move.rs
new file mode 100644
index 000000000..7ac1ae30f
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move.rs
@@ -0,0 +1,16 @@
+// run-pass
+// Test that we are able to infer a suitable kind for this `move`
+// closure that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    let v = {
+        let mut tick = move || { counter += 1; counter };
+        tick();
+        tick()
+    };
+
+    assert_eq!(counter, 0);
+    assert_eq!(v, 2);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut.rs
new file mode 100644
index 000000000..0fbb504c2
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut.rs
@@ -0,0 +1,15 @@
+// run-pass
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let mut tick = || counter += 1;
+        tick();
+        tick();
+    }
+
+    assert_eq!(counter, 2);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.rs
new file mode 100644
index 000000000..a98a01ca5
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.rs
@@ -0,0 +1,11 @@
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+use std::mem;
+
+fn main() {
+    let mut counter: Vec<i32> = Vec::new();
+    let tick = || mem::drop(counter);
+    tick();
+    tick(); //~ ERROR use of moved value: `tick`
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr
new file mode 100644
index 000000000..ab6f06518
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-call-twice.stderr
@@ -0,0 +1,22 @@
+error[E0382]: use of moved value: `tick`
+  --> $DIR/unboxed-closures-infer-fnonce-call-twice.rs:10:5
+   |
+LL |     tick();
+   |     ------ `tick` moved due to this call
+LL |     tick();
+   |     ^^^^ value used here after move
+   |
+note: closure cannot be invoked more than once because it moves the variable `counter` out of its environment
+  --> $DIR/unboxed-closures-infer-fnonce-call-twice.rs:8:29
+   |
+LL |     let tick = || mem::drop(counter);
+   |                             ^^^^^^^
+note: this value implements `FnOnce`, which causes it to be moved when called
+  --> $DIR/unboxed-closures-infer-fnonce-call-twice.rs:9:5
+   |
+LL |     tick();
+   |     ^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.rs
new file mode 100644
index 000000000..f87be4a06
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.rs
@@ -0,0 +1,11 @@
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnMut`).
+
+use std::mem;
+
+fn main() {
+    let mut counter: Vec<i32> = Vec::new();
+    let tick = move || mem::drop(counter);
+    tick();
+    tick(); //~ ERROR use of moved value: `tick`
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr
new file mode 100644
index 000000000..8d70a2b17
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move-call-twice.stderr
@@ -0,0 +1,22 @@
+error[E0382]: use of moved value: `tick`
+  --> $DIR/unboxed-closures-infer-fnonce-move-call-twice.rs:10:5
+   |
+LL |     tick();
+   |     ------ `tick` moved due to this call
+LL |     tick();
+   |     ^^^^ value used here after move
+   |
+note: closure cannot be invoked more than once because it moves the variable `counter` out of its environment
+  --> $DIR/unboxed-closures-infer-fnonce-move-call-twice.rs:8:34
+   |
+LL |     let tick = move || mem::drop(counter);
+   |                                  ^^^^^^^
+note: this value implements `FnOnce`, which causes it to be moved when called
+  --> $DIR/unboxed-closures-infer-fnonce-move-call-twice.rs:9:5
+   |
+LL |     tick();
+   |     ^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move.rs
new file mode 100644
index 000000000..6381386c4
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce-move.rs
@@ -0,0 +1,25 @@
+// run-pass
+// Test that we are able to infer a suitable kind for this `move`
+// closure that is just called (`FnOnce`).
+
+use std::mem;
+
+struct DropMe<'a>(&'a mut i32);
+
+impl<'a> Drop for DropMe<'a> {
+    fn drop(&mut self) {
+        *self.0 += 1;
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let drop_me = DropMe(&mut counter);
+        let tick = move || mem::drop(drop_me);
+        tick();
+    }
+
+    assert_eq!(counter, 1);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce.rs
new file mode 100644
index 000000000..3c8ea7d85
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnonce.rs
@@ -0,0 +1,25 @@
+// run-pass
+// Test that we are able to infer a suitable kind for this closure
+// that is just called (`FnOnce`).
+
+use std::mem;
+
+struct DropMe<'a>(&'a mut i32);
+
+impl<'a> Drop for DropMe<'a> {
+    fn drop(&mut self) {
+        *self.0 += 1;
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+
+    {
+        let drop_me = DropMe(&mut counter);
+        let tick = || mem::drop(drop_me);
+        tick();
+    }
+
+    assert_eq!(counter, 1);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-kind.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-kind.rs
new file mode 100644
index 000000000..fc01bd9b6
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-kind.rs
@@ -0,0 +1,27 @@
+// run-pass
+// Test that we can infer the "kind" of an unboxed closure based on
+// the expected type.
+
+// Test by-ref capture of environment in unboxed closure types
+
+fn call_fn<F: Fn()>(f: F) {
+    f()
+}
+
+fn call_fn_mut<F: FnMut()>(mut f: F) {
+    f()
+}
+
+fn call_fn_once<F: FnOnce()>(f: F) {
+    f()
+}
+
+fn main() {
+    let mut x = 0_usize;
+    let y = 2_usize;
+
+    call_fn(|| assert_eq!(x, 0));
+    call_fn_mut(|| x += y);
+    call_fn_once(|| x += y);
+    assert_eq!(x, y * 2);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs
new file mode 100644
index 000000000..a0fbbafe2
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-recursive-fn.rs
@@ -0,0 +1,45 @@
+// run-pass
+#![feature(fn_traits, unboxed_closures)]
+
+use std::marker::PhantomData;
+
+// Test that we are able to infer a suitable kind for a "recursive"
+// closure.  As far as I can tell, coding up a recursive closure
+// requires the good ol' [Y Combinator].
+//
+// [Y Combinator]: https://en.wikipedia.org/wiki/Fixed-point_combinator#Y_combinator
+
+struct YCombinator<F,A,R> {
+    func: F,
+    marker: PhantomData<(A,R)>,
+}
+
+impl<F,A,R> YCombinator<F,A,R> {
+    fn new(f: F) -> YCombinator<F,A,R> {
+        YCombinator { func: f, marker: PhantomData }
+    }
+}
+
+impl<A,R,F : Fn(&dyn Fn(A) -> R, A) -> R> Fn<(A,)> for YCombinator<F,A,R> {
+    extern "rust-call" fn call(&self, (arg,): (A,)) -> R {
+        (self.func)(self, arg)
+    }
+}
+
+impl<A,R,F : Fn(&dyn Fn(A) -> R, A) -> R> FnMut<(A,)> for YCombinator<F,A,R> {
+    extern "rust-call" fn call_mut(&mut self, args: (A,)) -> R { self.call(args) }
+}
+
+impl<A,R,F : Fn(&dyn Fn(A) -> R, A) -> R> FnOnce<(A,)> for YCombinator<F,A,R> {
+    type Output = R;
+    extern "rust-call" fn call_once(self, args: (A,)) -> R { self.call(args) }
+}
+
+fn main() {
+    let factorial = |recur: &dyn Fn(u32) -> u32, arg: u32| -> u32 {
+        if arg == 0 {1} else {arg * recur(arg-1)}
+    };
+    let factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial);
+    let r = factorial(10);
+    assert_eq!(3628800, r);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-upvar.rs b/src/test/ui/unboxed-closures/unboxed-closures-infer-upvar.rs
new file mode 100644
index 000000000..6a5e5b9c2
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-upvar.rs
@@ -0,0 +1,13 @@
+// run-pass
+// Test that the type variable in the type(`Vec<_>`) of a closed over
+// variable does not interfere with type inference.
+
+fn f<F: FnMut()>(mut f: F) {
+    f();
+}
+
+fn main() {
+    let mut v: Vec<_> = vec![];
+    f(|| v.push(0));
+    assert_eq!(v, [0]);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-manual-impl.rs b/src/test/ui/unboxed-closures/unboxed-closures-manual-impl.rs
new file mode 100644
index 000000000..df60b42ab
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-manual-impl.rs
@@ -0,0 +1,31 @@
+// run-pass
+#![feature(unboxed_closures, fn_traits)]
+
+struct S;
+
+impl FnMut<(i32,)> for S {
+    extern "rust-call" fn call_mut(&mut self, (x,): (i32,)) -> i32 {
+        x * x
+    }
+}
+
+impl FnOnce<(i32,)> for S {
+    type Output = i32;
+
+    extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 { self.call_mut(args) }
+}
+
+fn call_it<F:FnMut(i32)->i32>(mut f: F, x: i32) -> i32 {
+    f(x) + 3
+}
+
+fn call_box(f: &mut dyn FnMut(i32) -> i32, x: i32) -> i32 {
+    f(x) + 3
+}
+
+fn main() {
+    let x = call_it(S, 1);
+    let y = call_box(&mut S, 1);
+    assert_eq!(x, 4);
+    assert_eq!(y, 4);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-monomorphization.rs b/src/test/ui/unboxed-closures/unboxed-closures-monomorphization.rs
new file mode 100644
index 000000000..2df360d4a
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-monomorphization.rs
@@ -0,0 +1,26 @@
+// run-pass
+// Test that unboxed closures in contexts with free type parameters
+// monomorphize correctly (issue #16791)
+
+fn main(){
+    fn bar<'a, T:Clone+'a> (t: T) -> Box<dyn FnMut()->T + 'a> {
+        Box::new(move || t.clone())
+    }
+
+    let mut f = bar(42_u32);
+    assert_eq!(f(), 42);
+
+    let mut f = bar("forty-two");
+    assert_eq!(f(), "forty-two");
+
+    let x = 42_u32;
+    let mut f = bar(&x);
+    assert_eq!(f(), &x);
+
+    #[derive(Clone, Copy, Debug, PartialEq)]
+    struct Foo(usize, &'static str);
+
+    let x = Foo(42, "forty-two");
+    let mut f = bar(x);
+    assert_eq!(f(), x);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-from-projection-issue-30046.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-from-projection-issue-30046.rs
new file mode 100644
index 000000000..4388e6bcf
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-move-from-projection-issue-30046.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(unused)]
+
+fn foo<F>(f: F)
+    where F: FnOnce()
+{
+}
+
+fn main() {
+    // Test that this closure is inferred to `FnOnce`
+    // because it moves from `y.as<Option::Some>.0`:
+    let x = Some(vec![1, 2, 3]);
+    foo(|| {
+        match x {
+            Some(y) => { }
+            None => { }
+        }
+    });
+
+    // Test that this closure is inferred to `FnOnce`
+    // because it moves from `y.0`:
+    let y = (vec![1, 2, 3], 0);
+    foo(|| {
+        let x = y.0;
+    });
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs
new file mode 100644
index 000000000..470904fd3
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs
@@ -0,0 +1,31 @@
+// run-pass
+// pretty-expanded FIXME #23616
+
+#![deny(unused_mut)]
+#![allow(unused_must_use)]
+
+// Test that mutating a mutable upvar in a capture-by-value unboxed
+// closure does not ice (issue #18238) and marks the upvar as used
+// mutably so we do not get a spurious warning about it not needing to
+// be declared mutable (issue #18336 and #18769)
+
+fn set(x: &mut usize) { *x = 42; }
+
+fn main() {
+    {
+        let mut x = 0_usize;
+        move || x += 1; //~ WARN unused variable: `x`
+    }
+    {
+        let mut x = 0_usize;
+        move || x += 1; //~ WARN unused variable: `x`
+    }
+    {
+        let mut x = 0_usize;
+        move || set(&mut x);
+    }
+    {
+        let mut x = 0_usize;
+        move || set(&mut x);
+    }
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr
new file mode 100644
index 000000000..1254f8dbc
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr
@@ -0,0 +1,19 @@
+warning: unused variable: `x`
+  --> $DIR/unboxed-closures-move-mutable.rs:17:17
+   |
+LL |         move || x += 1;
+   |                 ^
+   |
+   = note: `#[warn(unused_variables)]` on by default
+   = help: did you mean to capture by reference instead?
+
+warning: unused variable: `x`
+  --> $DIR/unboxed-closures-move-mutable.rs:21:17
+   |
+LL |         move || x += 1;
+   |                 ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-some-upvars-in-by-ref-closure.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-some-upvars-in-by-ref-closure.rs
new file mode 100644
index 000000000..2d219643f
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-move-some-upvars-in-by-ref-closure.rs
@@ -0,0 +1,23 @@
+// run-pass
+// Test that in a by-ref once closure we move some variables even as
+// we capture others by mutable reference.
+
+fn call<F>(f: F) where F : FnOnce() {
+    f();
+}
+
+fn main() {
+    let mut x = vec![format!("Hello")];
+    let y = vec![format!("World")];
+    call(|| {
+        // Here: `x` must be captured with a mutable reference in
+        // order for us to append on it, and `y` must be captured by
+        // value.
+        for item in y {
+            x.push(item);
+        }
+    });
+    assert_eq!(x.len(), 2);
+    assert_eq!(&*x[0], "Hello");
+    assert_eq!(&*x[1], "World");
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.rs b/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.rs
new file mode 100644
index 000000000..57e6d3065
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.rs
@@ -0,0 +1,57 @@
+// Test that we cannot mutate an outer variable that is not declared
+// as `mut` through a closure. Also test that we CAN mutate a moved copy,
+// unless this is a `Fn` closure. Issue #16749.
+
+#![feature(unboxed_closures)]
+
+use std::mem;
+
+fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+
+fn a() {
+    let n = 0;
+    let mut f = to_fn_mut(|| {
+        n += 1; //~ ERROR cannot assign to `n`, as it is not declared as mutable
+    });
+}
+
+fn b() {
+    let mut n = 0;
+    let mut f = to_fn_mut(|| {
+        n += 1; // OK
+    });
+}
+
+fn c() {
+    let n = 0;
+    let mut f = to_fn_mut(move || {
+        // If we just did a straight-forward desugaring, this would
+        // compile, but we do something a bit more subtle, and hence
+        // we get an error.
+        n += 1; //~ ERROR cannot assign
+    });
+}
+
+fn d() {
+    let mut n = 0;
+    let mut f = to_fn_mut(move || {
+        n += 1; // OK
+    });
+}
+
+fn e() {
+    let n = 0;
+    let mut f = to_fn(move || {
+        n += 1; //~ ERROR cannot assign
+    });
+}
+
+fn f() {
+    let mut n = 0;
+    let mut f = to_fn(move || {
+        n += 1; //~ ERROR cannot assign
+    });
+}
+
+fn main() { }
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr b/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr
new file mode 100644
index 000000000..d6e74b5b8
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-mutate-upvar.stderr
@@ -0,0 +1,43 @@
+error[E0594]: cannot assign to `n`, as it is not declared as mutable
+  --> $DIR/unboxed-closures-mutate-upvar.rs:15:9
+   |
+LL |     let n = 0;
+   |         - help: consider changing this to be mutable: `mut n`
+LL |     let mut f = to_fn_mut(|| {
+LL |         n += 1;
+   |         ^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `n`, as it is not declared as mutable
+  --> $DIR/unboxed-closures-mutate-upvar.rs:32:9
+   |
+LL |     let n = 0;
+   |         - help: consider changing this to be mutable: `mut n`
+...
+LL |         n += 1;
+   |         ^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `n`, as it is not declared as mutable
+  --> $DIR/unboxed-closures-mutate-upvar.rs:46:9
+   |
+LL |     let n = 0;
+   |         - help: consider changing this to be mutable: `mut n`
+LL |     let mut f = to_fn(move || {
+LL |         n += 1;
+   |         ^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `n`, as it is a captured variable in a `Fn` closure
+  --> $DIR/unboxed-closures-mutate-upvar.rs:53:9
+   |
+LL | fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
+   |                        - change this to accept `FnMut` instead of `Fn`
+...
+LL |     let mut f = to_fn(move || {
+   |                 ----- ------- in this closure
+   |                 |
+   |                 expects `Fn` instead of `FnMut`
+LL |         n += 1;
+   |         ^^^^^^ cannot assign
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.rs b/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.rs
new file mode 100644
index 000000000..174ad245d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.rs
@@ -0,0 +1,14 @@
+// Test that a by-ref `FnMut` closure gets an error when it tries to
+// mutate a value.
+
+fn call<F>(f: F) where F : Fn() {
+    f();
+}
+
+fn main() {
+    let mut counter = 0;
+    call(|| {
+        counter += 1;
+        //~^ ERROR cannot assign to `counter`
+    });
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr b/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr
new file mode 100644
index 000000000..7d15cd0c8
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-mutated-upvar-from-fn-closure.stderr
@@ -0,0 +1,16 @@
+error[E0594]: cannot assign to `counter`, as it is a captured variable in a `Fn` closure
+  --> $DIR/unboxed-closures-mutated-upvar-from-fn-closure.rs:11:9
+   |
+LL | fn call<F>(f: F) where F : Fn() {
+   |               - change this to accept `FnMut` instead of `Fn`
+...
+LL |     call(|| {
+   |     ---- -- in this closure
+   |     |
+   |     expects `Fn` instead of `FnMut`
+LL |         counter += 1;
+   |         ^^^^^^^^^^^^ cannot assign
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-prelude.rs b/src/test/ui/unboxed-closures/unboxed-closures-prelude.rs
new file mode 100644
index 000000000..89a273b7a
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-prelude.rs
@@ -0,0 +1,18 @@
+// run-pass
+// Tests that the re-exports of `FnOnce` et al from the prelude work.
+
+// pretty-expanded FIXME #23616
+
+fn main() {
+    let task: Box<dyn Fn(isize) -> isize> = Box::new(|x| x);
+    task(0);
+
+    let mut task: Box<dyn FnMut(isize) -> isize> = Box::new(|x| x);
+    task(0);
+
+    call(|x| x, 22);
+}
+
+fn call<F:FnOnce(isize) -> isize>(f: F, x: isize) -> isize {
+    f(x)
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.rs b/src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.rs
new file mode 100644
index 000000000..5e354cb6f
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.rs
@@ -0,0 +1,43 @@
+#![feature(fn_traits, unboxed_closures)]
+
+use std::marker::PhantomData;
+
+// An erroneous variant of `run-pass/unboxed_closures-infer-recursive-fn.rs`
+// where we attempt to perform mutation in the recursive function. This fails to compile
+// because it winds up requiring `FnMut` which enforces linearity.
+
+struct YCombinator<F,A,R> {
+    func: F,
+    marker: PhantomData<(A,R)>,
+}
+
+impl<F,A,R> YCombinator<F,A,R> {
+    fn new(f: F) -> YCombinator<F,A,R> {
+        YCombinator { func: f, marker: PhantomData }
+    }
+}
+
+impl<A,R,F : FnMut(&mut dyn FnMut(A) -> R, A) -> R> FnMut<(A,)> for YCombinator<F,A,R> {
+    extern "rust-call" fn call_mut(&mut self, (arg,): (A,)) -> R {
+        (self.func)(self, arg)
+            //~^ ERROR cannot borrow `*self` as mutable more than once at a time
+    }
+}
+
+impl<A,R,F : FnMut(&mut dyn FnMut(A) -> R, A) -> R> FnOnce<(A,)> for YCombinator<F,A,R> {
+    type Output = R;
+    extern "rust-call" fn call_once(mut self, args: (A,)) -> R {
+        self.call_mut(args)
+    }
+}
+
+fn main() {
+    let mut counter = 0;
+    let factorial = |recur: &mut dyn FnMut(u32) -> u32, arg: u32| -> u32 {
+        counter += 1;
+        if arg == 0 {1} else {arg * recur(arg-1)}
+    };
+    let mut factorial: YCombinator<_,u32,u32> = YCombinator::new(factorial);
+    let mut r = factorial(10);
+    assert_eq!(3628800, r);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.stderr
new file mode 100644
index 000000000..830f6bc99
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-recursive-fn-using-fn-mut.stderr
@@ -0,0 +1,12 @@
+error[E0499]: cannot borrow `*self` as mutable more than once at a time
+  --> $DIR/unboxed-closures-recursive-fn-using-fn-mut.rs:22:21
+   |
+LL |         (self.func)(self, arg)
+   |         ----------- ^^^^ second mutable borrow occurs here
+   |         |
+   |         first mutable borrow occurs here
+   |         first borrow later used by call
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-simple.rs b/src/test/ui/unboxed-closures/unboxed-closures-simple.rs
new file mode 100644
index 000000000..144955402
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-simple.rs
@@ -0,0 +1,10 @@
+// run-pass
+#![allow(unused_mut)]
+#![allow(unused_imports)]
+use std::ops::FnMut;
+
+pub fn main() {
+    let mut f = |x: isize, y: isize| -> isize { x + y };
+    let z = f(1, 2);
+    assert_eq!(z, 3);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-single-word-env.rs b/src/test/ui/unboxed-closures/unboxed-closures-single-word-env.rs
new file mode 100644
index 000000000..8ada7494e
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-single-word-env.rs
@@ -0,0 +1,22 @@
+// run-pass
+// Ensures that single-word environments work right in unboxed closures.
+// These take a different path in codegen.
+
+fn a<F:Fn(isize, isize) -> isize>(f: F) -> isize {
+    f(1, 2)
+}
+
+fn b<F:FnMut(isize, isize) -> isize>(mut f: F) -> isize {
+    f(3, 4)
+}
+
+fn c<F:FnOnce(isize, isize) -> isize>(f: F) -> isize {
+    f(5, 6)
+}
+
+fn main() {
+    let z = 10;
+    assert_eq!(a(move |x: isize, y| x + y + z), 13);
+    assert_eq!(b(move |x: isize, y| x + y + z), 17);
+    assert_eq!(c(move |x: isize, y| x + y + z), 21);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-fn-once.rs b/src/test/ui/unboxed-closures/unboxed-closures-static-call-fn-once.rs
new file mode 100644
index 000000000..054f284ea
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-fn-once.rs
@@ -0,0 +1,7 @@
+// run-pass
+// pretty-expanded FIXME #23616
+
+fn main() {
+    let onetime = |x| x;
+    onetime(0);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.rs b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.rs
new file mode 100644
index 000000000..0e727b11c
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.rs
@@ -0,0 +1,8 @@
+#![feature(unboxed_closures)]
+
+fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
+
+fn main() {
+    let mut_ = to_fn_mut(|x| x);
+    mut_.call((0, )); //~ ERROR no method named `call` found
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
new file mode 100644
index 000000000..4f89afa32
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr
@@ -0,0 +1,11 @@
+error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]` in the current scope
+  --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
+   |
+LL |     mut_.call((0, ));
+   |     ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
+   |     |
+   |     this is a function, perhaps you wish to call it
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-sugar-object.rs b/src/test/ui/unboxed-closures/unboxed-closures-sugar-object.rs
new file mode 100644
index 000000000..1ca25517c
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-sugar-object.rs
@@ -0,0 +1,25 @@
+// run-pass
+// Test unboxed closure sugar used in object types.
+
+#![allow(dead_code)]
+
+struct Foo<T,U> {
+    t: T, u: U
+}
+
+trait Getter<A,R> {
+    fn get(&self, arg: A) -> R;
+}
+
+struct Identity;
+impl<X> Getter<X,X> for Identity {
+    fn get(&self, arg: X) -> X {
+        arg
+    }
+}
+
+fn main() {
+    let x: &dyn Getter<(i32,), (i32,)> = &Identity;
+    let (y,) = x.get((22,));
+    assert_eq!(y, 22);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.rs b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.rs
new file mode 100644
index 000000000..9f76849e5
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.rs
@@ -0,0 +1,7 @@
+use std::ops::FnMut;
+
+pub fn main() {
+    let mut f = |x: isize, y: isize| -> isize { x + y };
+    let z = f(1_usize, 2);    //~ ERROR mismatched types
+    println!("{}", z);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr
new file mode 100644
index 000000000..3241c9f85
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-type-mismatch.stderr
@@ -0,0 +1,21 @@
+error[E0308]: mismatched types
+  --> $DIR/unboxed-closures-type-mismatch.rs:5:15
+   |
+LL |     let z = f(1_usize, 2);
+   |             - ^^^^^^^ expected `isize`, found `usize`
+   |             |
+   |             arguments to this function are incorrect
+   |
+note: closure defined here
+  --> $DIR/unboxed-closures-type-mismatch.rs:4:17
+   |
+LL |     let mut f = |x: isize, y: isize| -> isize { x + y };
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: change the type of the numeric literal from `usize` to `isize`
+   |
+LL |     let z = f(1_isize, 2);
+   |                 ~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs b/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs
new file mode 100644
index 000000000..4b7016def
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-unique-type-id.rs
@@ -0,0 +1,26 @@
+// run-pass
+
+// This code used to produce the following ICE:
+//
+//    error: internal compiler error: get_unique_type_id_of_type() -
+//    unexpected type: closure,
+//    Closure(rustc_ast::DefId{krate: 0, node: 66},
+//    ReScope(63))
+//
+// This is a regression test for issue #17021.
+//
+// compile-flags: -g
+// ignore-asmjs wasm2js does not support source maps yet
+
+use std::ptr;
+
+pub fn replace_map<'a, T, F>(src: &mut T, prod: F) where F: FnOnce(T) -> T {
+    unsafe { *src = prod(ptr::read(src as *mut T as *const T)); }
+}
+
+pub fn main() {
+    let mut a = 7;
+    let b = &mut a;
+    replace_map(b, |x: usize| x * 2);
+    assert_eq!(*b, 14);
+}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs
new file mode 100644
index 000000000..e2082d4f7
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.rs
@@ -0,0 +1,34 @@
+// Tests that unsafe extern fn pointers do not implement any Fn traits.
+
+use std::ops::{Fn, FnMut, FnOnce};
+
+unsafe fn square(x: &isize) -> isize {
+    (*x) * (*x)
+}
+
+fn call_it<F: Fn(&isize) -> isize>(_: &F, _: isize) -> isize {
+    0
+}
+fn call_it_mut<F: FnMut(&isize) -> isize>(_: &mut F, _: isize) -> isize {
+    0
+}
+fn call_it_once<F: FnOnce(&isize) -> isize>(_: F, _: isize) -> isize {
+    0
+}
+
+fn a() {
+    let x = call_it(&square, 22);
+    //~^ ERROR E0277
+}
+
+fn b() {
+    let y = call_it_mut(&mut square, 22);
+    //~^ ERROR E0277
+}
+
+fn c() {
+    let z = call_it_once(square, 22);
+    //~^ ERROR E0277
+}
+
+fn main() {}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr
new file mode 100644
index 000000000..18e133957
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-unsafe-extern-fn.stderr
@@ -0,0 +1,51 @@
+error[E0277]: expected a `Fn<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}`
+  --> $DIR/unboxed-closures-unsafe-extern-fn.rs:20:21
+   |
+LL |     let x = call_it(&square, 22);
+   |             ------- ^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> Fn<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `call_it`
+  --> $DIR/unboxed-closures-unsafe-extern-fn.rs:9:15
+   |
+LL | fn call_it<F: Fn(&isize) -> isize>(_: &F, _: isize) -> isize {
+   |               ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it`
+
+error[E0277]: expected a `FnMut<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}`
+  --> $DIR/unboxed-closures-unsafe-extern-fn.rs:25:25
+   |
+LL |     let y = call_it_mut(&mut square, 22);
+   |             ----------- ^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> FnMut<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `call_it_mut`
+  --> $DIR/unboxed-closures-unsafe-extern-fn.rs:12:19
+   |
+LL | fn call_it_mut<F: FnMut(&isize) -> isize>(_: &mut F, _: isize) -> isize {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut`
+
+error[E0277]: expected a `FnOnce<(&isize,)>` closure, found `for<'r> unsafe fn(&'r isize) -> isize {square}`
+  --> $DIR/unboxed-closures-unsafe-extern-fn.rs:30:26
+   |
+LL |     let z = call_it_once(square, 22);
+   |             ------------ ^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> FnOnce<(&'r isize,)>` is not implemented for `for<'r> unsafe fn(&'r isize) -> isize {square}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `call_it_once`
+  --> $DIR/unboxed-closures-unsafe-extern-fn.rs:15:20
+   |
+LL | fn call_it_once<F: FnOnce(&isize) -> isize>(_: F, _: isize) -> isize {
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_once`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs
new file mode 100644
index 000000000..dd76c597d
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.rs
@@ -0,0 +1,34 @@
+// Tests that unsafe extern fn pointers do not implement any Fn traits.
+
+use std::ops::{Fn, FnMut, FnOnce};
+
+extern "C" fn square(x: &isize) -> isize {
+    (*x) * (*x)
+}
+
+fn call_it<F: Fn(&isize) -> isize>(_: &F, _: isize) -> isize {
+    0
+}
+fn call_it_mut<F: FnMut(&isize) -> isize>(_: &mut F, _: isize) -> isize {
+    0
+}
+fn call_it_once<F: FnOnce(&isize) -> isize>(_: F, _: isize) -> isize {
+    0
+}
+
+fn a() {
+    let x = call_it(&square, 22);
+    //~^ ERROR E0277
+}
+
+fn b() {
+    let y = call_it_mut(&mut square, 22);
+    //~^ ERROR E0277
+}
+
+fn c() {
+    let z = call_it_once(square, 22);
+    //~^ ERROR E0277
+}
+
+fn main() {}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr
new file mode 100644
index 000000000..77c176de6
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-abi.stderr
@@ -0,0 +1,48 @@
+error[E0277]: expected a `Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+  --> $DIR/unboxed-closures-wrong-abi.rs:20:21
+   |
+LL |     let x = call_it(&square, 22);
+   |             ------- ^^^^^^^ expected an `Fn<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> Fn<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+note: required by a bound in `call_it`
+  --> $DIR/unboxed-closures-wrong-abi.rs:9:15
+   |
+LL | fn call_it<F: Fn(&isize) -> isize>(_: &F, _: isize) -> isize {
+   |               ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it`
+
+error[E0277]: expected a `FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+  --> $DIR/unboxed-closures-wrong-abi.rs:25:25
+   |
+LL |     let y = call_it_mut(&mut square, 22);
+   |             ----------- ^^^^^^^^^^^ expected an `FnMut<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> FnMut<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+note: required by a bound in `call_it_mut`
+  --> $DIR/unboxed-closures-wrong-abi.rs:12:19
+   |
+LL | fn call_it_mut<F: FnMut(&isize) -> isize>(_: &mut F, _: isize) -> isize {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut`
+
+error[E0277]: expected a `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+  --> $DIR/unboxed-closures-wrong-abi.rs:30:26
+   |
+LL |     let z = call_it_once(square, 22);
+   |             ------------ ^^^^^^ expected an `FnOnce<(&isize,)>` closure, found `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> FnOnce<(&'r isize,)>` is not implemented for `for<'r> extern "C" fn(&'r isize) -> isize {square}`
+note: required by a bound in `call_it_once`
+  --> $DIR/unboxed-closures-wrong-abi.rs:15:20
+   |
+LL | fn call_it_once<F: FnOnce(&isize) -> isize>(_: F, _: isize) -> isize {
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_once`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs
new file mode 100644
index 000000000..02e8b7b47
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.rs
@@ -0,0 +1,35 @@
+// Tests that unsafe extern fn pointers do not implement any Fn traits.
+
+use std::ops::{Fn, FnMut, FnOnce};
+
+unsafe fn square(x: isize) -> isize {
+    x * x
+}
+// note: argument type here is `isize`, not `&isize`
+
+fn call_it<F: Fn(&isize) -> isize>(_: &F, _: isize) -> isize {
+    0
+}
+fn call_it_mut<F: FnMut(&isize) -> isize>(_: &mut F, _: isize) -> isize {
+    0
+}
+fn call_it_once<F: FnOnce(&isize) -> isize>(_: F, _: isize) -> isize {
+    0
+}
+
+fn a() {
+    let x = call_it(&square, 22);
+    //~^ ERROR E0277
+}
+
+fn b() {
+    let y = call_it_mut(&mut square, 22);
+    //~^ ERROR E0277
+}
+
+fn c() {
+    let z = call_it_once(square, 22);
+    //~^ ERROR E0277
+}
+
+fn main() {}
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr
new file mode 100644
index 000000000..c826af3c4
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-wrong-arg-type-extern-fn.stderr
@@ -0,0 +1,51 @@
+error[E0277]: expected a `Fn<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}`
+  --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:21:21
+   |
+LL |     let x = call_it(&square, 22);
+   |             ------- ^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> Fn<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `call_it`
+  --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:10:15
+   |
+LL | fn call_it<F: Fn(&isize) -> isize>(_: &F, _: isize) -> isize {
+   |               ^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it`
+
+error[E0277]: expected a `FnMut<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}`
+  --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:26:25
+   |
+LL |     let y = call_it_mut(&mut square, 22);
+   |             ----------- ^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> FnMut<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `call_it_mut`
+  --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:13:19
+   |
+LL | fn call_it_mut<F: FnMut(&isize) -> isize>(_: &mut F, _: isize) -> isize {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_mut`
+
+error[E0277]: expected a `FnOnce<(&isize,)>` closure, found `unsafe fn(isize) -> isize {square}`
+  --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:31:26
+   |
+LL |     let z = call_it_once(square, 22);
+   |             ------------ ^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |             |
+   |             required by a bound introduced by this call
+   |
+   = help: the trait `for<'r> FnOnce<(&'r isize,)>` is not implemented for `unsafe fn(isize) -> isize {square}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `call_it_once`
+  --> $DIR/unboxed-closures-wrong-arg-type-extern-fn.rs:16:20
+   |
+LL | fn call_it_once<F: FnOnce(&isize) -> isize>(_: F, _: isize) -> isize {
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `call_it_once`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-zero-args.rs b/src/test/ui/unboxed-closures/unboxed-closures-zero-args.rs
new file mode 100644
index 000000000..6f41c3558
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-zero-args.rs
@@ -0,0 +1,8 @@
+// run-pass
+#![allow(unused_mut)]
+// pretty-expanded FIXME #23616
+
+fn main() {
+    let mut zero = || {};
+    let () = zero();
+}
-- 
cgit v1.2.3