about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJosh White <jwhite927@gmail.com>2020-02-08 16:30:40 -0500
committerJosh White <jwhite927@gmail.com>2020-02-08 16:30:40 -0500
commitd705ad28a0129ec52c686d3476b731590ca207b7 (patch)
treea01e215c90c75dbfe5bc044a5e8b52c3a9bfd2bc
parent9d54bb28b30d26688f71cff672c68ed4fe00ab1e (diff)
parent85ffd44d3d86214fc06be4add039e26f85261a2b (diff)
downloadrust-d705ad28a0129ec52c686d3476b731590ca207b7.tar.gz
rust-d705ad28a0129ec52c686d3476b731590ca207b7.zip
Merge branch 'master' of https://github.com/jwhite927/rust into E0637
-rw-r--r--src/libcore/iter/range.rs4
-rw-r--r--src/libcore/ops/range.rs38
-rw-r--r--src/libcore/slice/mod.rs80
-rw-r--r--src/libcore/str/mod.rs10
-rw-r--r--src/libcore/tests/iter.rs16
-rw-r--r--src/librustc_target/abi/call/aarch64.rs2
-rw-r--r--src/librustc_target/abi/call/arm.rs2
-rw-r--r--src/librustc_target/abi/call/mod.rs163
-rw-r--r--src/librustc_target/abi/call/powerpc64.rs2
-rw-r--r--src/librustc_target/abi/call/sparc64.rs2
-rw-r--r--src/librustc_target/abi/call/wasm32.rs2
-rw-r--r--src/librustc_target/abi/call/x86.rs2
-rw-r--r--src/librustc_target/abi/call/x86_64.rs24
-rw-r--r--src/test/auxiliary/rust_test_helpers.c84
-rw-r--r--src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile7
-rw-r--r--src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs31
-rw-r--r--src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c66
-rw-r--r--src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile5
-rw-r--r--src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs21
-rw-r--r--src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c85
-rw-r--r--src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile5
-rw-r--r--src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs31
-rw-r--r--src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c61
-rw-r--r--src/test/run-make-fulldeps/return-non-c-like-enum/Makefile7
-rw-r--r--src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs21
-rw-r--r--src/test/run-make-fulldeps/return-non-c-like-enum/test.c63
-rw-r--r--src/test/ui/abi/abi-sysv64-arg-passing.rs81
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs4
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr4
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs10
-rw-r--r--src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr10
-rw-r--r--src/test/ui/layout/zero-sized-array-union.rs8
-rw-r--r--src/test/ui/layout/zero-sized-array-union.stderr8
-rw-r--r--src/test/ui/specialization/soundness/partial_eq_range_inclusive.rs35
-rw-r--r--src/test/ui/specialization/soundness/partial_ord_slice.rs42
35 files changed, 884 insertions, 152 deletions
diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs
index eac3c107d22..be9d832ed90 100644
--- a/src/libcore/iter/range.rs
+++ b/src/libcore/iter/range.rs
@@ -385,12 +385,14 @@ impl<A: Step> Iterator for ops::RangeInclusive<A> {
                 }
                 Some(Equal) => {
                     self.is_empty = Some(true);
+                    self.start = plus_n.clone();
                     return Some(plus_n);
                 }
                 _ => {}
             }
         }
 
+        self.start = self.end.clone();
         self.is_empty = Some(true);
         None
     }
@@ -477,12 +479,14 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
                 }
                 Some(Equal) => {
                     self.is_empty = Some(true);
+                    self.end = minus_n.clone();
                     return Some(minus_n);
                 }
                 _ => {}
             }
         }
 
+        self.end = self.start.clone();
         self.is_empty = Some(true);
         None
     }
diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs
index d38b3516569..6c0bc6bbbad 100644
--- a/src/libcore/ops/range.rs
+++ b/src/libcore/ops/range.rs
@@ -343,38 +343,21 @@ pub struct RangeInclusive<Idx> {
     pub(crate) is_empty: Option<bool>,
     // This field is:
     //  - `None` when next() or next_back() was never called
-    //  - `Some(false)` when `start <= end` assuming no overflow
-    //  - `Some(true)` otherwise
+    //  - `Some(false)` when `start < end`
+    //  - `Some(true)` when `end < start`
+    //  - `Some(false)` when `start == end` and the range hasn't yet completed iteration
+    //  - `Some(true)` when `start == end` and the range has completed iteration
     // The field cannot be a simple `bool` because the `..=` constructor can
     // accept non-PartialOrd types, also we want the constructor to be const.
 }
 
-trait RangeInclusiveEquality: Sized {
-    fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool;
-}
-
-impl<T> RangeInclusiveEquality for T {
-    #[inline]
-    default fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool {
-        range.is_empty.unwrap_or_default()
-    }
-}
-
-impl<T: PartialOrd> RangeInclusiveEquality for T {
-    #[inline]
-    fn canonicalized_is_empty(range: &RangeInclusive<Self>) -> bool {
-        range.is_empty()
-    }
-}
-
 #[stable(feature = "inclusive_range", since = "1.26.0")]
 impl<Idx: PartialEq> PartialEq for RangeInclusive<Idx> {
     #[inline]
     fn eq(&self, other: &Self) -> bool {
         self.start == other.start
             && self.end == other.end
-            && RangeInclusiveEquality::canonicalized_is_empty(self)
-                == RangeInclusiveEquality::canonicalized_is_empty(other)
+            && self.is_exhausted() == other.is_exhausted()
     }
 }
 
@@ -386,7 +369,8 @@ impl<Idx: Hash> Hash for RangeInclusive<Idx> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.start.hash(state);
         self.end.hash(state);
-        RangeInclusiveEquality::canonicalized_is_empty(self).hash(state);
+        // Ideally we would hash `is_exhausted` here as well, but there's no
+        // way for us to call it.
     }
 }
 
@@ -485,6 +469,14 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
     }
 }
 
+impl<Idx: PartialEq<Idx>> RangeInclusive<Idx> {
+    // Returns true if this is a range that started non-empty, and was iterated
+    // to exhaustion.
+    fn is_exhausted(&self) -> bool {
+        Some(true) == self.is_empty && self.start == self.end
+    }
+}
+
 impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
     /// Returns `true` if `item` is contained in the range.
     ///
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index 9b4d2015732..e79a775325f 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -5584,21 +5584,18 @@ where
 
 #[doc(hidden)]
 // intermediate trait for specialization of slice's PartialOrd
-trait SlicePartialOrd<B> {
-    fn partial_compare(&self, other: &[B]) -> Option<Ordering>;
+trait SlicePartialOrd: Sized {
+    fn partial_compare(left: &[Self], right: &[Self]) -> Option<Ordering>;
 }
 
-impl<A> SlicePartialOrd<A> for [A]
-where
-    A: PartialOrd,
-{
-    default fn partial_compare(&self, other: &[A]) -> Option<Ordering> {
-        let l = cmp::min(self.len(), other.len());
+impl<A: PartialOrd> SlicePartialOrd for A {
+    default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> {
+        let l = cmp::min(left.len(), right.len());
 
         // Slice to the loop iteration range to enable bound check
         // elimination in the compiler
-        let lhs = &self[..l];
-        let rhs = &other[..l];
+        let lhs = &left[..l];
+        let rhs = &right[..l];
 
         for i in 0..l {
             match lhs[i].partial_cmp(&rhs[i]) {
@@ -5607,36 +5604,61 @@ where
             }
         }
 
-        self.len().partial_cmp(&other.len())
+        left.len().partial_cmp(&right.len())
     }
 }
 
-impl<A> SlicePartialOrd<A> for [A]
+// This is the impl that we would like to have. Unfortunately it's not sound.
+// See `partial_ord_slice.rs`.
+/*
+impl<A> SlicePartialOrd for A
 where
     A: Ord,
 {
-    default fn partial_compare(&self, other: &[A]) -> Option<Ordering> {
-        Some(SliceOrd::compare(self, other))
+    default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> {
+        Some(SliceOrd::compare(left, right))
+    }
+}
+*/
+
+impl<A: AlwaysApplicableOrd> SlicePartialOrd for A {
+    fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> {
+        Some(SliceOrd::compare(left, right))
+    }
+}
+
+trait AlwaysApplicableOrd: SliceOrd + Ord {}
+
+macro_rules! always_applicable_ord {
+    ($([$($p:tt)*] $t:ty,)*) => {
+        $(impl<$($p)*> AlwaysApplicableOrd for $t {})*
     }
 }
 
+always_applicable_ord! {
+    [] u8, [] u16, [] u32, [] u64, [] u128, [] usize,
+    [] i8, [] i16, [] i32, [] i64, [] i128, [] isize,
+    [] bool, [] char,
+    [T: ?Sized] *const T, [T: ?Sized] *mut T,
+    [T: AlwaysApplicableOrd] &T,
+    [T: AlwaysApplicableOrd] &mut T,
+    [T: AlwaysApplicableOrd] Option<T>,
+}
+
 #[doc(hidden)]
 // intermediate trait for specialization of slice's Ord
-trait SliceOrd<B> {
-    fn compare(&self, other: &[B]) -> Ordering;
+trait SliceOrd: Sized {
+    fn compare(left: &[Self], right: &[Self]) -> Ordering;
 }
 
-impl<A> SliceOrd<A> for [A]
-where
-    A: Ord,
-{
-    default fn compare(&self, other: &[A]) -> Ordering {
-        let l = cmp::min(self.len(), other.len());
+impl<A: Ord> SliceOrd for A {
+    default fn compare(left: &[Self], right: &[Self]) -> Ordering {
+        let l = cmp::min(left.len(), right.len());
 
         // Slice to the loop iteration range to enable bound check
         // elimination in the compiler
-        let lhs = &self[..l];
-        let rhs = &other[..l];
+        let lhs = &left[..l];
+        let rhs = &right[..l];
 
         for i in 0..l {
             match lhs[i].cmp(&rhs[i]) {
@@ -5645,19 +5667,19 @@ where
             }
         }
 
-        self.len().cmp(&other.len())
+        left.len().cmp(&right.len())
     }
 }
 
 // memcmp compares a sequence of unsigned bytes lexicographically.
 // this matches the order we want for [u8], but no others (not even [i8]).
-impl SliceOrd<u8> for [u8] {
+impl SliceOrd for u8 {
     #[inline]
-    fn compare(&self, other: &[u8]) -> Ordering {
+    fn compare(left: &[Self], right: &[Self]) -> Ordering {
         let order =
-            unsafe { memcmp(self.as_ptr(), other.as_ptr(), cmp::min(self.len(), other.len())) };
+            unsafe { memcmp(left.as_ptr(), right.as_ptr(), cmp::min(left.len(), right.len())) };
         if order == 0 {
-            self.len().cmp(&other.len())
+            left.len().cmp(&right.len())
         } else if order < 0 {
             Less
         } else {
diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs
index 5a7cddd4041..734b3ba7c6b 100644
--- a/src/libcore/str/mod.rs
+++ b/src/libcore/str/mod.rs
@@ -12,7 +12,7 @@ use self::pattern::{DoubleEndedSearcher, ReverseSearcher, SearchStep, Searcher};
 use crate::char;
 use crate::fmt::{self, Write};
 use crate::iter::{Chain, FlatMap, Flatten};
-use crate::iter::{Cloned, Filter, FusedIterator, Map, TrustedLen, TrustedRandomAccess};
+use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen, TrustedRandomAccess};
 use crate::mem;
 use crate::ops::Try;
 use crate::option;
@@ -750,7 +750,7 @@ impl<'a> CharIndices<'a> {
 /// [`str`]: ../../std/primitive.str.html
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone, Debug)]
-pub struct Bytes<'a>(Cloned<slice::Iter<'a, u8>>);
+pub struct Bytes<'a>(Copied<slice::Iter<'a, u8>>);
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Iterator for Bytes<'_> {
@@ -2778,7 +2778,7 @@ impl str {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn bytes(&self) -> Bytes<'_> {
-        Bytes(self.as_bytes().iter().cloned())
+        Bytes(self.as_bytes().iter().copied())
     }
 
     /// Splits a string slice by whitespace.
@@ -3895,7 +3895,7 @@ impl str {
             debug_assert_eq!(
                 start, 0,
                 "The first search step from Searcher \
-                must include the first character"
+                 must include the first character"
             );
             // SAFETY: `Searcher` is known to return valid indices.
             unsafe { Some(self.get_unchecked(len..)) }
@@ -3934,7 +3934,7 @@ impl str {
                 end,
                 self.len(),
                 "The first search step from ReverseSearcher \
-                must include the last character"
+                 must include the last character"
             );
             // SAFETY: `Searcher` is known to return valid indices.
             unsafe { Some(self.get_unchecked(..start)) }
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index bd3218ec27f..5b41ef35065 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -1956,11 +1956,19 @@ fn test_range_inclusive_exhaustion() {
     assert_eq!(r.next(), None);
     assert_eq!(r.next(), None);
 
+    assert_eq!(*r.start(), 10);
+    assert_eq!(*r.end(), 10);
+    assert_ne!(r, 10..=10);
+
     let mut r = 10..=10;
     assert_eq!(r.next_back(), Some(10));
     assert!(r.is_empty());
     assert_eq!(r.next_back(), None);
 
+    assert_eq!(*r.start(), 10);
+    assert_eq!(*r.end(), 10);
+    assert_ne!(r, 10..=10);
+
     let mut r = 10..=12;
     assert_eq!(r.next(), Some(10));
     assert_eq!(r.next(), Some(11));
@@ -2078,6 +2086,9 @@ fn test_range_inclusive_nth() {
     assert_eq!((10..=15).nth(5), Some(15));
     assert_eq!((10..=15).nth(6), None);
 
+    let mut exhausted_via_next = 10_u8..=20;
+    while exhausted_via_next.next().is_some() {}
+
     let mut r = 10_u8..=20;
     assert_eq!(r.nth(2), Some(12));
     assert_eq!(r, 13..=20);
@@ -2087,6 +2098,7 @@ fn test_range_inclusive_nth() {
     assert_eq!(ExactSizeIterator::is_empty(&r), false);
     assert_eq!(r.nth(10), None);
     assert_eq!(r.is_empty(), true);
+    assert_eq!(r, exhausted_via_next);
     assert_eq!(ExactSizeIterator::is_empty(&r), true);
 }
 
@@ -2098,6 +2110,9 @@ fn test_range_inclusive_nth_back() {
     assert_eq!((10..=15).nth_back(6), None);
     assert_eq!((-120..=80_i8).nth_back(200), Some(-120));
 
+    let mut exhausted_via_next_back = 10_u8..=20;
+    while exhausted_via_next_back.next_back().is_some() {}
+
     let mut r = 10_u8..=20;
     assert_eq!(r.nth_back(2), Some(18));
     assert_eq!(r, 10..=17);
@@ -2107,6 +2122,7 @@ fn test_range_inclusive_nth_back() {
     assert_eq!(ExactSizeIterator::is_empty(&r), false);
     assert_eq!(r.nth_back(10), None);
     assert_eq!(r.is_empty(), true);
+    assert_eq!(r, exhausted_via_next_back);
     assert_eq!(ExactSizeIterator::is_empty(&r), true);
 }
 
diff --git a/src/librustc_target/abi/call/aarch64.rs b/src/librustc_target/abi/call/aarch64.rs
index 2dd41a0d80f..c8bac5aebc6 100644
--- a/src/librustc_target/abi/call/aarch64.rs
+++ b/src/librustc_target/abi/call/aarch64.rs
@@ -6,7 +6,7 @@ where
     Ty: TyLayoutMethods<'a, C> + Copy,
     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
 {
-    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
         let size = arg.layout.size;
 
         // Ensure we have at most four uniquely addressable members.
diff --git a/src/librustc_target/abi/call/arm.rs b/src/librustc_target/abi/call/arm.rs
index eb9364091dc..59ec87e3c9e 100644
--- a/src/librustc_target/abi/call/arm.rs
+++ b/src/librustc_target/abi/call/arm.rs
@@ -7,7 +7,7 @@ where
     Ty: TyLayoutMethods<'a, C> + Copy,
     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
 {
-    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
         let size = arg.layout.size;
 
         // Ensure we have at most four uniquely addressable members.
diff --git a/src/librustc_target/abi/call/mod.rs b/src/librustc_target/abi/call/mod.rs
index af82f9e3183..e3cbf176c35 100644
--- a/src/librustc_target/abi/call/mod.rs
+++ b/src/librustc_target/abi/call/mod.rs
@@ -219,26 +219,47 @@ impl CastTarget {
     }
 }
 
-/// Returns value from the `homogeneous_aggregate` test function.
+/// Return value from the `homogeneous_aggregate` test function.
 #[derive(Copy, Clone, Debug)]
 pub enum HomogeneousAggregate {
     /// Yes, all the "leaf fields" of this struct are passed in the
     /// same way (specified in the `Reg` value).
     Homogeneous(Reg),
 
-    /// There are distinct leaf fields passed in different ways,
-    /// or this is uninhabited.
-    Heterogeneous,
-
     /// There are no leaf fields at all.
     NoData,
 }
 
+/// Error from the `homogeneous_aggregate` test function, indicating
+/// there are distinct leaf fields passed in different ways,
+/// or this is uninhabited.
+#[derive(Copy, Clone, Debug)]
+pub struct Heterogeneous;
+
 impl HomogeneousAggregate {
     /// If this is a homogeneous aggregate, returns the homogeneous
     /// unit, else `None`.
     pub fn unit(self) -> Option<Reg> {
-        if let HomogeneousAggregate::Homogeneous(r) = self { Some(r) } else { None }
+        match self {
+            HomogeneousAggregate::Homogeneous(reg) => Some(reg),
+            HomogeneousAggregate::NoData => None,
+        }
+    }
+
+    /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
+    /// the same `struct`. Only succeeds if only one of them has any data,
+    /// or both units are identical.
+    fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
+        match (self, other) {
+            (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
+
+            (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
+                if a != b {
+                    return Err(Heterogeneous);
+                }
+                Ok(self)
+            }
+        }
     }
 }
 
@@ -250,8 +271,8 @@ impl<'a, Ty> TyLayout<'a, Ty> {
         }
     }
 
-    /// Returns `true` if this layout is an aggregate containing fields of only
-    /// a single type (e.g., `(u32, u32)`). Such aggregates are often
+    /// Returns `Homogeneous` if this layout is an aggregate containing fields of
+    /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
     /// special-cased in ABIs.
     ///
     /// Note: We generally ignore fields of zero-sized type when computing
@@ -260,13 +281,13 @@ impl<'a, Ty> TyLayout<'a, Ty> {
     /// This is public so that it can be used in unit tests, but
     /// should generally only be relevant to the ABI details of
     /// specific targets.
-    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> HomogeneousAggregate
+    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
     where
         Ty: TyLayoutMethods<'a, C> + Copy,
         C: LayoutOf<Ty = Ty, TyLayout = Self>,
     {
         match self.abi {
-            Abi::Uninhabited => HomogeneousAggregate::Heterogeneous,
+            Abi::Uninhabited => Err(Heterogeneous),
 
             // The primitive for this algorithm.
             Abi::Scalar(ref scalar) => {
@@ -274,80 +295,104 @@ impl<'a, Ty> TyLayout<'a, Ty> {
                     abi::Int(..) | abi::Pointer => RegKind::Integer,
                     abi::F32 | abi::F64 => RegKind::Float,
                 };
-                HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })
+                Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
             }
 
             Abi::Vector { .. } => {
                 assert!(!self.is_zst());
-                HomogeneousAggregate::Homogeneous(Reg { kind: RegKind::Vector, size: self.size })
+                Ok(HomogeneousAggregate::Homogeneous(Reg {
+                    kind: RegKind::Vector,
+                    size: self.size,
+                }))
             }
 
             Abi::ScalarPair(..) | Abi::Aggregate { .. } => {
-                let mut total = Size::ZERO;
-                let mut result = None;
-
-                let is_union = match self.fields {
-                    FieldPlacement::Array { count, .. } => {
-                        if count > 0 {
-                            return self.field(cx, 0).homogeneous_aggregate(cx);
-                        } else {
-                            return HomogeneousAggregate::NoData;
-                        }
-                    }
-                    FieldPlacement::Union(_) => true,
-                    FieldPlacement::Arbitrary { .. } => false,
-                };
+                // Helper for computing `homogenous_aggregate`, allowing a custom
+                // starting offset (used below for handling variants).
+                let from_fields_at =
+                    |layout: Self,
+                     start: Size|
+                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
+                        let is_union = match layout.fields {
+                            FieldPlacement::Array { count, .. } => {
+                                assert_eq!(start, Size::ZERO);
+
+                                let result = if count > 0 {
+                                    layout.field(cx, 0).homogeneous_aggregate(cx)?
+                                } else {
+                                    HomogeneousAggregate::NoData
+                                };
+                                return Ok((result, layout.size));
+                            }
+                            FieldPlacement::Union(_) => true,
+                            FieldPlacement::Arbitrary { .. } => false,
+                        };
 
-                for i in 0..self.fields.count() {
-                    if !is_union && total != self.fields.offset(i) {
-                        return HomogeneousAggregate::Heterogeneous;
-                    }
+                        let mut result = HomogeneousAggregate::NoData;
+                        let mut total = start;
 
-                    let field = self.field(cx, i);
+                        for i in 0..layout.fields.count() {
+                            if !is_union && total != layout.fields.offset(i) {
+                                return Err(Heterogeneous);
+                            }
 
-                    match (result, field.homogeneous_aggregate(cx)) {
-                        (_, HomogeneousAggregate::NoData) => {
-                            // Ignore fields that have no data
-                        }
-                        (_, HomogeneousAggregate::Heterogeneous) => {
-                            // The field itself must be a homogeneous aggregate.
-                            return HomogeneousAggregate::Heterogeneous;
-                        }
-                        // If this is the first field, record the unit.
-                        (None, HomogeneousAggregate::Homogeneous(unit)) => {
-                            result = Some(unit);
-                        }
-                        // For all following fields, the unit must be the same.
-                        (Some(prev_unit), HomogeneousAggregate::Homogeneous(unit)) => {
-                            if prev_unit != unit {
-                                return HomogeneousAggregate::Heterogeneous;
+                            let field = layout.field(cx, i);
+
+                            result = result.merge(field.homogeneous_aggregate(cx)?)?;
+
+                            // Keep track of the offset (without padding).
+                            let size = field.size;
+                            if is_union {
+                                total = total.max(size);
+                            } else {
+                                total += size;
                             }
                         }
-                    }
 
-                    // Keep track of the offset (without padding).
-                    let size = field.size;
-                    if is_union {
-                        total = total.max(size);
-                    } else {
-                        total += size;
+                        Ok((result, total))
+                    };
+
+                let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
+
+                match &self.variants {
+                    abi::Variants::Single { .. } => {}
+                    abi::Variants::Multiple { variants, .. } => {
+                        // Treat enum variants like union members.
+                        // HACK(eddyb) pretend the `enum` field (discriminant)
+                        // is at the start of every variant (otherwise the gap
+                        // at the start of all variants would disqualify them).
+                        //
+                        // NB: for all tagged `enum`s (which include all non-C-like
+                        // `enum`s with defined FFI representation), this will
+                        // match the homogenous computation on the equivalent
+                        // `struct { tag; union { variant1; ... } }` and/or
+                        // `union { struct { tag; variant1; } ... }`
+                        // (the offsets of variant fields should be identical
+                        // between the two for either to be a homogenous aggregate).
+                        let variant_start = total;
+                        for variant_idx in variants.indices() {
+                            let (variant_result, variant_total) =
+                                from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
+
+                            result = result.merge(variant_result)?;
+                            total = total.max(variant_total);
+                        }
                     }
                 }
 
                 // There needs to be no padding.
                 if total != self.size {
-                    HomogeneousAggregate::Heterogeneous
+                    Err(Heterogeneous)
                 } else {
                     match result {
-                        Some(reg) => {
+                        HomogeneousAggregate::Homogeneous(_) => {
                             assert_ne!(total, Size::ZERO);
-                            HomogeneousAggregate::Homogeneous(reg)
                         }
-                        None => {
+                        HomogeneousAggregate::NoData => {
                             assert_eq!(total, Size::ZERO);
-                            HomogeneousAggregate::NoData
                         }
                     }
+                    Ok(result)
                 }
             }
         }
diff --git a/src/librustc_target/abi/call/powerpc64.rs b/src/librustc_target/abi/call/powerpc64.rs
index 2db3954b481..93c4e97de10 100644
--- a/src/librustc_target/abi/call/powerpc64.rs
+++ b/src/librustc_target/abi/call/powerpc64.rs
@@ -22,7 +22,7 @@ where
     Ty: TyLayoutMethods<'a, C> + Copy,
     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
 {
-    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
         // ELFv1 only passes one-member aggregates transparently.
         // ELFv2 passes up to eight uniquely addressable members.
         if (abi == ELFv1 && arg.layout.size > unit.size)
diff --git a/src/librustc_target/abi/call/sparc64.rs b/src/librustc_target/abi/call/sparc64.rs
index 8bcb02b8764..c80f8316feb 100644
--- a/src/librustc_target/abi/call/sparc64.rs
+++ b/src/librustc_target/abi/call/sparc64.rs
@@ -8,7 +8,7 @@ where
     Ty: TyLayoutMethods<'a, C> + Copy,
     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
 {
-    arg.layout.homogeneous_aggregate(cx).unit().and_then(|unit| {
+    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
         // Ensure we have at most eight uniquely addressable members.
         if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
             return None;
diff --git a/src/librustc_target/abi/call/wasm32.rs b/src/librustc_target/abi/call/wasm32.rs
index 852ead8c522..9aab64ef272 100644
--- a/src/librustc_target/abi/call/wasm32.rs
+++ b/src/librustc_target/abi/call/wasm32.rs
@@ -7,7 +7,7 @@ where
     C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout,
 {
     if val.layout.is_aggregate() {
-        if let Some(unit) = val.layout.homogeneous_aggregate(cx).unit() {
+        if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) {
             let size = val.layout.size;
             if unit.size == size {
                 val.cast_to(Uniform { unit, total: size });
diff --git a/src/librustc_target/abi/call/x86.rs b/src/librustc_target/abi/call/x86.rs
index a7884849b82..e776a8b3fe4 100644
--- a/src/librustc_target/abi/call/x86.rs
+++ b/src/librustc_target/abi/call/x86.rs
@@ -100,7 +100,7 @@ where
             };
 
             // At this point we know this must be a primitive of sorts.
-            let unit = arg.layout.homogeneous_aggregate(cx).unit().unwrap();
+            let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap();
             assert_eq!(unit.size, arg.layout.size);
             if unit.kind == RegKind::Float {
                 continue;
diff --git a/src/librustc_target/abi/call/x86_64.rs b/src/librustc_target/abi/call/x86_64.rs
index a547d7262e2..4c192c46786 100644
--- a/src/librustc_target/abi/call/x86_64.rs
+++ b/src/librustc_target/abi/call/x86_64.rs
@@ -56,16 +56,24 @@ where
 
             Abi::Vector { .. } => Class::Sse,
 
-            Abi::ScalarPair(..) | Abi::Aggregate { .. } => match layout.variants {
-                abi::Variants::Single { .. } => {
-                    for i in 0..layout.fields.count() {
-                        let field_off = off + layout.fields.offset(i);
-                        classify(cx, layout.field(cx, i), cls, field_off)?;
+            Abi::ScalarPair(..) | Abi::Aggregate { .. } => {
+                for i in 0..layout.fields.count() {
+                    let field_off = off + layout.fields.offset(i);
+                    classify(cx, layout.field(cx, i), cls, field_off)?;
+                }
+
+                match &layout.variants {
+                    abi::Variants::Single { .. } => {}
+                    abi::Variants::Multiple { variants, .. } => {
+                        // Treat enum variants like union members.
+                        for variant_idx in variants.indices() {
+                            classify(cx, layout.for_variant(cx, variant_idx), cls, off)?;
+                        }
                     }
-                    return Ok(());
                 }
-                abi::Variants::Multiple { .. } => return Err(Memory),
-            },
+
+                return Ok(());
+            }
         };
 
         // Fill in `cls` for scalars (Int/Sse) and vectors (Sse).
diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c
index b95b0ca1a89..c1fe8b7743a 100644
--- a/src/test/auxiliary/rust_test_helpers.c
+++ b/src/test/auxiliary/rust_test_helpers.c
@@ -300,3 +300,87 @@ __int128 sub(__int128 a, __int128 b) {
 }
 
 #endif
+
+#define OPTION_TAG_NONE (0)
+#define OPTION_TAG_SOME (1)
+
+struct U8TaggedEnumOptionU64 {
+    uint8_t tag;
+    union {
+        uint64_t some;
+    };
+};
+
+struct U8TaggedEnumOptionU64
+rust_dbg_new_some_u64(uint64_t some) {
+    struct U8TaggedEnumOptionU64 r = {
+        .tag = OPTION_TAG_SOME,
+        .some = some,
+    };
+    return r;
+}
+
+struct U8TaggedEnumOptionU64
+rust_dbg_new_none_u64(void) {
+    struct U8TaggedEnumOptionU64 r = {
+        .tag = OPTION_TAG_NONE,
+    };
+    return r;
+}
+
+int32_t
+rust_dbg_unpack_option_u64(struct U8TaggedEnumOptionU64 o, uint64_t *into) {
+    assert(into);
+    switch (o.tag) {
+    case OPTION_TAG_SOME:
+        *into = o.some;
+        return 1;
+    case OPTION_TAG_NONE:
+        return 0;
+    default:
+        assert(0 && "unexpected tag");
+    }
+}
+
+struct U8TaggedEnumOptionU64U64 {
+    uint8_t tag;
+    union {
+        struct {
+            uint64_t a;
+            uint64_t b;
+        } some;
+    };
+};
+
+struct U8TaggedEnumOptionU64U64
+rust_dbg_new_some_u64u64(uint64_t a, uint64_t b) {
+    struct U8TaggedEnumOptionU64U64 r = {
+        .tag = OPTION_TAG_SOME,
+        .some = { .a = a, .b = b },
+    };
+    return r;
+}
+
+struct U8TaggedEnumOptionU64U64
+rust_dbg_new_none_u64u64(void) {
+    struct U8TaggedEnumOptionU64U64 r = {
+        .tag = OPTION_TAG_NONE,
+    };
+    return r;
+}
+
+int32_t
+rust_dbg_unpack_option_u64u64(struct U8TaggedEnumOptionU64U64 o, uint64_t *a, uint64_t *b) {
+    assert(a);
+    assert(b);
+    switch (o.tag) {
+    case OPTION_TAG_SOME:
+        *a = o.some.a;
+        *b = o.some.b;
+        return 1;
+    case OPTION_TAG_NONE:
+        return 0;
+    default:
+        assert(0 && "unexpected tag");
+    }
+}
diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile b/src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile
new file mode 100644
index 00000000000..5b5d620efe6
--- /dev/null
+++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/Makefile
@@ -0,0 +1,7 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) --crate-type=staticlib nonclike.rs
+	$(CC) test.c $(call STATICLIB,nonclike) $(call OUT_EXE,test) \
+		$(EXTRACFLAGS) $(EXTRACXXFLAGS)
+	$(call RUN,test)
diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs
new file mode 100644
index 00000000000..57c2c6127ed
--- /dev/null
+++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/nonclike.rs
@@ -0,0 +1,31 @@
+#[repr(C, u8)]
+pub enum TT {
+    AA(u64, u64),
+    BB,
+}
+
+#[no_mangle]
+pub extern "C" fn tt_add(a: TT, b: TT) -> u64 {
+    match (a, b) {
+        (TT::AA(a1, b1), TT::AA(a2, b2)) => a1 + a2 + b1 + b2,
+        (TT::AA(a1, b1), TT::BB) => a1 + b1,
+        (TT::BB, TT::AA(a1, b1)) => a1 + b1,
+        _ => 0,
+    }
+}
+
+#[repr(C, u8)]
+pub enum T {
+    A(u64),
+    B,
+}
+
+#[no_mangle]
+pub extern "C" fn t_add(a: T, b: T) -> u64 {
+    match (a, b) {
+        (T::A(a), T::A(b)) => a + b,
+        (T::A(a), T::B) => a,
+        (T::B, T::A(b)) => b,
+        _ => 0,
+    }
+}
diff --git a/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c
new file mode 100644
index 00000000000..0a1621e49f2
--- /dev/null
+++ b/src/test/run-make-fulldeps/arguments-non-c-like-enum/test.c
@@ -0,0 +1,66 @@
+#include <stdint.h>
+#include <assert.h>
+
+#include <stdio.h>
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum TT`
+ * type in nonclike.rs . */
+enum TT_Tag {
+  AA,
+  BB,
+};
+typedef uint8_t TT_Tag;
+
+typedef struct {
+  uint64_t _0;
+  uint64_t _1;
+} AA_Body;
+
+typedef struct {
+  TT_Tag tag;
+  union {
+    AA_Body aa;
+  };
+} TT;
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum T` type
+ * in nonclike.rs . */
+enum T_Tag {
+  A,
+  B,
+};
+typedef uint8_t T_Tag;
+
+typedef struct {
+  uint64_t _0;
+} A_Body;
+
+typedef struct {
+  T_Tag tag;
+  union {
+    A_Body a;
+  };
+} T;
+
+/* These symbols are defined by the Rust staticlib built from
+ * nonclike.rs. */
+extern uint64_t t_add(T a, T b);
+extern uint64_t tt_add(TT a, TT b);
+
+int main(int argc, char *argv[]) {
+  (void)argc; (void)argv;
+
+  /* This example works. */
+  TT xx = { .tag = AA, .aa = { ._0 = 1, ._1 = 2 } };
+  TT yy = { .tag = AA, .aa = { ._0 = 10, ._1 = 20 } };
+  uint64_t rr = tt_add(xx, yy);
+  assert(33 == rr);
+
+  /* This one used to return an incorrect result (see issue #68190). */
+  T x = { .tag = A, .a = { ._0 = 1 } };
+  T y = { .tag = A, .a = { ._0 = 10 } };
+  uint64_t r = t_add(x, y);
+  assert(11 == r);
+
+  return 0;
+}
diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile
new file mode 100644
index 00000000000..f3d9357865c
--- /dev/null
+++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all: $(call NATIVE_STATICLIB,test)
+	$(RUSTC) nonclike.rs -L$(TMPDIR) -ltest
+	$(call RUN,nonclike)
diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs
new file mode 100644
index 00000000000..517286a868d
--- /dev/null
+++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/nonclike.rs
@@ -0,0 +1,21 @@
+#[repr(C, u8)]
+pub enum TT {
+    AA(u64, u64),
+    BB,
+}
+
+#[repr(C,u8)]
+pub enum T {
+    A(u64),
+    B,
+}
+
+extern "C" {
+    pub fn t_add(a: T, b: T) -> u64;
+    pub fn tt_add(a: TT, b: TT) -> u64;
+}
+
+fn main() {
+    assert_eq!(33, unsafe { tt_add(TT::AA(1,2), TT::AA(10,20)) });
+    assert_eq!(11, unsafe { t_add(T::A(1), T::A(10)) });
+}
diff --git a/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c
new file mode 100644
index 00000000000..99511b2530f
--- /dev/null
+++ b/src/test/run-make-fulldeps/pass-non-c-like-enum-to-c/test.c
@@ -0,0 +1,85 @@
+#include <stdint.h>
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum TT`
+ * type in nonclike.rs . */
+enum TT_Tag {
+  AA,
+  BB,
+};
+typedef uint8_t TT_Tag;
+
+typedef struct {
+  uint64_t _0;
+  uint64_t _1;
+} AA_Body;
+
+typedef struct {
+  TT_Tag tag;
+  union {
+    AA_Body aa;
+  };
+} TT;
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum T` type
+ * in nonclike.rs . */
+enum T_Tag {
+  A,
+  B,
+};
+typedef uint8_t T_Tag;
+
+typedef struct {
+  uint64_t _0;
+} A_Body;
+
+typedef struct {
+  T_Tag tag;
+  union {
+    A_Body a;
+  };
+} T;
+
+uint64_t tt_add(TT a, TT b) {
+  if (a.tag == AA && b.tag == AA) {
+    return a.aa._0 + a.aa._1 + b.aa._0 + b.aa._1;
+  } else if (a.tag == AA) {
+    return a.aa._0 + a.aa._1;
+  } else if (b.tag == BB) {
+    return b.aa._0 + b.aa._1;
+  } else {
+    return 0;
+  }
+}
+
+uint64_t t_add(T a, T b) {
+  if (a.tag == A && b.tag == A) {
+    return a.a._0 + b.a._0;
+  } else if (a.tag == AA) {
+    return a.a._0;
+  } else if (b.tag == BB) {
+    return b.a._0;
+  } else {
+    return 0;
+  }
+}
+
+TT tt_new(uint64_t a, uint64_t b) {
+  TT tt = {
+    .tag = AA,
+    .aa = {
+      ._0 = a,
+      ._1 = b,
+    },
+  };
+  return tt;
+}
+
+T t_new(uint64_t a) {
+  T t = {
+    .tag = A,
+    .a = {
+      ._0 = a,
+    },
+  };
+  return t;
+}
diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile
new file mode 100644
index 00000000000..f3d9357865c
--- /dev/null
+++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/Makefile
@@ -0,0 +1,5 @@
+-include ../tools.mk
+
+all: $(call NATIVE_STATICLIB,test)
+	$(RUSTC) nonclike.rs -L$(TMPDIR) -ltest
+	$(call RUN,nonclike)
diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs
new file mode 100644
index 00000000000..ea22a2a56e0
--- /dev/null
+++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/nonclike.rs
@@ -0,0 +1,31 @@
+#[repr(C, u8)]
+pub enum TT {
+    AA(u64, u64),
+    BB,
+}
+
+#[repr(C,u8)]
+pub enum T {
+    A(u64),
+    B,
+}
+
+extern "C" {
+    pub fn t_new(a: u64) -> T;
+    pub fn tt_new(a: u64, b: u64) -> TT;
+}
+
+fn main() {
+    if let TT::AA(a, b) = unsafe { tt_new(10, 11) } {
+        assert_eq!(10, a);
+        assert_eq!(11, b);
+    } else {
+        panic!("expected TT::AA");
+    }
+
+    if let T::A(a) = unsafe { t_new(10) } {
+        assert_eq!(10, a);
+    } else {
+        panic!("expected T::A");
+    }
+}
diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c
new file mode 100644
index 00000000000..3ad135bab4a
--- /dev/null
+++ b/src/test/run-make-fulldeps/return-non-c-like-enum-from-c/test.c
@@ -0,0 +1,61 @@
+#include <stdint.h>
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum TT`
+ * type in nonclike.rs . */
+enum TT_Tag {
+  AA,
+  BB,
+};
+typedef uint8_t TT_Tag;
+
+typedef struct {
+  uint64_t _0;
+  uint64_t _1;
+} AA_Body;
+
+typedef struct {
+  TT_Tag tag;
+  union {
+    AA_Body aa;
+  };
+} TT;
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum T` type
+ * in nonclike.rs . */
+enum T_Tag {
+  A,
+  B,
+};
+typedef uint8_t T_Tag;
+
+typedef struct {
+  uint64_t _0;
+} A_Body;
+
+typedef struct {
+  T_Tag tag;
+  union {
+    A_Body a;
+  };
+} T;
+
+TT tt_new(uint64_t a, uint64_t b) {
+  TT tt = {
+    .tag = AA,
+    .aa = {
+      ._0 = a,
+      ._1 = b,
+    },
+  };
+  return tt;
+}
+
+T t_new(uint64_t a) {
+  T t = {
+    .tag = A,
+    .a = {
+      ._0 = a,
+    },
+  };
+  return t;
+}
diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/Makefile b/src/test/run-make-fulldeps/return-non-c-like-enum/Makefile
new file mode 100644
index 00000000000..5b5d620efe6
--- /dev/null
+++ b/src/test/run-make-fulldeps/return-non-c-like-enum/Makefile
@@ -0,0 +1,7 @@
+-include ../tools.mk
+
+all:
+	$(RUSTC) --crate-type=staticlib nonclike.rs
+	$(CC) test.c $(call STATICLIB,nonclike) $(call OUT_EXE,test) \
+		$(EXTRACFLAGS) $(EXTRACXXFLAGS)
+	$(call RUN,test)
diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs
new file mode 100644
index 00000000000..de529cf641a
--- /dev/null
+++ b/src/test/run-make-fulldeps/return-non-c-like-enum/nonclike.rs
@@ -0,0 +1,21 @@
+#[repr(C, u8)]
+pub enum TT {
+    AA(u64, u64),
+    BB,
+}
+
+#[no_mangle]
+pub extern "C" fn tt_new(a: u64, b: u64) -> TT {
+    TT::AA(a, b)
+}
+
+#[repr(C,u8)]
+pub enum T {
+    A(u64),
+    B,
+}
+
+#[no_mangle]
+pub extern "C" fn t_new(a: u64) -> T {
+    T::A(a)
+}
diff --git a/src/test/run-make-fulldeps/return-non-c-like-enum/test.c b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c
new file mode 100644
index 00000000000..afadd3c10c5
--- /dev/null
+++ b/src/test/run-make-fulldeps/return-non-c-like-enum/test.c
@@ -0,0 +1,63 @@
+#include <stdint.h>
+#include <assert.h>
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum TT`
+ * type in nonclike.rs . */
+enum TT_Tag {
+  AA,
+  BB,
+};
+typedef uint8_t TT_Tag;
+
+typedef struct {
+  uint64_t _0;
+  uint64_t _1;
+} AA_Body;
+
+typedef struct {
+  TT_Tag tag;
+  union {
+    AA_Body aa;
+  };
+} TT;
+
+/* This is the code generated by cbindgen 0.12.1 for the `enum T` type
+ * in nonclike.rs . */
+enum T_Tag {
+  A,
+  B,
+};
+typedef uint8_t T_Tag;
+
+typedef struct {
+  uint64_t _0;
+} A_Body;
+
+typedef struct {
+  T_Tag tag;
+  union {
+    A_Body a;
+  };
+} T;
+
+/* These symbols are defined by the Rust staticlib built from
+ * nonclike.rs. */
+extern TT tt_new(uint64_t a, uint64_t b);
+extern T t_new(uint64_t v);
+
+int main(int argc, char *argv[]) {
+  (void)argc; (void)argv;
+
+  /* This example works. */
+  TT tt = tt_new(10, 20);
+  assert(AA == tt.tag);
+  assert(10 == tt.aa._0);
+  assert(20 == tt.aa._1);
+
+  /* This one used to segfault (see issue #68190). */
+  T t = t_new(10);
+  assert(A == t.tag);
+  assert(10 == t.a._0);
+
+  return 0;
+}
diff --git a/src/test/ui/abi/abi-sysv64-arg-passing.rs b/src/test/ui/abi/abi-sysv64-arg-passing.rs
index d40006eb9b6..c87353b93a7 100644
--- a/src/test/ui/abi/abi-sysv64-arg-passing.rs
+++ b/src/test/ui/abi/abi-sysv64-arg-passing.rs
@@ -92,6 +92,18 @@ mod tests {
     #[derive(Copy, Clone)]
     pub struct Floats { a: f64, b: u8, c: f64 }
 
+    #[repr(C, u8)]
+    pub enum U8TaggedEnumOptionU64U64 {
+        None,
+        Some(u64,u64),
+    }
+
+    #[repr(C, u8)]
+    pub enum U8TaggedEnumOptionU64 {
+        None,
+        Some(u64),
+    }
+
     #[link(name = "rust_test_helpers", kind = "static")]
     extern "sysv64" {
         pub fn rust_int8_to_int32(_: i8) -> i32;
@@ -125,6 +137,16 @@ mod tests {
         ) -> f32;
         pub fn rust_dbg_abi_1(q: Quad) -> Quad;
         pub fn rust_dbg_abi_2(f: Floats) -> Floats;
+        pub fn rust_dbg_new_some_u64u64(a: u64, b: u64) -> U8TaggedEnumOptionU64U64;
+        pub fn rust_dbg_new_none_u64u64() -> U8TaggedEnumOptionU64U64;
+        pub fn rust_dbg_unpack_option_u64u64(
+            o: U8TaggedEnumOptionU64U64,
+            a: *mut u64,
+            b: *mut u64,
+        ) -> i32;
+        pub fn rust_dbg_new_some_u64(some: u64) -> U8TaggedEnumOptionU64;
+        pub fn rust_dbg_new_none_u64() -> U8TaggedEnumOptionU64;
+        pub fn rust_dbg_unpack_option_u64(o: U8TaggedEnumOptionU64, v: *mut u64) -> i32;
     }
 
     pub fn cabi_int_widening() {
@@ -336,6 +358,63 @@ mod tests {
         test1();
         test2();
     }
+
+    pub fn enum_passing_and_return_pair() {
+        let some_u64u64 = unsafe { rust_dbg_new_some_u64u64(10, 20) };
+        if let U8TaggedEnumOptionU64U64::Some(a, b) = some_u64u64 {
+            assert_eq!(10, a);
+            assert_eq!(20, b);
+        } else {
+            panic!("unexpected none");
+        }
+
+        let none_u64u64 = unsafe { rust_dbg_new_none_u64u64() };
+        if let U8TaggedEnumOptionU64U64::Some(_,_) = none_u64u64 {
+            panic!("unexpected some");
+        }
+
+        let mut a: u64 = 0;
+        let mut b: u64 = 0;
+        let r = unsafe {
+            rust_dbg_unpack_option_u64u64(some_u64u64, &mut a as *mut _, &mut b as *mut _)
+        };
+        assert_eq!(1, r);
+        assert_eq!(10, a);
+        assert_eq!(20, b);
+
+        let mut a: u64 = 0;
+        let mut b: u64 = 0;
+        let r = unsafe {
+            rust_dbg_unpack_option_u64u64(none_u64u64, &mut a as *mut _, &mut b as *mut _)
+        };
+        assert_eq!(0, r);
+        assert_eq!(0, a);
+        assert_eq!(0, b);
+    }
+
+    pub fn enum_passing_and_return() {
+        let some_u64 = unsafe { rust_dbg_new_some_u64(10) };
+        if let U8TaggedEnumOptionU64::Some(v) = some_u64 {
+            assert_eq!(10, v);
+        } else {
+            panic!("unexpected none");
+        }
+
+        let none_u64 = unsafe { rust_dbg_new_none_u64() };
+        if let U8TaggedEnumOptionU64::Some(_) = none_u64 {
+            panic!("unexpected some");
+        }
+
+        let mut target: u64 = 0;
+        let r = unsafe { rust_dbg_unpack_option_u64(some_u64, &mut target as *mut _) };
+        assert_eq!(1, r);
+        assert_eq!(10, target);
+
+        let mut target: u64 = 0;
+        let r = unsafe { rust_dbg_unpack_option_u64(none_u64, &mut target as *mut _) };
+        assert_eq!(0, r);
+        assert_eq!(0, target);
+    }
 }
 
 #[cfg(target_arch = "x86_64")]
@@ -359,6 +438,8 @@ fn main() {
     issue_28676();
     issue_62350();
     struct_return();
+    enum_passing_and_return_pair();
+    enum_passing_and_return();
 }
 
 #[cfg(not(target_arch = "x86_64"))]
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
index 1c70624e4a2..7eecd99dc01 100644
--- a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.rs
@@ -20,7 +20,7 @@ pub struct Middle {
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type TestMiddle = Middle;
-//~^ ERROR homogeneous_aggregate: Homogeneous
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous
 
 #[repr(C)]
 pub struct Final {
@@ -31,6 +31,6 @@ pub struct Final {
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type TestFinal = Final;
-//~^ ERROR homogeneous_aggregate: Homogeneous
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous
 
 fn main() { }
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
index 0d442606351..cd3fb5ca5ea 100644
--- a/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-c-struct.stderr
@@ -1,10 +1,10 @@
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:22:1
    |
 LL | pub type TestMiddle = Middle;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-c-struct.rs:33:1
    |
 LL | pub type TestFinal = Final;
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
index 4b429412aeb..ec2c9b70224 100644
--- a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.rs
@@ -52,22 +52,22 @@ pub struct WithEmptyRustEnum {
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type Test1 = BaseCase;
-//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type Test2 = WithPhantomData;
-//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type Test3 = WithEmptyRustStruct;
-//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type Test4 = WithTransitivelyEmptyRustStruct;
-//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
 
 #[rustc_layout(homogeneous_aggregate)]
 pub type Test5 = WithEmptyRustEnum;
-//~^ ERROR homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
 
 fn main() { }
diff --git a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
index be04ba3e7f6..ec2b08bf02d 100644
--- a/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
+++ b/src/test/ui/layout/homogeneous-aggr-zero-sized-repr-rust.stderr
@@ -1,28 +1,28 @@
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:54:1
    |
 LL | pub type Test1 = BaseCase;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:58:1
    |
 LL | pub type Test2 = WithPhantomData;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:62:1
    |
 LL | pub type Test3 = WithEmptyRustStruct;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:66:1
    |
 LL | pub type Test4 = WithTransitivelyEmptyRustStruct;
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/homogeneous-aggr-zero-sized-repr-rust.rs:70:1
    |
 LL | pub type Test5 = WithEmptyRustEnum;
diff --git a/src/test/ui/layout/zero-sized-array-union.rs b/src/test/ui/layout/zero-sized-array-union.rs
index 68b218249eb..1a662ba4467 100644
--- a/src/test/ui/layout/zero-sized-array-union.rs
+++ b/src/test/ui/layout/zero-sized-array-union.rs
@@ -57,7 +57,7 @@ struct Baz1 {
 
 #[rustc_layout(homogeneous_aggregate)]
 type TestBaz1 = Baz1;
-//~^ ERROR homogeneous_aggregate: Homogeneous
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous
 
 #[repr(C)]
 struct Baz2 {
@@ -68,7 +68,7 @@ struct Baz2 {
 
 #[rustc_layout(homogeneous_aggregate)]
 type TestBaz2 = Baz2;
-//~^ ERROR homogeneous_aggregate: Homogeneous
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous
 
 #[repr(C)]
 struct Baz3 {
@@ -79,7 +79,7 @@ struct Baz3 {
 
 #[rustc_layout(homogeneous_aggregate)]
 type TestBaz3 = Baz3;
-//~^ ERROR homogeneous_aggregate: Homogeneous
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous
 
 #[repr(C)]
 struct Baz4 {
@@ -90,6 +90,6 @@ struct Baz4 {
 
 #[rustc_layout(homogeneous_aggregate)]
 type TestBaz4 = Baz4;
-//~^ ERROR homogeneous_aggregate: Homogeneous
+//~^ ERROR homogeneous_aggregate: Ok(Homogeneous
 
 fn main() { }
diff --git a/src/test/ui/layout/zero-sized-array-union.stderr b/src/test/ui/layout/zero-sized-array-union.stderr
index 1bb31aaf7b7..43b1588266b 100644
--- a/src/test/ui/layout/zero-sized-array-union.stderr
+++ b/src/test/ui/layout/zero-sized-array-union.stderr
@@ -1,22 +1,22 @@
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/zero-sized-array-union.rs:59:1
    |
 LL | type TestBaz1 = Baz1;
    | ^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/zero-sized-array-union.rs:70:1
    |
 LL | type TestBaz2 = Baz2;
    | ^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/zero-sized-array-union.rs:81:1
    |
 LL | type TestBaz3 = Baz3;
    | ^^^^^^^^^^^^^^^^^^^^^
 
-error: homogeneous_aggregate: Homogeneous(Reg { kind: Float, size: Size { raw: 4 } })
+error: homogeneous_aggregate: Ok(Homogeneous(Reg { kind: Float, size: Size { raw: 4 } }))
   --> $DIR/zero-sized-array-union.rs:92:1
    |
 LL | type TestBaz4 = Baz4;
diff --git a/src/test/ui/specialization/soundness/partial_eq_range_inclusive.rs b/src/test/ui/specialization/soundness/partial_eq_range_inclusive.rs
new file mode 100644
index 00000000000..923dec892e0
--- /dev/null
+++ b/src/test/ui/specialization/soundness/partial_eq_range_inclusive.rs
@@ -0,0 +1,35 @@
+// run-pass
+
+use std::cell::RefCell;
+use std::cmp::Ordering;
+
+struct Evil<'a, 'b> {
+    values: RefCell<Vec<&'a str>>,
+    to_insert: &'b String,
+}
+
+impl<'a, 'b> PartialEq for Evil<'a, 'b> {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+impl<'a> PartialOrd for Evil<'a, 'a> {
+    fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
+        self.values.borrow_mut().push(self.to_insert);
+        None
+    }
+}
+
+fn main() {
+    let e;
+    let values;
+    {
+        let to_insert = String::from("Hello, world!");
+        e = Evil { values: RefCell::new(Vec::new()), to_insert: &to_insert };
+        let range = &e..=&e;
+        let _ = range == range;
+        values = e.values;
+    }
+    assert_eq!(*values.borrow(), Vec::<&str>::new());
+}
diff --git a/src/test/ui/specialization/soundness/partial_ord_slice.rs b/src/test/ui/specialization/soundness/partial_ord_slice.rs
new file mode 100644
index 00000000000..b9e80a48d33
--- /dev/null
+++ b/src/test/ui/specialization/soundness/partial_ord_slice.rs
@@ -0,0 +1,42 @@
+// Check that we aren't using unsound specialization in slice comparisons.
+
+// run-pass
+
+use std::cell::Cell;
+use std::cmp::Ordering;
+
+struct Evil<'a, 'b>(Cell<(&'a [i32], &'b [i32])>);
+
+impl PartialEq for Evil<'_, '_> {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+impl Eq for Evil<'_, '_> {}
+
+impl PartialOrd for Evil<'_, '_> {
+    fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
+        Some(Ordering::Equal)
+    }
+}
+
+impl<'a> Ord for Evil<'a, 'a> {
+    fn cmp(&self, _other: &Self) -> Ordering {
+        let (a, b) = self.0.get();
+        self.0.set((b, a));
+        Ordering::Equal
+    }
+}
+
+fn main() {
+    let x = &[1, 2, 3, 4];
+    let u = {
+        let a = Box::new([7, 8, 9, 10]);
+        let y = [Evil(Cell::new((x, &*a)))];
+        let _ = &y[..] <= &y[..];
+        let [Evil(c)] = y;
+        c.get().0
+    };
+    assert_eq!(u, &[1, 2, 3, 4]);
+}