think that, like traditional C++, we'd also only get errors upon post-resolution instantiations of operators on such types today, though we're pursuing an interface concept that, like C++, would aim to do the type checking more proactively. But it would still require an explicit instantiation at some point to get right.
It's not that hard to actually do basic parametric polymorphism. Then there are two ways to apply constraints: a kinding system, and type classes. Felix does both. C++ concepts should have been Haskell type classes.
I think your biggest problem is actually notation, i.e. grammar. At least to start you don't want to break the existing system of generics, even if they're flawed. For functions, I'd be starting with purely parametric polymorphism which means NO operations are allowed on values dependent on a type parameter unless the operation is passed in as an argument except for moving, copying, and assigning etc.
In fact even that extreme limitation is not strong enough ... because you also have substructural typing, that is, uniqueness types, and borrowing. Consider this (excuse Felix notation please):
fun diag[T] (x:T)=> x,x;
This should type check, but what happens if you pass a uniquely typed entity? They cannot be copied, only moved. So actually in Felix, the default kind for a type variable is TYPE
which means sharable types and excludes unique types. Here:
fun swap[T:LINEARTYPE, U:LINEARTYPE] (xT,y:U) => y,x;
works for any type, since it only need to move values. The kind LINEARTYPE
must be specified to allow the function to accept unique types.
That was all fine .. until I added the kind BORROWED
to the system. 
Now I have to check that you cannot pass borrowed values to this function because borrowed values must be forgotten, and both diag and swap return the values instead of forgetting them. The need to do borrowing was always a problem, and in fact reading the Chapel docs inspired me to figure out how to do it.
Chapel already has a lot of the capabilities to do things properly in the compiler. It's actually the surface syntax that's more of a problem .. in most languages if you can get the syntax right implementing it isn't too hard.