Allow dllimport non-type template arguments in C++17
Summary: Fixes PR35772. Reviewers: rsmith Differential Revision: https://reviews.llvm.org/D43320 llvm-svn: 332018
This commit is contained in:
parent
175400a801
commit
1a840d29b4
|
@ -658,6 +658,13 @@ public:
|
|||
ArrayRef<const Expr*> Args,
|
||||
const Expr *This = nullptr) const;
|
||||
|
||||
/// Indicates how the constant expression will be used.
|
||||
enum ConstExprUsage { EvaluateForCodeGen, EvaluateForMangling };
|
||||
|
||||
/// Evaluate an expression that is required to be a constant expression.
|
||||
bool EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
||||
const ASTContext &Ctx) const;
|
||||
|
||||
/// If the current Expr is a pointer, this will try to statically
|
||||
/// determine the number of bytes available where the pointer is pointing.
|
||||
/// Returns true if all of the above holds and we were able to figure out the
|
||||
|
|
|
@ -1720,7 +1720,8 @@ static void NoteLValueLocation(EvalInfo &Info, APValue::LValueBase Base) {
|
|||
/// value for an address or reference constant expression. Return true if we
|
||||
/// can fold this expression, whether or not it's a constant expression.
|
||||
static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
||||
QualType Type, const LValue &LVal) {
|
||||
QualType Type, const LValue &LVal,
|
||||
Expr::ConstExprUsage Usage) {
|
||||
bool IsReferenceType = Type->isReferenceType();
|
||||
|
||||
APValue::LValueBase Base = LVal.getLValueBase();
|
||||
|
@ -1753,7 +1754,7 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
|||
return false;
|
||||
|
||||
// A dllimport variable never acts like a constant.
|
||||
if (Var->hasAttr<DLLImportAttr>())
|
||||
if (Usage == Expr::EvaluateForCodeGen && Var->hasAttr<DLLImportAttr>())
|
||||
return false;
|
||||
}
|
||||
if (const auto *FD = dyn_cast<const FunctionDecl>(VD)) {
|
||||
|
@ -1767,7 +1768,8 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
|||
// The C language has no notion of ODR; furthermore, it has no notion of
|
||||
// dynamic initialization. This means that we are permitted to
|
||||
// perform initialization with the address of the thunk.
|
||||
if (Info.getLangOpts().CPlusPlus && FD->hasAttr<DLLImportAttr>())
|
||||
if (Info.getLangOpts().CPlusPlus && Usage == Expr::EvaluateForCodeGen &&
|
||||
FD->hasAttr<DLLImportAttr>())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1800,12 +1802,14 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, SourceLocation Loc,
|
|||
static bool CheckMemberPointerConstantExpression(EvalInfo &Info,
|
||||
SourceLocation Loc,
|
||||
QualType Type,
|
||||
const APValue &Value) {
|
||||
const APValue &Value,
|
||||
Expr::ConstExprUsage Usage) {
|
||||
const ValueDecl *Member = Value.getMemberPointerDecl();
|
||||
const auto *FD = dyn_cast_or_null<CXXMethodDecl>(Member);
|
||||
if (!FD)
|
||||
return true;
|
||||
return FD->isVirtual() || !FD->hasAttr<DLLImportAttr>();
|
||||
return Usage == Expr::EvaluateForMangling || FD->isVirtual() ||
|
||||
!FD->hasAttr<DLLImportAttr>();
|
||||
}
|
||||
|
||||
/// Check that this core constant expression is of literal type, and if not,
|
||||
|
@ -1843,8 +1847,10 @@ static bool CheckLiteralType(EvalInfo &Info, const Expr *E,
|
|||
/// Check that this core constant expression value is a valid value for a
|
||||
/// constant expression. If not, report an appropriate diagnostic. Does not
|
||||
/// check that the expression is of literal type.
|
||||
static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
|
||||
QualType Type, const APValue &Value) {
|
||||
static bool
|
||||
CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc, QualType Type,
|
||||
const APValue &Value,
|
||||
Expr::ConstExprUsage Usage = Expr::EvaluateForCodeGen) {
|
||||
if (Value.isUninit()) {
|
||||
Info.FFDiag(DiagLoc, diag::note_constexpr_uninitialized)
|
||||
<< true << Type;
|
||||
|
@ -1863,28 +1869,28 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
|
|||
QualType EltTy = Type->castAsArrayTypeUnsafe()->getElementType();
|
||||
for (unsigned I = 0, N = Value.getArrayInitializedElts(); I != N; ++I) {
|
||||
if (!CheckConstantExpression(Info, DiagLoc, EltTy,
|
||||
Value.getArrayInitializedElt(I)))
|
||||
Value.getArrayInitializedElt(I), Usage))
|
||||
return false;
|
||||
}
|
||||
if (!Value.hasArrayFiller())
|
||||
return true;
|
||||
return CheckConstantExpression(Info, DiagLoc, EltTy,
|
||||
Value.getArrayFiller());
|
||||
return CheckConstantExpression(Info, DiagLoc, EltTy, Value.getArrayFiller(),
|
||||
Usage);
|
||||
}
|
||||
if (Value.isUnion() && Value.getUnionField()) {
|
||||
return CheckConstantExpression(Info, DiagLoc,
|
||||
Value.getUnionField()->getType(),
|
||||
Value.getUnionValue());
|
||||
Value.getUnionValue(), Usage);
|
||||
}
|
||||
if (Value.isStruct()) {
|
||||
RecordDecl *RD = Type->castAs<RecordType>()->getDecl();
|
||||
if (const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD)) {
|
||||
unsigned BaseIndex = 0;
|
||||
for (CXXRecordDecl::base_class_const_iterator I = CD->bases_begin(),
|
||||
End = CD->bases_end(); I != End; ++I, ++BaseIndex) {
|
||||
if (!CheckConstantExpression(Info, DiagLoc, I->getType(),
|
||||
Value.getStructBase(BaseIndex)))
|
||||
for (const CXXBaseSpecifier &BS : CD->bases()) {
|
||||
if (!CheckConstantExpression(Info, DiagLoc, BS.getType(),
|
||||
Value.getStructBase(BaseIndex), Usage))
|
||||
return false;
|
||||
++BaseIndex;
|
||||
}
|
||||
}
|
||||
for (const auto *I : RD->fields()) {
|
||||
|
@ -1892,7 +1898,8 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
|
|||
continue;
|
||||
|
||||
if (!CheckConstantExpression(Info, DiagLoc, I->getType(),
|
||||
Value.getStructField(I->getFieldIndex())))
|
||||
Value.getStructField(I->getFieldIndex()),
|
||||
Usage))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1900,11 +1907,11 @@ static bool CheckConstantExpression(EvalInfo &Info, SourceLocation DiagLoc,
|
|||
if (Value.isLValue()) {
|
||||
LValue LVal;
|
||||
LVal.setFrom(Info.Ctx, Value);
|
||||
return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal);
|
||||
return CheckLValueConstantExpression(Info, DiagLoc, Type, LVal, Usage);
|
||||
}
|
||||
|
||||
if (Value.isMemberPointer())
|
||||
return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value);
|
||||
return CheckMemberPointerConstantExpression(Info, DiagLoc, Type, Value, Usage);
|
||||
|
||||
// Everything else is fine.
|
||||
return true;
|
||||
|
@ -10345,13 +10352,25 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const {
|
|||
LValue LV;
|
||||
if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects ||
|
||||
!CheckLValueConstantExpression(Info, getExprLoc(),
|
||||
Ctx.getLValueReferenceType(getType()), LV))
|
||||
Ctx.getLValueReferenceType(getType()), LV,
|
||||
Expr::EvaluateForCodeGen))
|
||||
return false;
|
||||
|
||||
LV.moveInto(Result.Val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Expr::EvaluateAsConstantExpr(EvalResult &Result, ConstExprUsage Usage,
|
||||
const ASTContext &Ctx) const {
|
||||
EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression;
|
||||
EvalInfo Info(Ctx, Result, EM);
|
||||
if (!::Evaluate(Result.Val, Info, this))
|
||||
return false;
|
||||
|
||||
return CheckConstantExpression(Info, getExprLoc(), getType(), Result.Val,
|
||||
Usage);
|
||||
}
|
||||
|
||||
bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
|
||||
const VarDecl *VD,
|
||||
SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
|
||||
|
|
|
@ -5407,10 +5407,11 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
|
|||
SmallVector<PartialDiagnosticAt, 8> Notes;
|
||||
Expr::EvalResult Eval;
|
||||
Eval.Diag = &Notes;
|
||||
Expr::ConstExprUsage Usage = CCE == Sema::CCEK_TemplateArg
|
||||
? Expr::EvaluateForMangling
|
||||
: Expr::EvaluateForCodeGen;
|
||||
|
||||
if ((T->isReferenceType()
|
||||
? !Result.get()->EvaluateAsLValue(Eval, S.Context)
|
||||
: !Result.get()->EvaluateAsRValue(Eval, S.Context)) ||
|
||||
if (!Result.get()->EvaluateAsConstantExpr(Eval, Usage, S.Context) ||
|
||||
(RequireInt && !Eval.Val.isInt())) {
|
||||
// The expression can't be folded, so we can't keep it at this position in
|
||||
// the AST.
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
// RUN: %clang_cc1 -std=c++14 %s -verify -fms-extensions -triple x86_64-windows-msvc
|
||||
// RUN: %clang_cc1 -std=c++17 %s -verify -fms-extensions -triple x86_64-windows-msvc
|
||||
|
||||
__declspec(dllimport) void imported_func();
|
||||
__declspec(dllimport) int imported_int;
|
||||
struct Foo {
|
||||
void __declspec(dllimport) imported_method();
|
||||
};
|
||||
|
||||
// Instantiation is OK.
|
||||
template <void (*FP)()> struct TemplateFnPtr {
|
||||
static void getit() { FP(); }
|
||||
};
|
||||
template <void (&FP)()> struct TemplateFnRef {
|
||||
static void getit() { FP(); }
|
||||
};
|
||||
void instantiate1() {
|
||||
TemplateFnPtr<&imported_func>::getit();
|
||||
TemplateFnRef<imported_func>::getit();
|
||||
}
|
||||
|
||||
// Check variable template instantiation.
|
||||
template <int *GI> struct TemplateIntPtr {
|
||||
static int getit() { return *GI; }
|
||||
};
|
||||
template <int &GI> struct TemplateIntRef {
|
||||
static int getit() { return GI; }
|
||||
};
|
||||
int instantiate2() {
|
||||
int r = 0;
|
||||
r += TemplateIntPtr<&imported_int>::getit();
|
||||
r += TemplateIntRef<imported_int>::getit();
|
||||
return r;
|
||||
}
|
||||
|
||||
// Member pointer instantiation.
|
||||
template <void (Foo::*MP)()> struct TemplateMemPtr { };
|
||||
TemplateMemPtr<&Foo::imported_method> instantiate_mp;
|
||||
|
||||
// constexpr initialization doesn't work for dllimport things.
|
||||
// expected-error@+1{{must be initialized by a constant expression}}
|
||||
constexpr void (*constexpr_import_func)() = &imported_func;
|
||||
// expected-error@+1{{must be initialized by a constant expression}}
|
||||
constexpr int *constexpr_import_int = &imported_int;
|
||||
// expected-error@+1{{must be initialized by a constant expression}}
|
||||
constexpr void (Foo::*constexpr_memptr)() = &Foo::imported_method;
|
||||
|
||||
// We make dynamic initializers for 'const' globals, but not constexpr ones.
|
||||
void (*const const_import_func)() = &imported_func;
|
||||
int *const const_import_int = &imported_int;
|
||||
void (Foo::*const const_memptr)() = &Foo::imported_method;
|
||||
|
||||
// Check that using a non-type template parameter for constexpr global
|
||||
// initialization is correctly diagnosed during template instantiation.
|
||||
template <void (*FP)()> struct StaticConstexpr {
|
||||
// expected-error@+1{{must be initialized by a constant expression}}
|
||||
static constexpr void (*g_fp)() = FP;
|
||||
};
|
||||
void instantiate3() {
|
||||
// expected-note@+1 {{requested here}}
|
||||
StaticConstexpr<imported_func>::g_fp();
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -verify -std=c++11 %s
|
||||
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -verify -std=c++17 %s
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
|
|
Loading…
Reference in New Issue