1) After rewriting your code as follows:
case class Monoid[A](m0: A) // We only care about the zero here
implicit def s[T] : Monoid[Set[T]] = Monoid(Set.empty[T])
implicit def l[T] : Monoid[List[T]] = Monoid(List.empty[T])
def mzero[A]()(implicit m: Monoid[A]) : A = m.m0
val zero = mzero[List[Int]]()
val zero2: List[Int] = mzero()
then becomes clearly why that works so.
2) After you had to set mzero as def mzero[A]()(implicit m: Monoid[_ <: A]) : A = m.m0 then you enabled additional type inference to resolve existencial type. Compiler got actual type from required return type.
You could check it with def mzero[A <: B, B]()(implicit m: Monoid[A]) : A = m.m0 if you want.
3) Of course all that behaviour is just subtleties of compiler and I not think that such partial cases really required deep understanding.