diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -814,15 +814,38 @@ template bool ByteCodeExprGen::VisitDeclRefExpr(const DeclRefExpr *E) { const auto *Decl = E->getDecl(); + bool IsReference = Decl->getType()->isReferenceType(); + bool FoundDecl = false; if (auto It = Locals.find(Decl); It != Locals.end()) { const unsigned Offset = It->second.Offset; - return this->emitGetPtrLocal(Offset, E); + if (!this->emitGetPtrLocal(Offset, E)) + return false; + + FoundDecl = true; } else if (auto GlobalIndex = P.getGlobal(Decl)) { - return this->emitGetPtrGlobal(*GlobalIndex, E); + if (!this->emitGetPtrGlobal(*GlobalIndex, E)) + return false; + + FoundDecl = true; } else if (const auto *PVD = dyn_cast(Decl)) { - if (auto It = this->Params.find(PVD); It != this->Params.end()) - return this->emitGetPtrParam(It->second, E); + if (auto It = this->Params.find(PVD); It != this->Params.end()) { + if (!this->emitGetPtrParam(It->second, E)) + return false; + + FoundDecl = true; + } + } + + // References are implemented using pointers, so when we get here, + // we have a pointer to a pointer, which we need to de-reference once. + if (FoundDecl) { + if (IsReference) { + if (!this->emitLoadPopPtr(E)) + return false; + } + + return true; } return false; diff --git a/clang/test/AST/Interp/arrays.cpp b/clang/test/AST/Interp/arrays.cpp --- a/clang/test/AST/Interp/arrays.cpp +++ b/clang/test/AST/Interp/arrays.cpp @@ -45,6 +45,13 @@ static_assert(getElementOf(foo[0], 1) == &m, ""); +template +constexpr T& getElementOfArray(T (&array)[N], int I) { + return array[I]; +} +static_assert(getElementOfArray(foo[2], 3) == &m, ""); + + constexpr int data[] = {5, 4, 3, 2, 1}; static_assert(data[0] == 4, ""); // expected-error{{failed}} \ // expected-note{{5 == 4}} \ diff --git a/clang/test/AST/Interp/references.cpp b/clang/test/AST/Interp/references.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/Interp/references.cpp @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s +// RUN: %clang_cc1 -verify=ref %s + + +// ref-no-diagnostics + +constexpr int a = 10; +constexpr const int &b = a; +static_assert(a == b, ""); + +constexpr int assignToReference() { + int a = 20; + int &b = a; + + b = 100; + return a; +} +static_assert(assignToReference() == 100, ""); + + +constexpr void setValue(int &dest, int val) { + dest = val; +} + +constexpr int checkSetValue() { + int l = 100; + setValue(l, 200); + return l; +} +static_assert(checkSetValue() == 200, ""); + +constexpr int readLocalRef() { + int a = 20; + int &b = a; + return b; +} +static_assert(readLocalRef() == 20, ""); + +constexpr int incRef() { + int a = 0; + int &b = a; + + b = b + 1; + + return a; +} +static_assert(incRef() == 1, ""); + + +template +constexpr void Plus3(int &A) { + A = V + 3; +} +constexpr int foo = 4; + +constexpr int callTemplate() { + int a = 3; + Plus3(a); + return a; +} +static_assert(callTemplate() == 7, ""); + + +constexpr int& getValue(int *array, int index) { + return array[index]; +} +constexpr int testGetValue() { + int values[] = {1, 2, 3, 4}; + getValue(values, 2) = 30; + return values[2]; +} +static_assert(testGetValue() == 30, ""); + +// FIXME: ExprWithCleanups + MaterializeTemporaryExpr not implemented +constexpr const int &MCE = 1; // expected-error{{must be initialized by a constant expression}} + + +struct S { + int i, j; +}; + +constexpr int RefToMemberExpr() { + S s{1, 2}; + + int &j = s.i; + j += 10; + + return j; +} +// FIXME: Should be accepted. +static_assert(RefToMemberExpr() == 11, ""); // expected-error{{not an integral constant expression}}