Skip to content

Commit

Permalink
[clang][Interp] Fix CheckCallable for undefined-and-not-constexpr fns
Browse files Browse the repository at this point in the history
  • Loading branch information
tbaederr committed Jul 18, 2024
1 parent 15495b8 commit d00b355
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 44 deletions.
86 changes: 45 additions & 41 deletions clang/lib/AST/Interp/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -579,57 +579,61 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
return false;
}

if (!F->isConstexpr() || !F->hasBody()) {
const SourceLocation &Loc = S.Current->getLocation(OpPC);
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();
if (F->isConstexpr() && F->hasBody() && F->getDecl()->isConstexpr())
return true;

// Invalid decls have been diagnosed before.
if (DiagDecl->isInvalidDecl())
return false;
// Implicitly constexpr.
if (F->isLambdaStaticInvoker())
return true;

// If this function is not constexpr because it is an inherited
// non-constexpr constructor, diagnose that directly.
const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
if (CD && CD->isInheritingConstructor()) {
const auto *Inherited = CD->getInheritedConstructor().getConstructor();
if (!Inherited->isConstexpr())
DiagDecl = CD = Inherited;
}
const SourceLocation &Loc = S.Current->getLocation(OpPC);
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();

// Invalid decls have been diagnosed before.
if (DiagDecl->isInvalidDecl())
return false;

// If this function is not constexpr because it is an inherited
// non-constexpr constructor, diagnose that directly.
const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
if (CD && CD->isInheritingConstructor()) {
const auto *Inherited = CD->getInheritedConstructor().getConstructor();
if (!Inherited->isConstexpr())
DiagDecl = CD = Inherited;
}

// FIXME: If DiagDecl is an implicitly-declared special member function
// or an inheriting constructor, we should be much more explicit about why
// it's not constexpr.
if (CD && CD->isInheritingConstructor()) {
S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
// FIXME: If DiagDecl is an implicitly-declared special member function
// or an inheriting constructor, we should be much more explicit about why
// it's not constexpr.
if (CD && CD->isInheritingConstructor()) {
S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
<< CD->getInheritedConstructor().getConstructor()->getParent();
S.Note(DiagDecl->getLocation(), diag::note_declared_at);
} else {
// Don't emit anything if the function isn't defined and we're checking
// for a constant expression. It might be defined at the point we're
// actually calling it.
bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
if (!DiagDecl->isDefined() && !IsExtern &&
S.checkingPotentialConstantExpression())
return false;
S.Note(DiagDecl->getLocation(), diag::note_declared_at);
} else {
// Don't emit anything if the function isn't defined and we're checking
// for a constant expression. It might be defined at the point we're
// actually calling it.
bool IsExtern = DiagDecl->getStorageClass() == SC_Extern;
if (!DiagDecl->isDefined() && !IsExtern && DiagDecl->isConstexpr() &&
S.checkingPotentialConstantExpression())
return false;

// If the declaration is defined, declared 'constexpr' _and_ has a body,
// the below diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr() &&
DiagDecl->hasBody())
return false;
// If the declaration is defined, declared 'constexpr' _and_ has a body,
// the below diagnostic doesn't add anything useful.
if (DiagDecl->isDefined() && DiagDecl->isConstexpr() &&
DiagDecl->hasBody())
return false;

S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
<< DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
S.Note(DiagDecl->getLocation(), diag::note_declared_at);
}
} else {
S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
S.Note(DiagDecl->getLocation(), diag::note_declared_at);
}
return false;
} else {
S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
}

return true;
return false;
}

bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2531,14 +2531,14 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
if (!CheckInvoke(S, OpPC, ThisPtr))
return false;
}

if (S.checkingPotentialConstantExpression())
return false;
}

if (!CheckCallable(S, OpPC, Func))
return false;

if (Func->hasThisPointer() && S.checkingPotentialConstantExpression())
return false;

if (!CheckCallDepth(S, OpPC))
return false;

Expand Down
15 changes: 15 additions & 0 deletions clang/test/AST/Interp/cxx2a.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fcxx-exceptions -verify=ref,both %s
// RUN: %clang_cc1 -std=c++2a -fsyntax-only -fcxx-exceptions -verify=expected,both %s -fexperimental-new-constant-interpreter

template <unsigned N>
struct S {
S() requires (N==1) = default;
S() requires (N==2) {} // both-note {{declared here}}
consteval S() requires (N==3) = default;
};

consteval int aConstevalFunction() { // both-error {{consteval function never produces a constant expression}}
S<2> s4; // both-note {{non-constexpr constructor 'S' cannot be used in a constant expression}}
return 0;
}
/// We're NOT calling the above function. The diagnostics should appear anyway.

0 comments on commit d00b355

Please sign in to comment.