Reverse the default for typename. Currently some_class<T>::thing is assumed to be an expression where 'thing' is a variable, when we don't know which template pattern to use because there may be an explicit specialization on the T that the user chooses. Hence, we have to say "typename std::vector<T>::iterator it;" instead of just saying "std::vector<T>::iterator it;". Instead, reverse that and assume it's a type by default unless shown that it's an expression. You'll need a new keyword for that, replacing "typename".
Remove the promotion-to-int rules. Currently in C (and in C++)
unsigned short test(unsigned short a, unsigned short b, unsigned short c) {
unsigned short x = a * b * c;
return x;
}
can have UB as signed integer overflow because any math done on an object smaller than int gets promoted to int. (No, you can't fix this with "(((unsigned short)x) * ((unsigned short)y))" the promotion happens on 's LHS and RHS, if those have types smaller than int.) Beyond this, people seem to expect that the type of the variable declaration will appertains to the calculation on the right, but it doesn't. For instance people seem to think "float f = a + b;" can't overflow where 'a' and 'b' are ints, because the assignment is going into a float.I haven't thought this idea through completely yet. Extend pointer types to include a static allocation identity as part of the type. Address-of local variable or global variable should produce one of these pointers. A "static allocation identity" is a special-typed zero-size variable, so you can stick it in code or as a class member. You could have pointers that were guaranteed to be allocated by THIS allocation point, instead of pointing to every possible T in the program. I'll fake up a syntax, "tree_node ^ tree::node_alloc ". It's known not to alias any other TreeNode the program might have, it has to be attached to the allocation point owned by that specific "node_alloc" in that object. (Let me phrase it differently. A tree in C or C++ has pointers which can point anywhere as long as it's another tree node type. That could be pointing to a different tree, it could be a self-pointer, it could be pointing up the tree, and so on. If your tree_node class has an allocation root, you can say that the pointers are things allocated through this allocation root. They can not outlive the allocation root. They are distinct from the things allocated by other allocation roots, which are the same tree_node types, but different tree_node objects. The node's list of children is std::vector<std::unique_ptr<tree_node ^ node_alloc >> so it clearly only holds pointers it allocated itself.)
There's another problem with pointer related to the above. Some code I saw used a "T &get_or_default<K, V>(Container &c, K key, V &default);" and the problem was that people would call it with a temporary for the default, like "Value &x = get_or_default(mymap, key, Value());" and they'd be holding a dangling reference. If you could make that an error, that'd be great. Maybe we use a trick like the "allocation root" above and treat pointers or references to temporaries have different type from the local variable. Then get_or_default takes and returns a reference-to-temporary and attempting to assign that to a reference in a variable declaration fails. Unlike the previous "allocation root" idea where you indicate the only thing you accept, this would be a case where you accept all allocation roots except one, the "temporaries" allocation root.
As far as I know, no compiler takes advantage of the freedom of the order of operations except in the most trivial ways. Everyone knows that in "f() g() + h()" that * must happen before +, but people think this means that f() and g() must happen before h(). No, they may happen in any order at all. I had to fix a lot of code that did "Print(stream.read(), stream.size())" where "read" updates the pointer and leaves size == 0: gcc ran stream.size() first and clang ran stream.read() first, setting the subsequent size to zero. Similar issue with "expr1() = expr2();" expressions.
Extend switch() and case to work on objects with any operator== defined. Add a statement for fallthrough and default to 'break;' before the start of the next case-label. Give each case label its own scope so I can declare variables in there without adding my own curly-braces. (Bonus 1 can you design a way to ensure that case labels are not overlapping? May require something other than operator==. Bonus 2 can you allow cases to be structured binding matches, similar to Rust?)
Speaking of structured binding, it's great but doesn't allow nesting. This
std::vector<std::unordered_map<std::string, std::pair<int, int>>> v;
for (auto [name, [lhsid, rhsid]] : v) {
is code I actually wanted to write in the past week yet that's a syntax error.Add the ability to declare object inheritance ("class Derived : Base;") so that I can cast between them before writing out the body of the derived class. Also allow me to write out the entire class tree with no possibility for extension in another translation unit. The "final" keyword states that a class may not be derived from, but I usually have a Base class which does have subclasses, but a known list of subclasses that will never grow without recompiling the whole project. Currently the compiler has to assume I could write a new subclass and compile it into a shared object that the existing program dlopen's and the existing program will work. It's crazy. No, I have the final tree not just some leaf classes, please devirtualize the whole thing for me.
Are ABI changes on the table? Explicit template instantiations and explicit specializations should mangle differently. See my comment elsewhere: https://github.com/dealii/dealii/issues/3705#issuecomment-11...
If I think of some more, I'll reply to myself.