Files
clang-p2996/clang/test/SemaCXX/deduced-return-void.cpp
Matheus Izvekov 989f76ce90 [clang] template / auto deduction deduces common sugar
After upgrading the type deduction machinery to retain type sugar in
D110216, we were left with a situation where there is no general
well behaved mechanism in Clang to unify the type sugar of multiple
deductions of the same type parameter.

So we ended up making an arbitrary choice: keep the sugar of the first
deduction, ignore subsequent ones.

In general, we already had this problem, but in a smaller scale.
The result of the conditional operator and many other binary ops
could benefit from such a mechanism.

This patch implements such a type sugar unification mechanism.

The basics:

This patch introduces a `getCommonSugaredType(QualType X, QualType Y)`
method to ASTContext which implements this functionality, and uses it
for unifying the results of type deduction and return type deduction.
This will return the most derived type sugar which occurs in both X and
Y.

Example:

Suppose we have these types:
```
using Animal = int;
using Cat = Animal;
using Dog = Animal;

using Tom = Cat;
using Spike = Dog;
using Tyke = Dog;
```
For `X = Tom, Y = Spike`, this will result in `Animal`.
For `X = Spike, Y = Tyke`, this will result in `Dog`.

How it works:

We take two types, X and Y, which we wish to unify as input.
These types must have the same (qualified or unqualified) canonical
type.

We dive down fast through top-level type sugar nodes, to the
underlying canonical node. If these canonical nodes differ, we
build a common one out of the two, unifying any sugar they had.
Note that this might involve a recursive call to unify any children
of those. We then return that canonical node, handling any qualifiers.

If they don't differ, we walk up the list of sugar type nodes we dived
through, finding the last identical pair, and returning that as the
result, again handling qualifiers.

Note that this patch will not unify sugar nodes if they are not
identical already. We will simply strip off top-level sugar nodes that
differ between X and Y. This sugar node unification will instead be
implemented in a subsequent patch.

This patch also implements a few users of this mechanism:
* Template argument deduction.
* Auto deduction, for functions returning auto / decltype(auto), with
  special handling for initializer_list as well.

Further users will be implemented in a subsequent patch.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>

Differential Revision: https://reviews.llvm.org/D111283
2022-09-16 11:20:10 +02:00

141 lines
5.0 KiB
C++

// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
// Check that we don't get any extra warning for "return" without an
// expression, in a function that might have been intended to return
// void all along.
decltype(h1) h1() { // expected-error {{use of undeclared identifier 'h1'}}
return;
}
namespace JustAuto {
int i;
auto f1() { }
auto f2() { return; }
auto f3() { return void(); }
auto f4() {
return i;
return; // expected-error {{'auto' in return type deduced as 'void' here but deduced as 'int' in earlier return statement}}
}
auto f5() {
return i;
return void(); // expected-error {{'auto' in return type deduced as 'void' here but deduced as 'int' in earlier return statement}}
}
auto l1 = []() { };
auto l2 = []() { return; };
auto l3 = []() { return void(); };
auto l4 = []() {
return i;
return; // expected-error {{return type 'void' must match previous return type 'int' when lambda expression has unspecified explicit return type}}
};
auto l5 = []() {
return i;
return void(); // expected-error {{return type 'void' must match previous return type 'int' when lambda expression has unspecified explicit return type}}
};
} // namespace JustAuto
namespace DecltypeAuto {
int i;
decltype(auto) f1() { }
decltype(auto) f2() { return; }
decltype(auto) f3() { return void(); }
decltype(auto) f4() {
return i;
return; // expected-error {{'decltype(auto)' in return type deduced as 'void' here but deduced as 'int' in earlier return statement}}
}
decltype(auto) f5() {
return i;
return void(); // expected-error {{'decltype(auto)' in return type deduced as 'void' here but deduced as 'int' in earlier return statement}}
}
auto l1 = []() -> decltype(auto) { };
auto l2 = []() -> decltype(auto) { return; };
auto l3 = []() -> decltype(auto) { return void(); };
auto l4 = []() -> decltype(auto) {
return i;
return; // expected-error {{'decltype(auto)' in return type deduced as 'void' here but deduced as 'int' in earlier return statement}}
};
auto l5 = []() -> decltype(auto) {
return i;
return void(); // expected-error {{'decltype(auto)' in return type deduced as 'void' here but deduced as 'int' in earlier return statement}}
};
} // namespace DecltypeAuto
namespace AutoPtr {
int i;
auto *f1() { } // expected-error {{cannot deduce return type 'auto *' for function with no return statements}}
auto *f2() {
return; // expected-error {{cannot deduce return type 'auto *' from omitted return expression}}
}
auto *f3() {
return void(); // expected-error {{cannot deduce return type 'auto *' from returned value of type 'void'}}
}
auto *f4() {
return &i;
return; // expected-error {{cannot deduce return type 'auto *' from omitted return expression}}
}
auto *f5() {
return &i;
return void(); // expected-error {{cannot deduce return type 'auto *' from returned value of type 'void'}}
}
auto l1 = []() -> auto* { }; // expected-error {{cannot deduce return type 'auto *' for function with no return statements}}
auto l2 = []() -> auto* {
return; // expected-error {{cannot deduce return type 'auto *' from omitted return expression}}
};
auto l3 = []() -> auto* {
return void(); // expected-error {{cannot deduce return type 'auto *' from returned value of type 'void'}}
};
auto l4 = []() -> auto* {
return &i;
return; // expected-error {{cannot deduce return type 'auto *' from omitted return expression}}
};
auto l5 = []() -> auto* {
return &i;
return void(); // expected-error {{cannot deduce return type 'auto *' from returned value of type 'void'}}
};
} // namespace AutoPtr
namespace AutoRef {
int i;
auto& f1() { // expected-error {{cannot deduce return type 'auto &' for function with no return statements}}
}
auto& f2() {
return; // expected-error {{cannot deduce return type 'auto &' from omitted return expression}}
}
auto& f3() {
return void(); // expected-error@-1 {{cannot form a reference to 'void'}}
}
auto& f4() {
return i;
return; // expected-error {{cannot deduce return type 'auto &' from omitted return expression}}
}
auto& f5() {
return i;
return void(); // expected-error {{deduced as 'int' in earlier return statement}}
}
auto& f6() { return 42; } // expected-error {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
auto l1 = []() -> auto& { }; // expected-error {{cannot deduce return type 'auto &' for function with no return statements}}
auto l2 = []() -> auto& {
return; // expected-error {{cannot deduce return type 'auto &' from omitted return expression}}
};
auto l3 = []() -> auto& { // expected-error {{cannot form a reference to 'void'}}
return void();
};
auto l4 = []() -> auto& {
return i;
return; // expected-error {{cannot deduce return type 'auto &' from omitted return expression}}
};
auto l5 = []() -> auto & {
return i;
return void(); // expected-error {{deduced as 'int' in earlier return statement}}
};
auto l6 = []() -> auto& {
return 42; // expected-error {{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}}
};
} // namespace AutoRef