AFAIU, Foo requires each of its subtypes to be a subtype of Bar
No it doesn't.
def test[T <: Foo]: Unit = {
  implicitly[T <:< Bar] // doesn't compile
}
I defined a subtype of Foo, namely T, which is not a subtype of Bar.
This is true for subclasses.
class Impl extends Foo with Bar
If a class extends Foo it must extend Bar too.
Types and classes are different. Subtypes and subclasses are different. Subtyping and inheritance are different.
In
trait Foo { self: Bar => }
Foo is not a subtype of Bar. So you can't assign value of type Foo to variable of type Bar
def x: Foo = ???
val y: Bar = x // doesn't compile
If you want to make Foo a subtype of Bar you should use inheritance
trait Foo extends Bar
def x: Foo = ???
val y: Bar = x // compiles
or subtyping
type Foo <: Bar
def x: Foo = ???
val y: Bar = x // compiles
For example with self-types you can define cyclic dependencies:
trait Bar { self: Foo => } 
trait Foo { self: Bar => } 
class Impl extends Foo with Bar
If trait A { self: B => } implied that A <: B then we would have in such case that Bar <: Foo and Foo <: Bar, so Bar =:= Foo but it's not true, types of those traits are different.
trait A { self: B => } could mean that A <: B (and in such case either we wouldn't have cyclic dependencies or such traits would have equal types) but there is no necessity in that: if you need A <: B you can just declare A extends B while trait A { self: B => } has a different meaning: all subclasses (not subtypes) of A are subtypes of B.
Or you can limit implementation
trait Foo { self: Impl => } 
class Impl extends Foo
(Impl can be the only implementation of trait Foo like making Foo sealed with the only inheritor) but there is no need to make types of Foo and Impl the same.
Let's consider also the following example
trait NatHelper { 
  //some helper methods 
} 
sealed trait Nat { self: NatHelper => 
  type Add[M <: Nat] <: Nat 
} 
object Zero extends Nat with NatHelper { 
  override type Add[M <: Nat] = M 
} 
class Succ[N <: Nat] extends Nat with NatHelper { 
  override type Add[M <: Nat] = Succ[N#Add[M]] 
}
Notice that the abstract type Nat#Add[M] is a subtype of Nat but there is no need to make it a subtype of NatHelper.
Types are not necessarily connected with runtime stuff. Types can have independent meaning. For example they can be used for type-level programming when you formulate your business logic in terms of types.
Also there are so called tagged types (or phantom types) when you attach some information to a type
val x: Int with Foo = 1.asInstanceOf[Int with Foo]
Here we attached "information" Foo to number 1. It's the same runtime number 1 but at compile time it's enriched with "information" Foo. Then x.isInstanceOf[Bar] gives false. I'm not sure you'll accept this example since we use asInstanceOf but the thing is that you can use some library function
val x: Int with Foo = 1.attach[Foo] 
and you will not know that it uses asInstanceOf under the hood (as often happens), you will just trust its signature that it returns Int with Foo.