diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -62,8 +62,10 @@ // Return a dummy function if compilation failed. if (BailLocation) return llvm::make_error(*BailLocation); - else + else { + Func->setIsFullyCompiled(true); return Func; + } } else { // Create scopes from descriptors. llvm::SmallVector Scopes; @@ -74,6 +76,7 @@ // Set the function's code. Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), std::move(Scopes)); + Func->setIsFullyCompiled(true); return Func; } } 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 @@ -612,6 +612,14 @@ } assert(Func); + // If the function is being compiled right now, this is a recursive call. + // In that case, the function can't be valid yet, even though it will be + // later. + // If the function is already fully compiled but not constexpr, it was + // found to be faulty earlier on, so bail out. + if (Func->isFullyCompiled() && !Func->isConstexpr()) + return false; + QualType ReturnType = E->getCallReturnType(Ctx.getASTContext()); Optional T = classify(ReturnType); diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -241,11 +241,16 @@ // Integers, pointers, primitives. if (Optional T = this->classify(DT)) { + const Expr *Init = VD->getInit(); + + if (!Init) + return false; + auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); - // Compile the initialiser in its own scope. + // Compile the initializer in its own scope. { ExprScope Scope(this); - if (!this->visit(VD->getInit())) + if (!this->visit(Init)) return false; } // Set the value. diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -112,6 +112,9 @@ /// Checks if the function is a constructor. bool isConstructor() const { return isa(F); } + /// Checks if the function is fully done compiling. + bool isFullyCompiled() const { return IsFullyCompiled; } + private: /// Construct a function representing an actual function. Function(Program &P, const FunctionDecl *F, unsigned ArgSize, @@ -128,6 +131,8 @@ IsValid = true; } + void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; } + private: friend class Program; friend class ByteCodeEmitter; @@ -154,6 +159,9 @@ llvm::DenseMap Params; /// Flag to indicate if the function is valid. bool IsValid = false; + /// Flag to indicate if the function is done being + /// compiled to bytecode. + bool IsFullyCompiled = false; public: /// Dumps the disassembled bytecode to \c llvm::errs(). diff --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -2,8 +2,6 @@ // RUN: %clang_cc1 -std=c++20 -verify=ref %s -// expected-no-diagnostics -// ref-no-diagnostics constexpr int getMinus5() { int a = 10; a = -5; @@ -53,3 +51,12 @@ return v; } static_assert(pointerAssign2() == 12, ""); + + +constexpr int unInitLocal() { + int a; + return a; // ref-note{{read of uninitialized object}} +} +static_assert(unInitLocal() == 0, ""); // expected-error {{not an integral constant expression}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'unInitLocal()'}}