А если "не уступает С кратно", то там не свободная типизация, а ... паллиатив :). Рассмотрим пример:
var = 2 + 2
....
var = "трам-тара-рам"
Компилятор видит (в процессе компиляции!), что результат 2 + 2 имеет тип "int", поскольку результат 2+2 имеет такой тип. А потому компилирует код:
{
int var = 2 + 2;
....
На самом деле операции с константами компилятор должен вычислять сам, то тут оно только для иллюстрации - вместо двоек могут быть иные переменные типа int.
Дойдя до присвоения строки, у которой другой тип, компилятор фигурную скобку закроет, что вызовет гибель "int var", и с новой скобки заведет другую переменную с тем же именем var, но на этот раз уже строкового типа. Получится код:
{
int var = 2 + 2;
....
}
{
char* var = "трам-тара-рам";
....
}
В нем свободной типизации нет, в есть подмена типов, осуществляемая во время компиляции, когда код линеен, и есть возможность отследить, в какой области объект имеет один тип, а в какой другой. Причем при переходе из одной такой области в другую попросту заводится новый объект с тем же именем, а старый разрушается/забывается.
При таком подходе код генерируется, как у C, но и негатива у этого метода тоже много - например, нельзя входить вовнутрь таких скобок снаружи.
Или, скажем, такая конструкция окажется компилятору не по зубам:
if( условие)
var = 2 + 2
else ....
var = "трам-тара-рам"
....
// а тут что-то с var делаем
....
}