Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -99,6 +99,15 @@ } }; +/// Contexts in which a converted constant expression is required. +enum CCEKind { + CCEK_CaseValue, ///< Expression in a case label. + CCEK_Enumerator, ///< Enumerator value with fixed underlying type. + CCEK_TemplateArg, ///< Value of a non-type template parameter. + CCEK_NewExpr, ///< Constant expression in a noptr-new-declarator. + CCEK_ConstexprIf ///< Condition in a constexpr if statement. +}; + /// Expr - This represents one expression. Note that Expr's are subclasses of /// Stmt. This allows an expression to be transparently used any place a Stmt /// is required. @@ -658,6 +667,10 @@ ArrayRef Args, const Expr *This = nullptr) const; + /// Evaluate an expression that is required to be a core constant expression. + bool EvaluateAsCoreConstExpr(EvalResult &Result, QualType ParamType, + CCEKind CCE, const ASTContext &Ctx) const; + /// \brief 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 Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2582,14 +2582,6 @@ ExprResult PerformContextuallyConvertToBool(Expr *From); ExprResult PerformContextuallyConvertToObjCPointer(Expr *From); - /// Contexts in which a converted constant expression is required. - enum CCEKind { - CCEK_CaseValue, ///< Expression in a case label. - CCEK_Enumerator, ///< Enumerator value with fixed underlying type. - CCEK_TemplateArg, ///< Value of a non-type template parameter. - CCEK_NewExpr, ///< Constant expression in a noptr-new-declarator. - CCEK_ConstexprIf ///< Condition in a constexpr if statement. - }; ExprResult CheckConvertedConstantExpression(Expr *From, QualType T, llvm::APSInt &Value, CCEKind CCE); ExprResult CheckConvertedConstantExpression(Expr *From, QualType T, Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -649,6 +649,10 @@ /// a constant expression. EM_PotentialConstantExpression, + /// Evaluate as a C++17 non-type template argument, which is a core + /// constant expression with a special case for dllimport declarations. + EM_TemplateArgument, + /// Fold the expression to a constant. Stop if we hit a side-effect that /// we can't model. EM_ConstantFold, @@ -738,6 +742,14 @@ return false; } + /// Return true if this declaration is dllimport and we cannot treat the + /// address as a constant expression. Generally, we do not want to constant + /// fold dllimport declarations unless they are used in a non-type template + /// parameter. + bool isNonConstDllImportDecl(const Decl *D) { + return EvalMode != EM_TemplateArgument && D->hasAttr(); + } + CallStackFrame *getCallFrame(unsigned CallIndex) { assert(CallIndex && "no call index in getCallFrame"); // We will eventually hit BottomFrame, which has Index 1, so Frame can't @@ -790,6 +802,7 @@ LLVM_FALLTHROUGH; case EM_ConstantExpression: case EM_PotentialConstantExpression: + case EM_TemplateArgument: case EM_ConstantExpressionUnevaluated: case EM_PotentialConstantExpressionUnevaluated: case EM_OffsetFold: @@ -882,6 +895,7 @@ return true; case EM_ConstantExpression: + case EM_TemplateArgument: case EM_ConstantExpressionUnevaluated: case EM_ConstantFold: case EM_OffsetFold: @@ -909,6 +923,7 @@ case EM_PotentialConstantExpression: case EM_PotentialConstantExpressionUnevaluated: case EM_ConstantExpression: + case EM_TemplateArgument: case EM_ConstantExpressionUnevaluated: return false; } @@ -936,6 +951,7 @@ return true; case EM_ConstantExpression: + case EM_TemplateArgument: case EM_ConstantExpressionUnevaluated: case EM_ConstantFold: case EM_IgnoreSideEffects: @@ -1686,7 +1702,7 @@ return false; // A dllimport variable never acts like a constant. - if (Var->hasAttr()) + if (Info.isNonConstDllImportDecl(Var)) return false; } if (const auto *FD = dyn_cast(VD)) { @@ -1700,7 +1716,7 @@ // 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()) + if (Info.getLangOpts().CPlusPlus && Info.isNonConstDllImportDecl(FD)) return false; } } @@ -1738,7 +1754,7 @@ const auto *FD = dyn_cast_or_null(Member); if (!FD) return true; - return FD->isVirtual() || !FD->hasAttr(); + return FD->isVirtual() || !Info.isNonConstDllImportDecl(FD); } /// Check that this core constant expression is of literal type, and if not, @@ -7684,6 +7700,7 @@ // size of the referenced object. switch (Info.EvalMode) { case EvalInfo::EM_ConstantExpression: + case EvalInfo::EM_TemplateArgument: case EvalInfo::EM_PotentialConstantExpression: case EvalInfo::EM_ConstantFold: case EvalInfo::EM_EvaluateForOverflow: @@ -10147,6 +10164,33 @@ return true; } +bool Expr::EvaluateAsCoreConstExpr(EvalResult &Result, QualType ParamType, + CCEKind CCE, const ASTContext &Ctx) const { + EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression; + if (CCE == CCEK_TemplateArg) + EM = EvalInfo::EM_TemplateArgument; + + // A reference must be an lvalue. + if (ParamType->isReferenceType()) { + EvalInfo Info(Ctx, Result, EM); + LValue LV; + if (!EvaluateLValue(this, LV, Info) || Result.HasSideEffects || + !CheckLValueConstantExpression( + Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV)) + return false; + + LV.moveInto(Result.Val); + return true; + } + + // Otherwise, evaluate it as an rvalue. + bool IsConst; + if (FastEvaluateAsRValue(this, Result, Ctx, IsConst)) + return IsConst; + EvalInfo Info(Ctx, Result, EM); + return ::EvaluateAsRValue(Info, this, Result.Val); +} + bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, const VarDecl *VD, SmallVectorImpl &Notes) const { Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -5297,7 +5297,7 @@ /// the converted expression, per C++11 [expr.const]p3. static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From, QualType T, APValue &Value, - Sema::CCEKind CCE, + CCEKind CCE, bool RequireInt) { assert(S.getLangOpts().CPlusPlus11 && "converted constant expression outside C++11"); @@ -5315,7 +5315,7 @@ // condition shall be a contextually converted constant expression of type // bool. ImplicitConversionSequence ICS = - CCE == Sema::CCEK_ConstexprIf + CCE == CCEK_ConstexprIf ? TryContextuallyConvertToBool(S, From) : TryCopyInitialization(S, From, T, /*SuppressUserConversions=*/false, @@ -5398,9 +5398,7 @@ Expr::EvalResult Eval; Eval.Diag = &Notes; - if ((T->isReferenceType() - ? !Result.get()->EvaluateAsLValue(Eval, S.Context) - : !Result.get()->EvaluateAsRValue(Eval, S.Context)) || + if (!Result.get()->EvaluateAsCoreConstExpr(Eval, T, CCE, S.Context) || (RequireInt && !Eval.Val.isInt())) { // The expression can't be folded, so we can't keep it at this position in // the AST. Index: clang/test/SemaCXX/dllimport-constexpr.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/dllimport-constexpr.cpp @@ -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 struct TemplateFnPtr { + static void getit() { FP(); } +}; +template struct TemplateFnRef { + static void getit() { FP(); } +}; +void instantiate1() { + TemplateFnPtr<&imported_func>::getit(); + TemplateFnRef::getit(); +} + +// Check variable template instantiation. +template struct TemplateIntPtr { + static int getit() { return *GI; } +}; +template struct TemplateIntRef { + static int getit() { return GI; } +}; +int instantiate2() { + int r = 0; + r += TemplateIntPtr<&imported_int>::getit(); + r += TemplateIntRef::getit(); + return r; +} + +// Member pointer instantiation. +template 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 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::g_fp(); +} Index: clang/test/SemaCXX/dllimport-memptr.cpp =================================================================== --- clang/test/SemaCXX/dllimport-memptr.cpp +++ clang/test/SemaCXX/dllimport-memptr.cpp @@ -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