Most programming languages are classified as being statically typed or dynamically typed. I think of static and dynamic typing as features (which can be used together in a single language, if desired), and weak typing as lack of either.
Static typing is a feature where the compiler checks for type mismatch-related errors.
Dynamic typing is a feature where the compiled program checks types. The compiler is involved only in generating executable code that does the check at runtime. In this scenario, trying to call aPig.Quack() compiles, but throws an exception at runtime.
Weak typing is lack of type checking--e.g., calling aPig.Quack() will run with undefined behavior.
In this view, polymorphism is a dynamic typing feature that works well with statically-typed languages: The compiler may ensure that all aDuck really is a Duck, but the runtime environment will check to see what kind of Duck aDuck is--Mallard, WoodenDecoy, or something else--to know which implementation of the virtual method Quack() to call. Hence we have an example of static and dynamic typing working together.