summaryrefslogtreecommitdiffstats
path: root/tests/ui/implied-bounds
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:03 +0000
commit64d98f8ee037282c35007b64c2649055c56af1db (patch)
tree5492bcf97fce41ee1c0b1cc2add283f3e66cdab0 /tests/ui/implied-bounds
parentAdding debian version 1.67.1+dfsg1-1. (diff)
downloadrustc-64d98f8ee037282c35007b64c2649055c56af1db.tar.xz
rustc-64d98f8ee037282c35007b64c2649055c56af1db.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/ui/implied-bounds')
-rw-r--r--tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs27
-rw-r--r--tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr15
-rw-r--r--tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.rs61
-rw-r--r--tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.stderr30
-rw-r--r--tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs35
-rw-r--r--tests/ui/implied-bounds/impl-header-unnormalized-types.rs28
-rw-r--r--tests/ui/implied-bounds/impl-header-unnormalized-types.stderr20
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs22
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr31
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs21
-rw-r--r--tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr31
-rw-r--r--tests/ui/implied-bounds/issue-100690.rs45
-rw-r--r--tests/ui/implied-bounds/issue-100690.stderr22
-rw-r--r--tests/ui/implied-bounds/issue-101951.rs50
14 files changed, 438 insertions, 0 deletions
diff --git a/tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs b/tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs
new file mode 100644
index 000000000..33b746c5e
--- /dev/null
+++ b/tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.rs
@@ -0,0 +1,27 @@
+// Test for a less than ideal interaction of implied bounds and normalization.
+trait Tr {
+ type Ty;
+}
+
+impl<T: 'static> Tr for T {
+ type Ty = &'static T;
+}
+
+// `<&'a u8 as Tr>::Ty` should cause an error because `&'a u8: Tr` doesn't hold for
+// all possible 'a. However, we consider normalized types for implied bounds.
+//
+// We normalize this projection to `&'static &'a u8` and add a nested `&'a u8: 'static`
+// bound. This bound is then proven using the implied bounds for `&'static &'a u8` which
+// we only get by normalizing in the first place.
+fn test<'a>(x: &'a u8, _wf: <&'a u8 as Tr>::Ty) -> &'static u8 { x }
+
+fn main() {
+ // This works as we have 'static references due to promotion.
+ let _: &'static u8 = test(&3, &&3);
+ // This causes an error because the projection requires 'a to be 'static.
+ // It would be unsound if this compiled.
+ let x: u8 = 3;
+ let _: &'static u8 = test(&x, &&3);
+ //~^ ERROR `x` does not live long enough
+
+}
diff --git a/tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr b/tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr
new file mode 100644
index 000000000..d0249e74f
--- /dev/null
+++ b/tests/ui/implied-bounds/assoc-ty-wf-used-to-get-assoc-ty.stderr
@@ -0,0 +1,15 @@
+error[E0597]: `x` does not live long enough
+ --> $DIR/assoc-ty-wf-used-to-get-assoc-ty.rs:24:31
+ |
+LL | let _: &'static u8 = test(&x, &&3);
+ | -----^^------
+ | | |
+ | | borrowed value does not live long enough
+ | argument requires that `x` is borrowed for `'static`
+...
+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/tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.rs b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.rs
new file mode 100644
index 000000000..c177655c5
--- /dev/null
+++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.rs
@@ -0,0 +1,61 @@
+// A test exploiting the bug behind #25860 except with
+// implied trait bounds which currently don't exist without `-Ztrait-solver=chalk`.
+use std::marker::PhantomData;
+struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+where
+ T: Convert<'a, 'b>;
+
+trait Convert<'a, 'b>: Sized {
+ fn cast(&'a self) -> &'b Self;
+}
+impl<'long: 'short, 'short, T> Convert<'long, 'short> for T {
+ fn cast(&'long self) -> &'short T {
+ self
+ }
+}
+
+// This function will compile once we add implied trait bounds.
+//
+// If we're not careful with our impl, the transformations
+// in `bad` would succeed, which is unsound ✨
+//
+// FIXME: the error is pretty bad, this should say
+//
+// `T: Convert<'in_, 'out>` is not implemented
+//
+// help: needed by `Foo<'in_, 'out, T>`
+//
+// Please ping @lcnr if your changes end up causing `badboi` to compile.
+fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ T) -> &'out T {
+ //~^ ERROR lifetime mismatch
+ sadness.cast()
+}
+
+fn badboi2<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ T) {
+ //~^ ERROR lifetime mismatch
+ let _: &'out T = sadness.cast();
+}
+
+fn badboi3<'in_, 'out, T>(a: Foo<'in_, 'out, (&'in_ T, &'out T)>, sadness: &'in_ T) {
+ //~^ ERROR lifetime mismatch
+ let _: &'out T = sadness.cast();
+}
+
+fn bad<'short, T>(value: &'short T) -> &'static T {
+ let x: for<'in_, 'out> fn(Foo<'in_, 'out, T>, &'in_ T) -> &'out T = badboi;
+ let x: for<'out> fn(Foo<'short, 'out, T>, &'short T) -> &'out T = x;
+ let x: for<'out> fn(Foo<'static, 'out, T>, &'short T) -> &'out T = x;
+ let x: fn(Foo<'static, 'static, T>, &'short T) -> &'static T = x;
+ x(Foo(PhantomData), value)
+}
+
+// Use `bad` to cause a segfault.
+fn main() {
+ let mut outer: Option<&'static u32> = Some(&3);
+ let static_ref: &'static &'static u32 = match outer {
+ Some(ref reference) => bad(reference),
+ None => unreachable!(),
+ };
+ outer = None;
+ println!("{}", static_ref);
+}
diff --git a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.stderr b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.stderr
new file mode 100644
index 000000000..0c00bbc38
--- /dev/null
+++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-guard.stderr
@@ -0,0 +1,30 @@
+error[E0623]: lifetime mismatch
+ --> $DIR/hrlt-implied-trait-bounds-guard.rs:29:29
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ T) -> &'out T {
+ | ^^^^^^^^^^^^^^^^^^ -------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+ | ...but data from `x` is returned here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/hrlt-implied-trait-bounds-guard.rs:34:30
+ |
+LL | fn badboi2<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ T) {
+ | ^^^^^^^^^^^^^^^^^^
+ | |
+ | this type is declared with multiple lifetimes...
+ | ...but data with one lifetime flows into the other here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/hrlt-implied-trait-bounds-guard.rs:39:30
+ |
+LL | fn badboi3<'in_, 'out, T>(a: Foo<'in_, 'out, (&'in_ T, &'out T)>, sadness: &'in_ T) {
+ | ^^^^^^^^^^^^^^^^^-------^^-------^^
+ | | |
+ | | these two types are declared with different lifetimes...
+ | ...but data from `a` flows into `a` here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0623`.
diff --git a/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs
new file mode 100644
index 000000000..69847d6a8
--- /dev/null
+++ b/tests/ui/implied-bounds/hrlt-implied-trait-bounds-roundtrip.rs
@@ -0,0 +1,35 @@
+// check-pass
+struct Foo<'a>(&'a ())
+where
+ (): Trait<'a>;
+
+trait Trait<'a> {
+ fn id<T>(value: &'a T) -> &'static T;
+}
+
+impl Trait<'static> for () {
+ fn id<T>(value: &'static T) -> &'static T {
+ value
+ }
+}
+
+fn could_use_implied_bounds<'a, T>(_: Foo<'a>, x: &'a T) -> &'static T
+where
+ (): Trait<'a>, // This could be an implied bound
+{
+ <()>::id(x)
+}
+
+fn main() {
+ let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {};
+
+ // If `could_use_implied_bounds` were to use implied bounds,
+ // keeping 'a late-bound, then we could assign that function
+ // to this variable.
+ let bar: for<'a> fn(Foo<'a>, &'a ()) = bar;
+
+ // In this case, the subtyping relation here would be unsound,
+ // allowing us to transmute lifetimes. This currently compiles
+ // because we incorrectly deal with implied bounds inside of binders.
+ let _bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = bar;
+}
diff --git a/tests/ui/implied-bounds/impl-header-unnormalized-types.rs b/tests/ui/implied-bounds/impl-header-unnormalized-types.rs
new file mode 100644
index 000000000..d84539f8a
--- /dev/null
+++ b/tests/ui/implied-bounds/impl-header-unnormalized-types.rs
@@ -0,0 +1,28 @@
+struct Foo<T>(T);
+
+trait GoodBye {
+ type Forget;
+}
+impl<T> GoodBye for T {
+ type Forget = ();
+}
+
+trait NeedsWf<'a, 'b> {
+ type Assoc;
+}
+
+impl<'a, 'b> NeedsWf<'a, 'b> for Foo<<&'a &'b () as GoodBye>::Forget> {
+ type Assoc = &'a &'b ();
+ //~^ ERROR in type `&'a &'b ()`, reference has a longer lifetime than the data it references
+}
+
+fn needs_wf<'a, 'b, T: NeedsWf<'a, 'b>>() {}
+
+fn foo<'a: 'a, 'b: 'b>(_: &'b String) {
+ needs_wf::<'a, 'b, Foo<()>>();
+}
+
+fn main() {
+ let x = String::from("hello");
+ foo::<'static, '_>(&x);
+}
diff --git a/tests/ui/implied-bounds/impl-header-unnormalized-types.stderr b/tests/ui/implied-bounds/impl-header-unnormalized-types.stderr
new file mode 100644
index 000000000..88abd5f54
--- /dev/null
+++ b/tests/ui/implied-bounds/impl-header-unnormalized-types.stderr
@@ -0,0 +1,20 @@
+error[E0491]: in type `&'a &'b ()`, reference has a longer lifetime than the data it references
+ --> $DIR/impl-header-unnormalized-types.rs:15:18
+ |
+LL | type Assoc = &'a &'b ();
+ | ^^^^^^^^^^
+ |
+note: the pointer is valid for the lifetime `'a` as defined here
+ --> $DIR/impl-header-unnormalized-types.rs:14:6
+ |
+LL | impl<'a, 'b> NeedsWf<'a, 'b> for Foo<<&'a &'b () as GoodBye>::Forget> {
+ | ^^
+note: but the referenced data is only valid for the lifetime `'b` as defined here
+ --> $DIR/impl-header-unnormalized-types.rs:14:10
+ |
+LL | impl<'a, 'b> NeedsWf<'a, 'b> for Foo<<&'a &'b () as GoodBye>::Forget> {
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0491`.
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs
new file mode 100644
index 000000000..6ccbb5bb2
--- /dev/null
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.rs
@@ -0,0 +1,22 @@
+#![deny(implied_bounds_entailment)]
+
+trait Project {
+ type Ty;
+}
+impl Project for &'_ &'_ () {
+ type Ty = ();
+}
+trait Trait {
+ fn get<'s>(s: &'s str, _: ()) -> &'static str;
+}
+impl Trait for () {
+ fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+ //~^ ERROR impl method assumes more implied bounds than the corresponding trait method
+ //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ s
+ }
+}
+fn main() {
+ let val = <() as Trait>::get(&String::from("blah blah blah"), ());
+ println!("{}", val);
+}
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr
new file mode 100644
index 000000000..ebe07027d
--- /dev/null
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility-unnormalized.stderr
@@ -0,0 +1,31 @@
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
+ |
+LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+Future incompatibility report: Future breakage diagnostic:
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
+ |
+LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs
new file mode 100644
index 000000000..d097bc16a
--- /dev/null
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.rs
@@ -0,0 +1,21 @@
+#![deny(implied_bounds_entailment)]
+
+use std::cell::RefCell;
+
+pub struct MessageListeners<'a> {
+ listeners: RefCell<Vec<Box<dyn FnMut(()) + 'a>>>,
+}
+
+pub trait MessageListenersInterface {
+ fn listeners<'c>(&'c self) -> &'c MessageListeners<'c>;
+}
+
+impl<'a> MessageListenersInterface for MessageListeners<'a> {
+ fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+ //~^ ERROR impl method assumes more implied bounds than the corresponding trait method
+ //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ self
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr
new file mode 100644
index 000000000..43d3e058f
--- /dev/null
+++ b/tests/ui/implied-bounds/impl-implied-bounds-compatibility.stderr
@@ -0,0 +1,31 @@
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility.rs:14:35
+ |
+LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+Future incompatibility report: Future breakage diagnostic:
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility.rs:14:35
+ |
+LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/tests/ui/implied-bounds/issue-100690.rs b/tests/ui/implied-bounds/issue-100690.rs
new file mode 100644
index 000000000..5599cd410
--- /dev/null
+++ b/tests/ui/implied-bounds/issue-100690.rs
@@ -0,0 +1,45 @@
+// This code (probably) _should_ compile, but it currently does not because we
+// are not smart enough about implied bounds.
+
+use std::io;
+
+fn real_dispatch<T, F>(f: F) -> Result<(), io::Error>
+//~^ NOTE required by a bound in this
+where
+ F: FnOnce(&mut UIView<T>) -> Result<(), io::Error> + Send + 'static,
+ //~^ NOTE required by this bound in `real_dispatch`
+ //~| NOTE required by a bound in `real_dispatch`
+{
+ todo!()
+}
+
+#[derive(Debug)]
+struct UIView<'a, T: 'a> {
+ _phantom: std::marker::PhantomData<&'a mut T>,
+}
+
+trait Handle<'a, T: 'a, V, R> {
+ fn dispatch<F>(&self, f: F) -> Result<(), io::Error>
+ where
+ F: FnOnce(&mut V) -> R + Send + 'static;
+}
+
+#[derive(Debug, Clone)]
+struct TUIHandle<T> {
+ _phantom: std::marker::PhantomData<T>,
+}
+
+impl<'a, T: 'a> Handle<'a, T, UIView<'a, T>, Result<(), io::Error>> for TUIHandle<T> {
+ fn dispatch<F>(&self, f: F) -> Result<(), io::Error>
+ where
+ F: FnOnce(&mut UIView<'a, T>) -> Result<(), io::Error> + Send + 'static,
+ {
+ real_dispatch(f)
+ //~^ ERROR expected a `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F`
+ //~| NOTE expected an `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F`
+ //~| NOTE expected a closure with arguments
+ //~| NOTE required by a bound introduced by this call
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/implied-bounds/issue-100690.stderr b/tests/ui/implied-bounds/issue-100690.stderr
new file mode 100644
index 000000000..3f6af70d8
--- /dev/null
+++ b/tests/ui/implied-bounds/issue-100690.stderr
@@ -0,0 +1,22 @@
+error[E0277]: expected a `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F`
+ --> $DIR/issue-100690.rs:37:23
+ |
+LL | real_dispatch(f)
+ | ------------- ^ expected an `FnOnce<(&mut UIView<'_, T>,)>` closure, found `F`
+ | |
+ | required by a bound introduced by this call
+ |
+ = note: expected a closure with arguments `(&mut UIView<'a, T>,)`
+ found a closure with arguments `(&mut UIView<'_, T>,)`
+note: required by a bound in `real_dispatch`
+ --> $DIR/issue-100690.rs:9:8
+ |
+LL | fn real_dispatch<T, F>(f: F) -> Result<(), io::Error>
+ | ------------- required by a bound in this
+...
+LL | F: FnOnce(&mut UIView<T>) -> Result<(), io::Error> + Send + 'static,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `real_dispatch`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/implied-bounds/issue-101951.rs b/tests/ui/implied-bounds/issue-101951.rs
new file mode 100644
index 000000000..108fef8a1
--- /dev/null
+++ b/tests/ui/implied-bounds/issue-101951.rs
@@ -0,0 +1,50 @@
+// Taken directly from that issue.
+//
+// This test detected that we didn't correctly resolve
+// inference variables when computing implied bounds.
+//
+// check-pass
+pub trait BuilderFn<'a> {
+ type Output;
+}
+
+impl<'a, F, Out> BuilderFn<'a> for F
+where
+ F: FnOnce(&'a mut ()) -> Out,
+{
+ type Output = Out;
+}
+
+pub trait ConstructionFirm {
+ type Builder: for<'a> BuilderFn<'a>;
+}
+
+pub trait Campus<T>
+where
+ T: ConstructionFirm,
+{
+ fn add_building(
+ &mut self,
+ building: &mut <<T as ConstructionFirm>::Builder as BuilderFn<'_>>::Output,
+ );
+}
+
+struct ArchitectsInc {}
+
+impl ConstructionFirm for ArchitectsInc {
+ type Builder = fn(&mut ()) -> PrettyCondo<'_>;
+}
+
+struct PrettyCondo<'a> {
+ _marker: &'a mut (),
+}
+
+struct CondoEstate {}
+
+impl Campus<ArchitectsInc> for CondoEstate {
+ fn add_building(&mut self, _building: &mut PrettyCondo<'_>) {
+ todo!()
+ }
+}
+
+fn main() {}