Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #425: Show the correct reference type for structured bindings. #451

Merged
merged 1 commit into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3895,7 +3895,6 @@ void CodeGenerator::InsertArg(const BindingDecl*)

void StructuredBindingsCodeGenerator::InsertArg(const BindingDecl* stmt)
{
// Assume that we are looking at a builtin type. We have to construct the variable declaration information.
const auto* bindingStmt = stmt->getBinding();

// In a dependent context we have no binding and with that no type. Leave this as it is, we are looking at a
Expand All @@ -3907,17 +3906,20 @@ void StructuredBindingsCodeGenerator::InsertArg(const BindingDecl* stmt)
// Assume that we are looking at a builtin type. We have to construct the variable declaration information.
auto type = stmt->getType();

// If we have holding var we are looking at a user defined type like tuple and those the defaults from above are
// If we have a holding var we are looking at a user defined type like tuple and those the defaults from above are
// wrong. This type contains the variable declaration so we insert this.
if(const auto* holdingVar = stmt->getHoldingVar()) {
// A rvalue reference boils down to just the type. If it is a reference then it is a lvalue reference at this
// point. Hence we need to strip the &&.
// Initial paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0144r0.pdf

// The type of the binding depends on the initializer. In case the initializer is an lvalue we get a T&,
// otherwise a T&&. We typically look at an lvalue if the decomposition declaration was auto& [a,b]. Note the &
// here We have a rvalue in case the decomposition declaration was auto [a,b]. Note no reference. The standard
// std::get returns a lvalue reference in case e in get(e) is an lvalue, otherwise it returns an rvalue
// reference because then the call is get(std::move(e))
type = holdingVar->getType().getCanonicalType();
if(type->isRValueReferenceType()) {
type = type.getNonReferenceType();
}

bindingStmt = holdingVar->getAnyInitializer();

} else if(not type->isLValueReferenceType()) {
type = stmt->getASTContext().getLValueReferenceType(type);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/Issue131.expect
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ std::tuple<int, float> foo();


std::tuple<int, float> __foo5 = foo();
int a = std::get<0UL>(static_cast<std::tuple<int, float> &&>(__foo5));
float b = std::get<1UL>(static_cast<std::tuple<int, float> &&>(__foo5));
int && a = std::get<0UL>(static_cast<std::tuple<int, float> &&>(__foo5));
float && b = std::get<1UL>(static_cast<std::tuple<int, float> &&>(__foo5));

4 changes: 2 additions & 2 deletions tests/Issue20.expect
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ int main()
{
std::map<int, int> map = std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >{std::initializer_list<std::pair<const int, int> >{std::pair<const int, int>{1, 2}}, std::less<int>()};
std::pair<const int, int> __operator6 = std::pair<const int, int>(map.begin().operator*());
const int key = std::get<0UL>(static_cast<std::pair<const int, int> &&>(__operator6));
int value = std::get<1UL>(static_cast<std::pair<const int, int> &&>(__operator6));
const int && key = std::get<0UL>(static_cast<std::pair<const int, int> &&>(__operator6));
int && value = std::get<1UL>(static_cast<std::pair<const int, int> &&>(__operator6));
return 0;
}

4 changes: 2 additions & 2 deletions tests/Issue381.expect
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ int main()
{
std::tuple<int, int> tup = std::tuple<int, int>{2, 5};
std::tuple<int, int> __tup6 = std::tuple<int, int>(tup);
int a = std::get<0UL>(static_cast<std::tuple<int, int> &&>(__tup6));
int b = std::get<1UL>(static_cast<std::tuple<int, int> &&>(__tup6));
int && a = std::get<0UL>(static_cast<std::tuple<int, int> &&>(__tup6));
int && b = std::get<1UL>(static_cast<std::tuple<int, int> &&>(__tup6));
return 0;
}

7 changes: 7 additions & 0 deletions tests/Issue425.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <utility>

int main()
{
std::pair<int, char> p;
auto [a, b] = p;
}
11 changes: 11 additions & 0 deletions tests/Issue425.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <utility>

int main()
{
std::pair<int, char> p = std::pair<int, char>();
std::pair<int, char> __p6 = std::pair<int, char>(p);
int && a = std::get<0UL>(static_cast<std::pair<int, char> &&>(__p6));
char && b = std::get<1UL>(static_cast<std::pair<int, char> &&>(__p6));
return 0;
}

12 changes: 12 additions & 0 deletions tests/StructuredBindingsHandler10Test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Source: https://en.cppreference.com/w/cpp/language/structured_binding
#include <tuple>

float x{};
char y{};
int z{};

std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
const auto& [a,b,c] = tpl;
// a names a structured binding that refers to x; decltype(a) is float&
// b names a structured binding that refers to y; decltype(b) is char&&
// c names a structured binding that refers to the 3rd element of tpl; decltype(c) is const int
20 changes: 20 additions & 0 deletions tests/StructuredBindingsHandler10Test.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Source: https://en.cppreference.com/w/cpp/language/structured_binding
#include <tuple>

float x = {};

char y = {};

int z = {};


std::tuple<float &, char &&, int> tpl = std::tuple<float &, char &&, int>(x, std::move(y), z);

const std::tuple<float &, char &&, int> & __tpl9 = tpl;
float & a = std::get<0UL>(__tpl9);
char & b = std::get<1UL>(__tpl9);
const int & c = std::get<2UL>(__tpl9);

// a names a structured binding that refers to x; decltype(a) is float&
// b names a structured binding that refers to y; decltype(b) is char&&
// c names a structured binding that refers to the 3rd element of tpl; decltype(c) is const int
18 changes: 9 additions & 9 deletions tests/StructuredBindingsHandler3Test.expect
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,25 @@ namespace constant
{
Q q = Q();
Q __q17 = Q(q);
int a = get<0UL>(static_cast<Q &&>(__q17));
int b = get<1UL>(static_cast<Q &&>(__q17));
int c = get<2UL>(static_cast<Q &&>(__q17));
int && a = get<0UL>(static_cast<Q &&>(__q17));
int && b = get<1UL>(static_cast<Q &&>(__q17));
int && c = get<2UL>(static_cast<Q &&>(__q17));
inline constexpr bool f()
{
Q __q19 = Q(q);
int a = get<0UL>(static_cast<Q &&>(__q19));
int b = get<1UL>(static_cast<Q &&>(__q19));
int c = get<2UL>(static_cast<Q &&>(__q19));
int && a = get<0UL>(static_cast<Q &&>(__q19));
int && b = get<1UL>(static_cast<Q &&>(__q19));
int && c = get<2UL>(static_cast<Q &&>(__q19));
return ((a == 0) && (b == 1)) && (c == 4);
}
inline constexpr int g()
{
int * p = nullptr;
{
Q __q26 = Q(q);
int a = get<0UL>(static_cast<Q &&>(__q26));
int b = get<1UL>(static_cast<Q &&>(__q26));
int c = get<2UL>(static_cast<Q &&>(__q26));
int && a = get<0UL>(static_cast<Q &&>(__q26));
int && b = get<1UL>(static_cast<Q &&>(__q26));
int && c = get<2UL>(static_cast<Q &&>(__q26));
p = &c;
};
return *p;
Expand Down
4 changes: 2 additions & 2 deletions tests/StructuredBindingsHandler5Test.expect
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ int main()
Point p = Point{1, 2};
Point & __p64 = p;
double & x = __p64.get<0>();
double y = __p64.get<1>();
double && y = __p64.get<1>();
printf("x:%lf y:%lf\n", p.GetX(), p.GetY());
++x;
++y;
Expand All @@ -161,7 +161,7 @@ int main()
constexpr const Point p2 = Point{3, 4};
const Point & __p273 = p2;
const double & x2 = __p273.get<0>();
const double y2 = static_cast<const double>(__p273.get<1>());
const double && y2 = static_cast<const double>(__p273.get<1>());
return 0;
}

10 changes: 5 additions & 5 deletions tests/StructuredBindingsHandler6Test.expect
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ int main()
{
Point p = Point{1, 2};
Point __p58 = Point(p);
double x = static_cast<const Point &&>(__p58).get<0>();
double y = static_cast<const Point &&>(__p58).get<1>();
double && x = static_cast<const Point &&>(__p58).get<0>();
double && y = static_cast<const Point &&>(__p58).get<1>();
printf("x:%lf y:%lf\n", p.GetX(), p.GetY());
printf("x:%lf y:%lf\n", x, y);
char ar[2] = {7, 8};
Expand All @@ -143,9 +143,9 @@ int main()
char & c = std::get<1UL>(__muple80);
double & d = std::get<2UL>(__muple80);
std::tuple<int, char, double> __muple82 = std::tuple<int, char, double>(muple);
int ii = std::get<0UL>(static_cast<std::tuple<int, char, double> &&>(__muple82));
char cc = std::get<1UL>(static_cast<std::tuple<int, char, double> &&>(__muple82));
double dd = std::get<2UL>(static_cast<std::tuple<int, char, double> &&>(__muple82));
int && ii = std::get<0UL>(static_cast<std::tuple<int, char, double> &&>(__muple82));
char && cc = std::get<1UL>(static_cast<std::tuple<int, char, double> &&>(__muple82));
double && dd = std::get<2UL>(static_cast<std::tuple<int, char, double> &&>(__muple82));
return 0;
}

10 changes: 10 additions & 0 deletions tests/StructuredBindingsHandler8Test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <tuple>

auto tup = std::tuple<int, int>{2, 5};
auto [a1, b1] = tup;


int i = 5;
auto tup2 = std::tuple<int, int&>{2, i};
auto [a2, b2] = tup2;

19 changes: 19 additions & 0 deletions tests/StructuredBindingsHandler8Test.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <tuple>

std::tuple<int, int> tup = std::tuple<int, int>{2, 5};

std::tuple<int, int> __tup4 = std::tuple<int, int>(tup);
int && a1 = std::get<0UL>(static_cast<std::tuple<int, int> &&>(__tup4));
int && b1 = std::get<1UL>(static_cast<std::tuple<int, int> &&>(__tup4));



int i = 5;

std::tuple<int, int &> tup2 = std::tuple<int, int &>{2, i};

std::tuple<int, int &> __tup29 = std::tuple<int, int &>(tup2);
int && a2 = std::get<0UL>(static_cast<std::tuple<int, int &> &&>(__tup29));
int & b2 = std::get<1UL>(static_cast<std::tuple<int, int &> &&>(__tup29));


4 changes: 2 additions & 2 deletions tests/StructuredBindingsHandler9Test.expect
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ std::tuple<int, float> foo = std::tuple<int, float>();


std::tuple<int, float> __foo5 = std::tuple<int, float>(foo);
int a = std::get<0UL>(static_cast<std::tuple<int, float> &&>(__foo5));
float b = std::get<1UL>(static_cast<std::tuple<int, float> &&>(__foo5));
int && a = std::get<0UL>(static_cast<std::tuple<int, float> &&>(__foo5));
float && b = std::get<1UL>(static_cast<std::tuple<int, float> &&>(__foo5));

std::tuple<int, float> & __foo6 = foo;
int & ra = std::get<0UL>(__foo6);
Expand Down
4 changes: 2 additions & 2 deletions tests/TupeInRangeBasedForTest.expect
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ int main()
std::__wrap_iter<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> *> __end1 = __range1.end();
for(; std::operator!=(__begin1, __end1); __begin1.operator++()) {
std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> __operator15 = std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int>(__begin1.operator*());
std::basic_string<char, std::char_traits<char>, std::allocator<char> > s = std::get<0UL>(static_cast<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> &&>(__operator15));
int n = std::get<1UL>(static_cast<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> &&>(__operator15));
std::basic_string<char, std::char_traits<char>, std::allocator<char> > && s = std::get<0UL>(static_cast<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> &&>(__operator15));
int && n = std::get<1UL>(static_cast<std::tuple<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int> &&>(__operator15));
printf("c=%s, n=%d\n", s.c_str(), n);
}

Expand Down
86 changes: 86 additions & 0 deletions tests/dcl.struct.bind.Test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

// p1
// cv := denote the cv-qualifiers in the decl-specifier-seq
// S := consist of the storage-class-specifiers of the decl-specifier-seq (if any)
//
// 1) a variable with a unique name e is introduced.
// If the assignment-expression in the initializer has array type A and no ref-qualifier is present, e is defined by:
//
// attribute-specifier-seq_{opt} S cv A e ;
//
// otherwise e is defined as-if by:
//
// attribute-specifier-seq_{opt} decl-specifier-seq ref-qualifier_{opt} e initializer ;
//
// The type of the id-expression e is called E.
//
// Note: E is never a reference type
//
//
// -- tuple --
// Let i be an index of type std::size_t corresponding to vi
// either:
// e.get<i>()
// get<i>(e)
//
// In either case,
// - e is an lvalue if the type of the entity e is an lvalue reference and
// - an xvalue otherwise.
//
// -> auto& [a ,b ] -> e := lvalue
// -> auto [a ,b ] -> e := xvalue
//
// T_i := std::tuple_element<i, E>::type
// U_i := either
// - T_i&: if the initializer is an lvalue,
// - T_i&&: an rvalue reference otherwise,
//
// variables are introduced with unique names r_i as follows:
//
// S U_i r_i = initializer ;
//
// Each vi is the name of an lvalue of type Ti that refers to the object bound to ri; the referenced type is Ti.
//


// The lvalue is a bit-field if that member is a bit-field. [Example:
// struct S { int x1 : 2; volatile double y1; };
// S f();
// const auto [ x, y ] = f();
// The type of the id-expression x is “const int”, the type of the id-expression y is “const volatile double”. —end example]


// For each identifier, a variable whose type is "reference to std::tuple_element<i, E>::type" is introduced: lvalue reference if its corresponding initializer is an lvalue, rvalue reference otherwise. The initializer for the i-th variable is
// - e.get<i>(), if lookup for the identifier get in the scope of E by class member access lookup finds at least one declaration that is a function template whose first template parameter is a non-type parameter
// - Otherwise, get<i>(e), where get is looked up by argument-dependent lookup only, ignoring non-ADL lookup.
//
// The initializer for the new variable is e.get<i> or get<i>(e).
// Here the overload of get that is called is a rvalue in case we use auto and an lvalue in case we use auto&



#include <cassert>
#include <tuple>

// https://en.cppreference.com/w/cpp/language/structured_binding
float x{};
char y{};
int z{};

std::tuple<float&,char&&,int> tpl(x,std::move(y),z);
//auto tpl = std::tuple{x,std::move(y),z};
auto& [a,b,c] = tpl;
// a names a structured binding that refers to x; decltype(a) is float&
// b names a structured binding that refers to y; decltype(b) is char&&
// c names a structured binding that refers to the 3rd element of tpl; decltype(c) is int

int main() {
a = 4.5;
c = 5;

// assert(4.5 == x);
// assert(0 == z);

// std::cout << a << '\n';
// std::cout << z << '\n';
}
Loading