I think I get what you mean and I may have missed or misunderstood that piece of information. With that established, here's the wrinkle in the semantics I had in mind. Now, let me know if I still got it wrong, I think we use different terminology sometimes.
If we have this case:
fn do_something() {
// Process items
}
fn example() {
do_something()
}
This is allowed with both mandatory and inferred semicolons. It's the last expression but the return types match. The compiler doesn't complain.
If do_something is later changed to return a value to report back what it did, for example the number of processed items:
fn do_something() -> usize {
// Process items
processed_item_count // Some callers want this, some don't care
}
fn example() {
do_something() // Oops!
}
Now, this also errors in both versions (right?), but the implication is slightly different.
With mandatory semicolons, this is a clear mistake in example, as far as the compiler can tell. There wouldn't have been an error if the author didn't forget to add the semicolon to say that nothing should be returned. Clear as day, according to those rules.
With inferred semicolons, skipping the semicolon doesn't necessarily mean anything specific. It was left out because it's easier to not add it and it may look nicer. The return types matched before so all was good. However, now when they don't match, the author has to go and add a semicolon to "opt out" from returning, even if no mistake was initially made.
This is the scenario where I meant that one may eventually start to add a "defensive" semicolons at the end, regardless of whether it's needed or not. This may be a necessary and acceptable cost, depending who you ask.
Comparing to other languages, if you also would make the return keyword mandatory, we would have a new signal of intent and it would circumvent this specific situation. Nested blocks could still have more wrinkles, but they would likely also be more convoluted.