Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trait aliases make it possible to sidestep various syntactic trait bound checks #135342

Closed
fmease opened this issue Jan 10, 2025 · 6 comments
Closed
Labels
C-bug Category: This is a bug. F-trait_alias `#![feature(trait_alias)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@fmease
Copy link
Member

fmease commented Jan 10, 2025

Similar to how type aliases allow you to circumvent some syntactic/syntax-driven1 checks made by AST lowering (#132212), you can utilize trait aliases to bypass certain restrictions applying to trait bounds.

#![feature(trait_alias)]

trait SuperMaybeNeg: ?Sized {} // 🔴 REJECTED: `?Trait` is not permitted in supertraits
trait SuperMaybePos: MaybeSized {} // 🟢 Workaround: ACCEPTED

type DynMaybeNeg = dyn std::io::Write + ?Sized; // 🔴 REJECTED: `?Trait` is not permitted in trait object types
type DynMaybePos = dyn std::io::Write + MaybeSized; // 🟢 Workaround: ACCEPTED

fn noop_maybe_neg() where i32: ?Sized {} // 🔴 REJECTED: `?Trait` bounds are only permitted at the point where a type parameter is declared
fn noop_maybe_pos() where i32: MaybeSized {} // 🟢 Workaround: ACCEPTED


trait MaybeSized = ?Sized;
#![feature(trait_alias, const_trait_impl)]

const fn incompat_modifs_neg<T: const ?Trait>() {} // 🔴 REJECTED: const` trait not allowed with `?` trait polarity modifier
const fn incompat_modifs_pos<T: ?ConstTrait>() {} // 🟢 Workaround: ACCEPTED


trait ConstTrait = const Trait; // alternatively, `async Fn()` under feat `async_trait_bounds`
#[const_trait] trait Trait {}
#![feature(trait_alias)]

type DupeAssocNeg = dyn std::ops::Deref<Target = String, Target = i32>; // 🔴 REJECTED: the value of the associated type `Target` in trait `Deref` is already specified
type DupeAssocPos = dyn AssocFixed<Target = i32>; // 🟢 Workaround: ACCEPTED
//^ This bypasses a HIR ty lowering check, not an AST validation one.

type DynAtbNeg = dyn std::ops::Deref<Target: Copy>; // 🔴 REJECTED: associated type bounds are not allowed in `dyn` types
type DynAtbPosIsh<T> = dyn AssocBounded<Target = T>; // 🟢 Workaround-ish: ACCEPTED


trait AssocFixed = std::ops::Deref<Target = String>;
trait AssocBounded = std::ops::Deref<Target: Copy>;
#![feature(trait_alias, return_type_notation)]

struct DynRtnNeg(dyn Trait<method(..): Copy>); // 🔴 REJECTED: associated type bounds are not allowed in `dyn` types
                                               //              return type notation is not allowed to use type equality
struct DynRtnPos(dyn Rtn); // 🟢 Workaround: ACCEPTED

trait Trait { fn method(&self) -> impl Sized where Self: Sized; }
trait Rtn = Trait<method(..): Copy>;

This either calls into question the very existence of these restrictions or it demonstrates that all of these checks ought to run "later" (i.e., after trait alias expansion).

Footnotes

  1. I.e., checks on the AST or HIR without any sort of prior substitution/expansion/normalization/...

@fmease fmease added C-bug Category: This is a bug. F-trait_alias `#![feature(trait_alias)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jan 10, 2025
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jan 10, 2025
@fmease fmease added T-types Relevant to the types team, which will review and decide on the PR/issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Jan 10, 2025
@fmease fmease changed the title Trait aliases make it possible to sidestep several syntactic trait bound checks Trait aliases make it possible to sidestep various syntactic trait bound checks Jan 10, 2025
@compiler-errors
Copy link
Member

I'm not totally convinced all of these are bugs. For the first one, That MaybeSized one just seems like it needs to exist since we want to have a placeholder for "no trait bounds" -- think impl ?Sized for opaques.

For the second one, maybe we should just make ?Trait a hard error for trait aliases? Or else, I think if we envision trait X = Y as trait X: Y {} + impl X for T where T: Y {}, it's not exactly the same as writing ?const.

Third one seems like a disagreement about what we consider to be WF when it comes to dyn trait aliases. This will remain a bug until we stop doing that sketchy eager trait alias expansion behavior in dyn.

Same thing with the fourth one.

@fmease
Copy link
Member Author

fmease commented Jan 12, 2025

I guess I need to shift my perspective, thanks for the "reality check". Your explanation does make sense yeah.

Do we track the issues concerning trait aliases inside trait object types anywhere? In an issue or in one of your PRs?

@compiler-errors
Copy link
Member

compiler-errors commented Jan 13, 2025

Do we track the issues concerning trait aliases inside trait object types anywhere? In an issue or in one of your PRs?

Not currently, though it would be useful if you opened open an issue for those specifically since I've wanted to point to them / may want to link to it if I ever get around to opening a PR to remove the current dyn TraitAlias lowering strategy.

@theemathas
Copy link
Contributor

On the other hand, ?Sized doesn't work for trait aliases when it probably should be.

#![feature(trait_alias)]

trait Lol = Sized;

fn foo<T: ?Lol>() {}

fn main() {
    foo::<str>();
}
warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default
 --> src/main.rs:5:11
  |
5 | fn foo<T: ?Lol>() {}
  |           ^^^^

error[E0277]: the size for values of type `str` cannot be known at compilation time
 --> src/main.rs:8:11
  |
8 |     foo::<str>();
  |           ^^^ doesn't have a size known at compile-time
  |
  = help: the trait `Sized` is not implemented for `str`
note: required by an implicit `Sized` bound in `foo`
 --> src/main.rs:5:8
  |
5 | fn foo<T: ?Lol>() {}
  |        ^ required by the implicit `Sized` requirement on this type parameter in `foo`
help: consider relaxing the implicit `Sized` restriction
  |
5 | fn foo<T: ?Lol + ?Sized>() {}
  |                ++++++++

For more information about this error, try `rustc --explain E0277`.

See also #135809

@compiler-errors
Copy link
Member

I think trait Foo = Sized; ?Foo is nonsense tbh, for the same reason I said above:

I think if we envision trait X = Y as trait X: Y {} + impl X for T where T: Y {}, it's not exactly the same as writing ?const.

You're writing an automatically-implemented subtrait of Sized, not an ast-level substitution for Sized.

@fmease
Copy link
Member Author

fmease commented Feb 11, 2025

Do we track the issues concerning trait aliases inside trait object types anywhere? In an issue or in one of your PRs?

Not currently, though it would be useful if you opened open an issue for those specifically since I've wanted to point to them / may want to link to it if I ever get around to opening a PR to remove the current dyn TraitAlias lowering strategy.

I guess I won't create a follow-up issue since your FCP-merge PR seems to take care of it (e.g., tests/ui/associated-types/associated-types-overridden-binding.rs)

@fmease fmease closed this as not planned Won't fix, can't repro, duplicate, stale Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. F-trait_alias `#![feature(trait_alias)]` requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants