Indeed, that is just a guess on my part, trying to make sense of as much of the situation as possible.
Based on that example it seems (mildly annoyingly) that the nullability of the resulting column depends on how the COALESCE statement ends up being rewritten. With the NOT NULL column as the first argument, when it is treated as a CASE WHEN y IS NOT NULL THEN y ELSE x END, the resulting column is NULLable.
When the NOT NULL column y is replaced with the constant 2, the whole CASE structure disappears, replaced by the the constant 2. That makes good sense, but it is a tad odd that COALESCE with the initial argument as a constant is treated as NOT NULL, while COALESCE with a constant anywhere else in the argument list, which can also never be NULL, is treated as NULLable.
The only real difference is that the version with the leading constant gets rewritten as just that constant, while the others retain the CASE/IIF structure.
So, maybe it's all less a set of rules about COALESCE and more about the rules for what is in the resulting rewrite. For the CASE WHEN...ELSE expression where all potential outputs are NOT NULL, the resulting column is NOT NULL, while otherwise it's NULLable.
If it gets rewritten as a constant, then well, it's of course NOT NULL.
A bit strange indeed that it seems to depend not on the function itself but how it gets rewritten. Alternatively, it could be expressed as "COALESCE either with a constant as the first argument or with all NOT NULL arguments is evaluated as NOT NULL. Otherwise it is evaluated as NULLable."
As before, just some semi-educated guessing based on a few tests. Who knows what's really going on over at MS 🙂