diff options
Diffstat (limited to 'lib/Sema')
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 19 | ||||
-rw-r--r-- | lib/Sema/DelayedDiagnostic.cpp | 6 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 12 | ||||
-rw-r--r-- | lib/Sema/SemaCast.cpp | 94 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 35 | ||||
-rw-r--r-- | lib/Sema/SemaCoroutine.cpp | 56 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 40 | ||||
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 115 | ||||
-rw-r--r-- | lib/Sema/SemaDeclObjC.cpp | 5 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 130 | ||||
-rw-r--r-- | lib/Sema/SemaExprMember.cpp | 9 | ||||
-rw-r--r-- | lib/Sema/SemaLambda.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 108 | ||||
-rw-r--r-- | lib/Sema/SemaObjCProperty.cpp | 14 | ||||
-rw-r--r-- | lib/Sema/SemaOpenMP.cpp | 43 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 31 | ||||
-rw-r--r-- | lib/Sema/SemaPseudoObject.cpp | 9 | ||||
-rw-r--r-- | lib/Sema/SemaStmt.cpp | 67 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 252 |
19 files changed, 739 insertions, 308 deletions
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index fd2d07957c2b8..f83baa790b497 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -394,15 +394,21 @@ static bool hasThrowOutNonThrowingFunc(SourceLocation &OpLoc, CFG *BodyCFG) { static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc, const FunctionDecl *FD) { - if (!S.getSourceManager().isInSystemHeader(OpLoc)) { + if (!S.getSourceManager().isInSystemHeader(OpLoc) && + FD->getTypeSourceInfo()) { S.Diag(OpLoc, diag::warn_throw_in_noexcept_func) << FD; if (S.getLangOpts().CPlusPlus11 && (isa<CXXDestructorDecl>(FD) || FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || - FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) - S.Diag(FD->getLocation(), diag::note_throw_in_dtor); - else - S.Diag(FD->getLocation(), diag::note_throw_in_function); + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) { + if (const auto *Ty = FD->getTypeSourceInfo()->getType()-> + getAs<FunctionProtoType>()) + S.Diag(FD->getLocation(), diag::note_throw_in_dtor) + << !isa<CXXDestructorDecl>(FD) << !Ty->hasExceptionSpec() + << FD->getExceptionSpecSourceRange(); + } else + S.Diag(FD->getLocation(), diag::note_throw_in_function) + << FD->getExceptionSpecSourceRange(); } } @@ -420,8 +426,7 @@ static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, static bool isNoexcept(const FunctionDecl *FD) { const auto *FPT = FD->getType()->castAs<FunctionProtoType>(); - if (FPT->getExceptionSpecType() != EST_None && - FPT->isNothrow(FD->getASTContext())) + if (FPT->isNothrow(FD->getASTContext())) return true; return false; } diff --git a/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp index 2fa5718d4e9b5..3d321d561e60b 100644 --- a/lib/Sema/DelayedDiagnostic.cpp +++ b/lib/Sema/DelayedDiagnostic.cpp @@ -22,7 +22,8 @@ using namespace sema; DelayedDiagnostic DelayedDiagnostic::makeAvailability(AvailabilityResult AR, SourceLocation Loc, - const NamedDecl *D, + const NamedDecl *ReferringDecl, + const NamedDecl *OffendingDecl, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, StringRef Msg, @@ -31,7 +32,8 @@ DelayedDiagnostic::makeAvailability(AvailabilityResult AR, DD.Kind = Availability; DD.Triggered = false; DD.Loc = Loc; - DD.AvailabilityData.Decl = D; + DD.AvailabilityData.ReferringDecl = ReferringDecl; + DD.AvailabilityData.OffendingDecl = OffendingDecl; DD.AvailabilityData.UnknownObjCClass = UnknownObjCClass; DD.AvailabilityData.ObjCProperty = ObjCProperty; char *MessageData = nullptr; diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 34f5e26be8104..dc9f977d41ac2 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -705,6 +705,18 @@ void Sema::emitAndClearUnusedLocalTypedefWarnings() { UnusedLocalTypedefNameCandidates.clear(); } +/// This is called before the very first declaration in the translation unit +/// is parsed. Note that the ASTContext may have already injected some +/// declarations. +void Sema::ActOnStartOfTranslationUnit() { + if (getLangOpts().ModulesTS) { + // We start in the global module; all those declarations are implicitly + // module-private (though they do not have module linkage). + Context.getTranslationUnitDecl()->setModuleOwnershipKind( + Decl::ModuleOwnershipKind::ModulePrivate); + } +} + /// ActOnEndOfTranslationUnit - This is called at the very end of the /// translation unit when EOF is reached and all but the top-level scope is /// popped. diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 7d534263f4681..ba2049d8a606b 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -143,6 +143,9 @@ namespace { }; } +static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr, + QualType DestType); + // The Try functions attempt a specific way of casting. If they succeed, they // return TC_Success. If their way of casting is not appropriate for the given // arguments, they return TC_NotApplicable and *may* set diag to a diagnostic @@ -427,6 +430,10 @@ static void diagnoseBadCast(Sema &S, unsigned msg, CastType castType, /// the same kind of pointer (plain or to-member). Unlike the Sema function, /// this one doesn't care if the two pointers-to-member don't point into the /// same class. This is because CastsAwayConstness doesn't care. +/// And additionally, it handles C++ references. If both the types are +/// references, then their pointee types are returned, +/// else if only one of them is reference, it's pointee type is returned, +/// and the other type is returned as-is. static bool UnwrapDissimilarPointerTypes(QualType& T1, QualType& T2) { const PointerType *T1PtrType = T1->getAs<PointerType>(), *T2PtrType = T2->getAs<PointerType>(); @@ -475,6 +482,26 @@ static bool UnwrapDissimilarPointerTypes(QualType& T1, QualType& T2) { return true; } + const LValueReferenceType *T1RefType = T1->getAs<LValueReferenceType>(), + *T2RefType = T2->getAs<LValueReferenceType>(); + if (T1RefType && T2RefType) { + T1 = T1RefType->getPointeeType(); + T2 = T2RefType->getPointeeType(); + return true; + } + + if (T1RefType) { + T1 = T1RefType->getPointeeType(); + // T2 = T2; + return true; + } + + if (T2RefType) { + // T1 = T1; + T2 = T2RefType->getPointeeType(); + return true; + } + return false; } @@ -503,11 +530,13 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType, // the rules are non-trivial. So first we construct Tcv *...cv* as described // in C++ 5.2.11p8. assert((SrcType->isAnyPointerType() || SrcType->isMemberPointerType() || - SrcType->isBlockPointerType()) && + SrcType->isBlockPointerType() || + DestType->isLValueReferenceType()) && "Source type is not pointer or pointer to member."); assert((DestType->isAnyPointerType() || DestType->isMemberPointerType() || - DestType->isBlockPointerType()) && - "Destination type is not pointer or pointer to member."); + DestType->isBlockPointerType() || + DestType->isLValueReferenceType()) && + "Destination type is not pointer or pointer to member, or reference."); QualType UnwrappedSrcType = Self.Context.getCanonicalType(SrcType), UnwrappedDestType = Self.Context.getCanonicalType(DestType); @@ -2177,6 +2206,8 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, bool ListInitialization) { + assert(Self.getLangOpts().CPlusPlus); + // Handle placeholders. if (isPlaceholder()) { // C-style casts can resolve __unknown_any types. @@ -2580,30 +2611,42 @@ void CastOperation::CheckCStyleCast() { if (Kind == CK_BitCast) checkCastAlign(); +} + +/// DiagnoseCastQual - Warn whenever casts discards a qualifiers, be it either +/// const, volatile or both. +static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr, + QualType DestType) { + if (SrcExpr.isInvalid()) + return; + + QualType SrcType = SrcExpr.get()->getType(); + if (!((SrcType->isAnyPointerType() && DestType->isAnyPointerType()) || + DestType->isLValueReferenceType())) + return; - // -Wcast-qual QualType TheOffendingSrcType, TheOffendingDestType; Qualifiers CastAwayQualifiers; - if (SrcType->isAnyPointerType() && DestType->isAnyPointerType() && - CastsAwayConstness(Self, SrcType, DestType, true, false, - &TheOffendingSrcType, &TheOffendingDestType, - &CastAwayQualifiers)) { - int qualifiers = -1; - if (CastAwayQualifiers.hasConst() && CastAwayQualifiers.hasVolatile()) { - qualifiers = 0; - } else if (CastAwayQualifiers.hasConst()) { - qualifiers = 1; - } else if (CastAwayQualifiers.hasVolatile()) { - qualifiers = 2; - } - // This is a variant of int **x; const int **y = (const int **)x; - if (qualifiers == -1) - Self.Diag(SrcExpr.get()->getLocStart(), diag::warn_cast_qual2) << - SrcType << DestType; - else - Self.Diag(SrcExpr.get()->getLocStart(), diag::warn_cast_qual) << - TheOffendingSrcType << TheOffendingDestType << qualifiers; - } + if (!CastsAwayConstness(Self, SrcType, DestType, true, false, + &TheOffendingSrcType, &TheOffendingDestType, + &CastAwayQualifiers)) + return; + + int qualifiers = -1; + if (CastAwayQualifiers.hasConst() && CastAwayQualifiers.hasVolatile()) { + qualifiers = 0; + } else if (CastAwayQualifiers.hasConst()) { + qualifiers = 1; + } else if (CastAwayQualifiers.hasVolatile()) { + qualifiers = 2; + } + // This is a variant of int **x; const int **y = (const int **)x; + if (qualifiers == -1) + Self.Diag(SrcExpr.get()->getLocStart(), diag::warn_cast_qual2) + << SrcType << DestType; + else + Self.Diag(SrcExpr.get()->getLocStart(), diag::warn_cast_qual) + << TheOffendingSrcType << TheOffendingDestType << qualifiers; } ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, @@ -2624,6 +2667,9 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, if (Op.SrcExpr.isInvalid()) return ExprError(); + // -Wcast-qual + DiagnoseCastQual(Op.Self, Op.SrcExpr, Op.DestType); + return Op.complete(CStyleCastExpr::Create(Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(), &Op.BasePath, CastTypeInfo, LPLoc, RPLoc)); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 845c4bf61b7aa..8446601334ee8 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -309,7 +309,8 @@ static bool SemaOpenCLBuiltinKernelWorkGroupSize(Sema &S, CallExpr *TheCall) { Expr *BlockArg = TheCall->getArg(0); if (!isBlockPointer(BlockArg)) { S.Diag(BlockArg->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) << "block"; + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; return true; } return checkOpenCLBlockArgs(S, BlockArg); @@ -394,24 +395,24 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { // First argument always needs to be a queue_t type. if (!Arg0->getType()->isQueueT()) { S.Diag(TheCall->getArg(0)->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) - << S.Context.OCLQueueTy; + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << S.Context.OCLQueueTy; return true; } // Second argument always needs to be a kernel_enqueue_flags_t enum value. if (!Arg1->getType()->isIntegerType()) { S.Diag(TheCall->getArg(1)->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) - << "'kernel_enqueue_flags_t' (i.e. uint)"; + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "'kernel_enqueue_flags_t' (i.e. uint)"; return true; } // Third argument is always an ndrange_t type. if (Arg2->getType().getUnqualifiedType().getAsString() != "ndrange_t") { S.Diag(TheCall->getArg(2)->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) - << "'ndrange_t'"; + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "'ndrange_t'"; return true; } @@ -420,8 +421,8 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { if (NumArgs == 4) { // check that the last argument is the right block type. if (!isBlockPointer(Arg3)) { - S.Diag(Arg3->getLocStart(), diag::err_opencl_enqueue_kernel_expected_type) - << "block"; + S.Diag(Arg3->getLocStart(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; return true; } // we have a block type, check the prototype @@ -443,8 +444,8 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { // check common block argument. Expr *Arg6 = TheCall->getArg(6); if (!isBlockPointer(Arg6)) { - S.Diag(Arg6->getLocStart(), diag::err_opencl_enqueue_kernel_expected_type) - << "block"; + S.Diag(Arg6->getLocStart(), diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "block"; return true; } if (checkOpenCLBlockArgs(S, Arg6)) @@ -453,8 +454,8 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { // Forth argument has to be any integer type. if (!Arg3->getType()->isIntegerType()) { S.Diag(TheCall->getArg(3)->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) - << "integer"; + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << "integer"; return true; } // check remaining common arguments. @@ -466,7 +467,8 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { Expr::NPC_ValueDependentIsNotNull) && !Arg4->getType()->getPointeeOrArrayElementType()->isClkEventT()) { S.Diag(TheCall->getArg(4)->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << S.Context.getPointerType(S.Context.OCLClkEventTy); return true; } @@ -477,7 +479,8 @@ static bool SemaOpenCLBuiltinEnqueueKernel(Sema &S, CallExpr *TheCall) { !(Arg5->getType()->isPointerType() && Arg5->getType()->getPointeeType()->isClkEventT())) { S.Diag(TheCall->getArg(5)->getLocStart(), - diag::err_opencl_enqueue_kernel_expected_type) + diag::err_opencl_builtin_expected_type) + << TheCall->getDirectCallee() << S.Context.getPointerType(S.Context.OCLClkEventTy); return true; } @@ -12094,6 +12097,8 @@ void Sema::RefersToMemberWithReducedAlignment( if (ME->isArrow()) BaseType = BaseType->getPointeeType(); RecordDecl *RD = BaseType->getAs<RecordType>()->getDecl(); + if (RD->isInvalidDecl()) + return; ValueDecl *MD = ME->getMemberDecl(); auto *FD = dyn_cast<FieldDecl>(MD); diff --git a/lib/Sema/SemaCoroutine.cpp b/lib/Sema/SemaCoroutine.cpp index b05c0998d3dd7..dc7d8e4e9cec3 100644 --- a/lib/Sema/SemaCoroutine.cpp +++ b/lib/Sema/SemaCoroutine.cpp @@ -43,9 +43,10 @@ static bool lookupMember(Sema &S, const char *Name, CXXRecordDecl *RD, /// Look up the std::coroutine_traits<...>::promise_type for the given /// function type. -static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, - SourceLocation KwLoc, - SourceLocation FuncLoc) { +static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD, + SourceLocation KwLoc) { + const FunctionProtoType *FnType = FD->getType()->castAs<FunctionProtoType>(); + const SourceLocation FuncLoc = FD->getLocation(); // FIXME: Cache std::coroutine_traits once we've found it. NamespaceDecl *StdExp = S.lookupStdExperimentalNamespace(); if (!StdExp) { @@ -71,16 +72,35 @@ static QualType lookupPromiseType(Sema &S, const FunctionProtoType *FnType, return QualType(); } - // Form template argument list for coroutine_traits<R, P1, P2, ...>. + // Form template argument list for coroutine_traits<R, P1, P2, ...> according + // to [dcl.fct.def.coroutine]3 TemplateArgumentListInfo Args(KwLoc, KwLoc); - Args.addArgument(TemplateArgumentLoc( - TemplateArgument(FnType->getReturnType()), - S.Context.getTrivialTypeSourceInfo(FnType->getReturnType(), KwLoc))); - // FIXME: If the function is a non-static member function, add the type - // of the implicit object parameter before the formal parameters. - for (QualType T : FnType->getParamTypes()) + auto AddArg = [&](QualType T) { Args.addArgument(TemplateArgumentLoc( TemplateArgument(T), S.Context.getTrivialTypeSourceInfo(T, KwLoc))); + }; + AddArg(FnType->getReturnType()); + // If the function is a non-static member function, add the type + // of the implicit object parameter before the formal parameters. + if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) { + if (MD->isInstance()) { + // [over.match.funcs]4 + // For non-static member functions, the type of the implicit object + // parameter is + // -- "lvalue reference to cv X" for functions declared without a + // ref-qualifier or with the & ref-qualifier + // -- "rvalue reference to cv X" for functions declared with the && + // ref-qualifier + QualType T = + MD->getThisType(S.Context)->getAs<PointerType>()->getPointeeType(); + T = FnType->getRefQualifier() == RQ_RValue + ? S.Context.getRValueReferenceType(T) + : S.Context.getLValueReferenceType(T, /*SpelledAsLValue*/ true); + AddArg(T); + } + } + for (QualType T : FnType->getParamTypes()) + AddArg(T); // Build the template-id. QualType CoroTrait = @@ -424,12 +444,16 @@ static ExprResult buildPromiseCall(Sema &S, VarDecl *Promise, VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) { assert(isa<FunctionDecl>(CurContext) && "not in a function scope"); auto *FD = cast<FunctionDecl>(CurContext); + bool IsThisDependentType = [&] { + if (auto *MD = dyn_cast_or_null<CXXMethodDecl>(FD)) + return MD->isInstance() && MD->getThisType(Context)->isDependentType(); + else + return false; + }(); - QualType T = - FD->getType()->isDependentType() - ? Context.DependentTy - : lookupPromiseType(*this, FD->getType()->castAs<FunctionProtoType>(), - Loc, FD->getLocation()); + QualType T = FD->getType()->isDependentType() || IsThisDependentType + ? Context.DependentTy + : lookupPromiseType(*this, FD, Loc); if (T.isNull()) return nullptr; @@ -721,8 +745,6 @@ static Expr *buildStdNoThrowDeclRef(Sema &S, SourceLocation Loc) { return nullptr; } - // FIXME: Mark the variable as ODR used. This currently does not work - // likely due to the scope at in which this function is called. auto *VD = Result.getAsSingle<VarDecl>(); if (!VD) { Result.suppressDiagnostics(); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ef8a408f90de9..31b24f91c1d93 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1998,8 +1998,7 @@ static void filterNonConflictingPreviousTypedefDecls(Sema &S, // If both declarations give a tag declaration a typedef name for linkage // purposes, then they declare the same entity. - if (S.getLangOpts().CPlusPlus && - OldTD->getAnonDeclWithTypedefName(/*AnyRedecl*/true) && + if (OldTD->getAnonDeclWithTypedefName(/*AnyRedecl*/true) && Decl->getAnonDeclWithTypedefName()) continue; } @@ -2117,7 +2116,7 @@ void Sema::MergeTypedefNameDecl(Scope *S, TypedefNameDecl *New, auto *OldTag = OldTD->getAnonDeclWithTypedefName(/*AnyRedecl*/true); auto *NewTag = New->getAnonDeclWithTypedefName(); NamedDecl *Hidden = nullptr; - if (getLangOpts().CPlusPlus && OldTag && NewTag && + if (OldTag && NewTag && OldTag->getCanonicalDecl() != NewTag->getCanonicalDecl() && !hasVisibleDefinition(OldTag, &Hidden)) { // There is a definition of this tag, but it is not visible. Use it @@ -16055,8 +16054,6 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, return nullptr; } - // FIXME: Create a ModuleDecl and return it. - // FIXME: Most of this work should be done by the preprocessor rather than // here, in order to support macro import. @@ -16070,6 +16067,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, ModuleName += Piece.first->getName(); } + // FIXME: If we've already seen a module-declaration, report an error. + // If a module name was explicitly specified on the command line, it must be // correct. if (!getLangOpts().CurrentModule.empty() && @@ -16082,6 +16081,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName; auto &Map = PP.getHeaderSearchInfo().getModuleMap(); + Module *Mod; switch (MDK) { case ModuleDeclKind::Module: { @@ -16100,12 +16100,9 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, } // Create a Module for the module that we're defining. - Module *Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName); + Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName); assert(Mod && "module creation should not fail"); - - // Enter the semantic scope of the module. - ActOnModuleBegin(ModuleLoc, Mod); - return nullptr; + break; } case ModuleDeclKind::Partition: @@ -16115,14 +16112,26 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, case ModuleDeclKind::Implementation: std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc( PP.getIdentifierInfo(ModuleName), Path[0].second); - - DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, ModuleNameLoc); - if (Import.isInvalid()) + Mod = getModuleLoader().loadModule(ModuleLoc, Path, Module::AllVisible, + /*IsIncludeDirective=*/false); + if (!Mod) return nullptr; - return ConvertDeclToDeclGroup(Import.get()); + break; } - llvm_unreachable("unexpected module decl kind"); + // Enter the semantic scope of the module. + ModuleScopes.push_back({}); + ModuleScopes.back().Module = Mod; + ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules); + VisibleModules.setVisible(Mod, ModuleLoc); + + // From now on, we have an owning module for all declarations we see. + // However, those declarations are module-private unless explicitly + // exported. + Context.getTranslationUnitDecl()->setLocalOwningModule(Mod); + + // FIXME: Create a ModuleDecl. + return nullptr; } DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, @@ -16311,6 +16320,7 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, CurContext->addDecl(D); PushDeclContext(S, D); + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::VisibleWhenImported); return D; } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 1929bc5391889..5fb79a6bf6307 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -6929,8 +6929,34 @@ shouldDiagnoseAvailabilityByDefault(const ASTContext &Context, DeclVersion >= ForceAvailabilityFromVersion; } +static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { + for (Decl *Ctx = OrigCtx; Ctx; + Ctx = cast_or_null<Decl>(Ctx->getDeclContext())) { + if (isa<TagDecl>(Ctx) || isa<FunctionDecl>(Ctx) || isa<ObjCMethodDecl>(Ctx)) + return cast<NamedDecl>(Ctx); + if (auto *CD = dyn_cast<ObjCContainerDecl>(Ctx)) { + if (auto *Imp = dyn_cast<ObjCImplDecl>(Ctx)) + return Imp->getClassInterface(); + return CD; + } + } + + return dyn_cast<NamedDecl>(OrigCtx); +} + +/// Actually emit an availability diagnostic for a reference to an unavailable +/// decl. +/// +/// \param Ctx The context that the reference occurred in +/// \param ReferringDecl The exact declaration that was referenced. +/// \param OffendingDecl A related decl to \c ReferringDecl that has an +/// availability attribute corrisponding to \c K attached to it. Note that this +/// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and +/// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl +/// and OffendingDecl is the EnumDecl. static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, - Decl *Ctx, const NamedDecl *D, + Decl *Ctx, const NamedDecl *ReferringDecl, + const NamedDecl *OffendingDecl, StringRef Message, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass, const ObjCPropertyDecl *ObjCProperty, @@ -6938,7 +6964,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // Diagnostics for deprecated or unavailable. unsigned diag, diag_message, diag_fwdclass_message; unsigned diag_available_here = diag::note_availability_specified_here; - SourceLocation NoteLocation = D->getLocation(); + SourceLocation NoteLocation = OffendingDecl->getLocation(); // Matches 'diag::note_property_attribute' options. unsigned property_note_select; @@ -6947,7 +6973,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, unsigned available_here_select_kind; VersionTuple DeclVersion; - if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, D)) + if (const AvailabilityAttr *AA = getAttrForPlatform(S.Context, OffendingDecl)) DeclVersion = AA->getIntroduced(); if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) @@ -6961,7 +6987,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; property_note_select = /* deprecated */ 0; available_here_select_kind = /* deprecated */ 2; - if (const auto *attr = D->getAttr<DeprecatedAttr>()) + if (const auto *attr = OffendingDecl->getAttr<DeprecatedAttr>()) NoteLocation = attr->getLocation(); break; @@ -6973,13 +6999,14 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, property_note_select = /* unavailable */ 1; available_here_select_kind = /* unavailable */ 0; - if (auto attr = D->getAttr<UnavailableAttr>()) { + if (auto attr = OffendingDecl->getAttr<UnavailableAttr>()) { if (attr->isImplicit() && attr->getImplicitReason()) { // Most of these failures are due to extra restrictions in ARC; // reflect that in the primary diagnostic when applicable. auto flagARCError = [&] { if (S.getLangOpts().ObjCAutoRefCount && - S.getSourceManager().isInSystemHeader(D->getLocation())) + S.getSourceManager().isInSystemHeader( + OffendingDecl->getLocation())) diag = diag::err_unavailable_in_arc; }; @@ -7022,7 +7049,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // not specified for deployment targets >= to iOS 11 or equivalent or // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or // later. - const AvailabilityAttr *AA = getAttrForPlatform(S.getASTContext(), D); + const AvailabilityAttr *AA = + getAttrForPlatform(S.getASTContext(), OffendingDecl); VersionTuple Introduced = AA->getIntroduced(); bool NewWarning = shouldDiagnoseAvailabilityByDefault( S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), @@ -7045,9 +7073,9 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, CharSourceRange UseRange; StringRef Replacement; if (K == AR_Deprecated) { - if (auto attr = D->getAttr<DeprecatedAttr>()) + if (auto attr = OffendingDecl->getAttr<DeprecatedAttr>()) Replacement = attr->getReplacement(); - if (auto attr = getAttrForPlatform(S.Context, D)) + if (auto attr = getAttrForPlatform(S.Context, OffendingDecl)) Replacement = attr->getReplacement(); if (!Replacement.empty()) @@ -7056,21 +7084,21 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } if (!Message.empty()) { - S.Diag(Loc, diag_message) << D << Message + S.Diag(Loc, diag_message) << ReferringDecl << Message << (UseRange.isValid() ? FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else if (!UnknownObjCClass) { - S.Diag(Loc, diag) << D + S.Diag(Loc, diag) << ReferringDecl << (UseRange.isValid() ? FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else { - S.Diag(Loc, diag_fwdclass_message) << D + S.Diag(Loc, diag_fwdclass_message) << ReferringDecl << (UseRange.isValid() ? FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); @@ -7078,16 +7106,16 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, // The declaration can have multiple availability attributes, we are looking // at one of them. - const AvailabilityAttr *A = getAttrForPlatform(S.Context, D); + const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); if (A && A->isInherited()) { - for (const Decl *Redecl = D->getMostRecentDecl(); Redecl; + for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; Redecl = Redecl->getPreviousDecl()) { const AvailabilityAttr *AForRedecl = getAttrForPlatform(S.Context, Redecl); if (AForRedecl && !AForRedecl->isInherited()) { // If D is a declaration with inherited attributes, the note should // point to the declaration with actual attributes. - S.Diag(Redecl->getLocation(), diag_available_here) << D + S.Diag(Redecl->getLocation(), diag_available_here) << OffendingDecl << available_here_select_kind; break; } @@ -7095,10 +7123,19 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } else S.Diag(NoteLocation, diag_available_here) - << D << available_here_select_kind; + << OffendingDecl << available_here_select_kind; if (K == AR_NotYetIntroduced) - S.Diag(Loc, diag::note_partial_availability_silence) << D; + if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { + if (auto *TD = dyn_cast<TagDecl>(Enclosing)) + if (TD->getDeclName().isEmpty()) { + S.Diag(TD->getLocation(), diag::note_partial_availability_silence) + << /*Anonymous*/1 << TD->getKindName(); + return; + } + S.Diag(Enclosing->getLocation(), diag::note_partial_availability_silence) + << /*Named*/0 << Enclosing; + } } static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, @@ -7108,9 +7145,9 @@ static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, DD.Triggered = true; DoEmitAvailabilityWarning( - S, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityDecl(), - DD.getAvailabilityMessage(), DD.Loc, DD.getUnknownObjCClass(), - DD.getObjCProperty(), false); + S, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(), + DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(), DD.Loc, + DD.getUnknownObjCClass(), DD.getObjCProperty(), false); } void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { @@ -7169,22 +7206,25 @@ void Sema::redelayDiagnostics(DelayedDiagnosticPool &pool) { } void Sema::EmitAvailabilityWarning(AvailabilityResult AR, - NamedDecl *D, StringRef Message, - SourceLocation Loc, + const NamedDecl *ReferringDecl, + const NamedDecl *OffendingDecl, + StringRef Message, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass, - const ObjCPropertyDecl *ObjCProperty, + const ObjCPropertyDecl *ObjCProperty, bool ObjCPropertyAccess) { // Delay if we're currently parsing a declaration. if (DelayedDiagnostics.shouldDelayDiagnostics()) { - DelayedDiagnostics.add(DelayedDiagnostic::makeAvailability( - AR, Loc, D, UnknownObjCClass, ObjCProperty, Message, - ObjCPropertyAccess)); + DelayedDiagnostics.add( + DelayedDiagnostic::makeAvailability( + AR, Loc, ReferringDecl, OffendingDecl, UnknownObjCClass, + ObjCProperty, Message, ObjCPropertyAccess)); return; } Decl *Ctx = cast<Decl>(getCurLexicalContext()); - DoEmitAvailabilityWarning(*this, AR, Ctx, D, Message, Loc, UnknownObjCClass, - ObjCProperty, ObjCPropertyAccess); + DoEmitAvailabilityWarning(*this, AR, Ctx, ReferringDecl, OffendingDecl, + Message, Loc, UnknownObjCClass, ObjCProperty, + ObjCPropertyAccess); } namespace { @@ -7336,19 +7376,21 @@ public: void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( NamedDecl *D, SourceRange Range) { - - VersionTuple ContextVersion = AvailabilityStack.back(); - if (AvailabilityResult Result = - SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr)) { + AvailabilityResult Result; + const NamedDecl *OffendingDecl; + std::tie(Result, OffendingDecl) = + SemaRef.ShouldDiagnoseAvailabilityOfDecl(D, nullptr); + if (Result != AR_Available) { // All other diagnostic kinds have already been handled in // DiagnoseAvailabilityOfDecl. if (Result != AR_NotYetIntroduced) return; - const AvailabilityAttr *AA = getAttrForPlatform(SemaRef.getASTContext(), D); + const AvailabilityAttr *AA = + getAttrForPlatform(SemaRef.getASTContext(), OffendingDecl); VersionTuple Introduced = AA->getIntroduced(); - if (ContextVersion >= Introduced) + if (AvailabilityStack.back() >= Introduced) return; // If the context of this function is less available than D, we should not @@ -7373,8 +7415,9 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability( SemaRef.getASTContext().getTargetInfo().getPlatformName()) << Introduced.getAsString(); - SemaRef.Diag(D->getLocation(), diag::note_availability_specified_here) - << D << /* partial */ 3; + SemaRef.Diag(OffendingDecl->getLocation(), + diag::note_availability_specified_here) + << OffendingDecl << /* partial */ 3; auto FixitDiag = SemaRef.Diag(Range.getBegin(), diag::note_unguarded_available_silence) diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 2c8080dbf02b9..778b8062f68cb 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -458,7 +458,10 @@ static void diagnoseUseOfProtocols(Sema &TheSema, // Diagnose availability in the context of the ObjC container. Sema::ContextRAII SavedContext(TheSema, CD); for (unsigned i = 0; i < NumProtoRefs; ++i) { - (void)TheSema.DiagnoseUseOfDecl(ProtoRefs[i], ProtoLocs[i]); + (void)TheSema.DiagnoseUseOfDecl(ProtoRefs[i], ProtoLocs[i], + /*UnknownObjCClass=*/nullptr, + /*ObjCPropertyAccess=*/false, + /*AvoidPartialAvailabilityChecks=*/true); } } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index f49df6b3216df..8016bf99889f7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -87,24 +87,9 @@ static void DiagnoseUnusedOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc) { } } -static bool HasRedeclarationWithoutAvailabilityInCategory(const Decl *D) { - const auto *OMD = dyn_cast<ObjCMethodDecl>(D); - if (!OMD) - return false; - const ObjCInterfaceDecl *OID = OMD->getClassInterface(); - if (!OID) - return false; - - for (const ObjCCategoryDecl *Cat : OID->visible_categories()) - if (ObjCMethodDecl *CatMeth = - Cat->getMethod(OMD->getSelector(), OMD->isInstanceMethod())) - if (!CatMeth->hasAttr<AvailabilityAttr>()) - return true; - return false; -} - -AvailabilityResult -Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { +std::pair<AvailabilityResult, const NamedDecl *> +Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, + std::string *Message) { AvailabilityResult Result = D->getAvailability(Message); // For typedefs, if the typedef declaration appears available look @@ -121,78 +106,61 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(NamedDecl *&D, std::string *Message) { } // Forward class declarations get their attributes from their definition. - if (ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { + if (const ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(D)) { if (IDecl->getDefinition()) { D = IDecl->getDefinition(); Result = D->getAvailability(Message); } } - if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) + if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) if (Result == AR_Available) { const DeclContext *DC = ECD->getDeclContext(); - if (const EnumDecl *TheEnumDecl = dyn_cast<EnumDecl>(DC)) + if (const auto *TheEnumDecl = dyn_cast<EnumDecl>(DC)) { Result = TheEnumDecl->getAvailability(Message); + D = TheEnumDecl; + } } - if (Result == AR_NotYetIntroduced) { - // Don't do this for enums, they can't be redeclared. - if (isa<EnumConstantDecl>(D) || isa<EnumDecl>(D)) - return AR_Available; - - bool Warn = !D->getAttr<AvailabilityAttr>()->isInherited(); - // Objective-C method declarations in categories are not modelled as - // redeclarations, so manually look for a redeclaration in a category - // if necessary. - if (Warn && HasRedeclarationWithoutAvailabilityInCategory(D)) - Warn = false; - // In general, D will point to the most recent redeclaration. However, - // for `@class A;` decls, this isn't true -- manually go through the - // redecl chain in that case. - if (Warn && isa<ObjCInterfaceDecl>(D)) - for (Decl *Redecl = D->getMostRecentDecl(); Redecl && Warn; - Redecl = Redecl->getPreviousDecl()) - if (!Redecl->hasAttr<AvailabilityAttr>() || - Redecl->getAttr<AvailabilityAttr>()->isInherited()) - Warn = false; - - return Warn ? AR_NotYetIntroduced : AR_Available; - } - - return Result; + return {Result, D}; } static void DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass, - bool ObjCPropertyAccess) { + bool ObjCPropertyAccess, + bool AvoidPartialAvailabilityChecks = false) { std::string Message; + AvailabilityResult Result; + const NamedDecl* OffendingDecl; // See if this declaration is unavailable, deprecated, or partial. - if (AvailabilityResult Result = - S.ShouldDiagnoseAvailabilityOfDecl(D, &Message)) { + std::tie(Result, OffendingDecl) = S.ShouldDiagnoseAvailabilityOfDecl(D, &Message); + if (Result == AR_Available) + return; - if (Result == AR_NotYetIntroduced) { - if (S.getCurFunctionOrMethodDecl()) { - S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true; - return; - } else if (S.getCurBlock() || S.getCurLambda()) { - S.getCurFunction()->HasPotentialAvailabilityViolations = true; - return; - } + if (Result == AR_NotYetIntroduced) { + if (AvoidPartialAvailabilityChecks) + return; + if (S.getCurFunctionOrMethodDecl()) { + S.getEnclosingFunction()->HasPotentialAvailabilityViolations = true; + return; + } else if (S.getCurBlock() || S.getCurLambda()) { + S.getCurFunction()->HasPotentialAvailabilityViolations = true; + return; } + } - const ObjCPropertyDecl *ObjCPDecl = nullptr; - if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { - if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { - AvailabilityResult PDeclResult = PD->getAvailability(nullptr); - if (PDeclResult == Result) - ObjCPDecl = PD; - } + const ObjCPropertyDecl *ObjCPDecl = nullptr; + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { + AvailabilityResult PDeclResult = PD->getAvailability(nullptr); + if (PDeclResult == Result) + ObjCPDecl = PD; } - - S.EmitAvailabilityWarning(Result, D, Message, Loc, UnknownObjCClass, - ObjCPDecl, ObjCPropertyAccess); } + + S.EmitAvailabilityWarning(Result, D, OffendingDecl, Message, Loc, + UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); } /// \brief Emit a note explaining that this function is deleted. @@ -310,7 +278,8 @@ void Sema::MaybeSuggestAddingStaticToDecl(const FunctionDecl *Cur) { /// bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass, - bool ObjCPropertyAccess) { + bool ObjCPropertyAccess, + bool AvoidPartialAvailabilityChecks) { if (getLangOpts().CPlusPlus && isa<FunctionDecl>(D)) { // If there were any diagnostics suppressed by template argument deduction, // emit them now. @@ -395,7 +364,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, } DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass, - ObjCPropertyAccess); + ObjCPropertyAccess, + AvoidPartialAvailabilityChecks); DiagnoseUnusedOfDecl(*this, D, Loc); @@ -14695,24 +14665,24 @@ static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, ME->performsVirtualDispatch(SemaRef.getLangOpts()); if (!IsVirtualCall) return; - const Expr *Base = ME->getBase(); - const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType(); - if (!MostDerivedClassDecl) - return; - CXXMethodDecl *DM = MD->getCorrespondingMethodInClass(MostDerivedClassDecl); - if (!DM || DM->isPure()) - return; - SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse); + + // If it's possible to devirtualize the call, mark the called function + // referenced. + CXXMethodDecl *DM = MD->getDevirtualizedMethod( + ME->getBase(), SemaRef.getLangOpts().AppleKext); + if (DM) + SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse); } /// \brief Perform reference-marking and odr-use handling for a DeclRefExpr. -void Sema::MarkDeclRefReferenced(DeclRefExpr *E) { +void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) { // TODO: update this with DR# once a defect report is filed. // C++11 defect. The address of a pure member should not be an ODR use, even // if it's a qualified reference. bool OdrUse = true; - if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl())) - if (Method->isVirtual()) + if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl())) + if (Method->isVirtual() && + !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) OdrUse = false; MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse); } diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index b18de7e946865..c3d0e2db76b67 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -1842,10 +1842,6 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, FoundDecl, Field); if (Base.isInvalid()) return ExprError(); - MemberExpr *ME = - BuildMemberExpr(*this, Context, Base.get(), IsArrow, OpLoc, SS, - /*TemplateKWLoc=*/SourceLocation(), Field, FoundDecl, - MemberNameInfo, MemberType, VK, OK); // Build a reference to a private copy for non-static data members in // non-static member functions, privatized by OpenMP constructs. @@ -1855,7 +1851,10 @@ Sema::BuildFieldReferenceExpr(Expr *BaseExpr, bool IsArrow, if (auto *PrivateCopy = IsOpenMPCapturedDecl(Field)) return getOpenMPCapturedExpr(PrivateCopy, VK, OK, OpLoc); } - return ME; + + return BuildMemberExpr(*this, Context, Base.get(), IsArrow, OpLoc, SS, + /*TemplateKWLoc=*/SourceLocation(), Field, FoundDecl, + MemberNameInfo, MemberType, VK, OK); } /// Builds an implicit member access expression. The current context diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index d6b70610d461c..46f2ba3760068 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -1595,7 +1595,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, ContainsUnexpandedParameterPack); // If the lambda expression's call operator is not explicitly marked constexpr // and we are not in a dependent context, analyze the call operator to infer - // its constexpr-ness, supressing diagnostics while doing so. + // its constexpr-ness, suppressing diagnostics while doing so. if (getLangOpts().CPlusPlus1z && !CallOperator->isInvalidDecl() && !CallOperator->isConstexpr() && !isa<CoroutineBodyStmt>(CallOperator->getBody()) && diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 2e7fb875a2769..85596ed52e9d6 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1395,6 +1395,20 @@ bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) { return false; } +bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) { + // FIXME: When not in local visibility mode, we can't tell the difference + // between a declaration being visible because we merged a local copy of + // the same declaration into it, and it being visible because its owning + // module is visible. + if (Def->getModuleOwnershipKind() == Decl::ModuleOwnershipKind::Visible && + getLangOpts().ModulesLocalVisibility) + return true; + for (Module *Merged : Context.getModulesWithMergedDefinition(Def)) + if (Merged->getTopLevelModuleName() == getLangOpts().CurrentModule) + return true; + return false; +} + template<typename ParmDecl> static bool hasVisibleDefaultArgument(Sema &S, const ParmDecl *D, @@ -1495,23 +1509,40 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { assert(D->isHidden() && "should not call this: not in slow case"); Module *DeclModule = SemaRef.getOwningModule(D); - assert(DeclModule && "hidden decl not from a module"); - - // If the owning module is visible, and the decl is not module private, - // then the decl is visible too. (Module private is ignored within the same - // top-level module.) - // FIXME: Check the owning module for module-private declarations rather than - // assuming "same AST file" is the same thing as "same module". - if ((!D->isFromASTFile() || !D->isModulePrivate()) && - (SemaRef.isModuleVisible(DeclModule) || - SemaRef.hasVisibleMergedDefinition(D))) - return true; + if (!DeclModule) { + // A module-private declaration with no owning module means this is in the + // global module in the C++ Modules TS. This is visible within the same + // translation unit only. + // FIXME: Don't assume that "same translation unit" means the same thing + // as "not from an AST file". + assert(D->isModulePrivate() && "hidden decl has no module"); + if (!D->isFromASTFile() || SemaRef.hasMergedDefinitionInCurrentModule(D)) + return true; + } else { + // If the owning module is visible, and the decl is not module private, + // then the decl is visible too. (Module private is ignored within the same + // top-level module.) + if (D->isModulePrivate() + ? DeclModule->getTopLevelModuleName() == + SemaRef.getLangOpts().CurrentModule || + SemaRef.hasMergedDefinitionInCurrentModule(D) + : SemaRef.isModuleVisible(DeclModule) || + SemaRef.hasVisibleMergedDefinition(D)) + return true; + } + + // Determine whether a decl context is a file context for the purpose of + // visibility. This looks through some (export and linkage spec) transparent + // contexts, but not others (enums). + auto IsEffectivelyFileContext = [](const DeclContext *DC) { + return DC->isFileContext() || isa<LinkageSpecDecl>(DC) || + isa<ExportDecl>(DC); + }; - // If this declaration is not at namespace scope nor module-private, + // If this declaration is not at namespace scope // then it is visible if its lexical parent has a visible definition. DeclContext *DC = D->getLexicalDeclContext(); - if (!D->isModulePrivate() && DC && !DC->isFileContext() && - !isa<LinkageSpecDecl>(DC) && !isa<ExportDecl>(DC)) { + if (DC && !IsEffectivelyFileContext(DC)) { // For a parameter, check whether our current template declaration's // lexical context is visible, not whether there's some other visible // definition of it, because parameters aren't "within" the definition. @@ -1519,32 +1550,45 @@ bool LookupResult::isVisibleSlow(Sema &SemaRef, NamedDecl *D) { // In C++ we need to check for a visible definition due to ODR merging, // and in C we must not because each declaration of a function gets its own // set of declarations for tags in prototype scope. - if ((D->isTemplateParameter() || isa<ParmVarDecl>(D) - || (isa<FunctionDecl>(DC) && !SemaRef.getLangOpts().CPlusPlus)) - ? isVisible(SemaRef, cast<NamedDecl>(DC)) - : SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC))) { - if (SemaRef.CodeSynthesisContexts.empty() && - // FIXME: Do something better in this case. - !SemaRef.getLangOpts().ModulesLocalVisibility) { - // Cache the fact that this declaration is implicitly visible because - // its parent has a visible definition. - D->setVisibleDespiteOwningModule(); - } - return true; + bool VisibleWithinParent; + if (D->isTemplateParameter() || isa<ParmVarDecl>(D) || + (isa<FunctionDecl>(DC) && !SemaRef.getLangOpts().CPlusPlus)) + VisibleWithinParent = isVisible(SemaRef, cast<NamedDecl>(DC)); + else if (D->isModulePrivate()) { + // A module-private declaration is only visible if an enclosing lexical + // parent was merged with another definition in the current module. + VisibleWithinParent = false; + do { + if (SemaRef.hasMergedDefinitionInCurrentModule(cast<NamedDecl>(DC))) { + VisibleWithinParent = true; + break; + } + DC = DC->getLexicalParent(); + } while (!IsEffectivelyFileContext(DC)); + } else { + VisibleWithinParent = SemaRef.hasVisibleDefinition(cast<NamedDecl>(DC)); } - return false; + + if (VisibleWithinParent && SemaRef.CodeSynthesisContexts.empty() && + // FIXME: Do something better in this case. + !SemaRef.getLangOpts().ModulesLocalVisibility) { + // Cache the fact that this declaration is implicitly visible because + // its parent has a visible definition. + D->setVisibleDespiteOwningModule(); + } + return VisibleWithinParent; } + // FIXME: All uses of DeclModule below this point should also check merged + // modules. + if (!DeclModule) + return false; + // Find the extra places where we need to look. llvm::DenseSet<Module*> &LookupModules = SemaRef.getLookupModules(); if (LookupModules.empty()) return false; - if (!DeclModule) { - DeclModule = SemaRef.getOwningModule(D); - assert(DeclModule && "hidden decl not from a module"); - } - // If our lookup set contains the decl's module, it's visible. if (LookupModules.count(DeclModule)) return true; diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 6c57164548742..62a771bcffa0e 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1676,8 +1676,9 @@ static bool SuperClassImplementsProperty(ObjCInterfaceDecl *IDecl, /// \brief Default synthesizes all properties which must be synthesized /// in class's \@implementation. -void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, - ObjCInterfaceDecl *IDecl) { +void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl *IMPDecl, + ObjCInterfaceDecl *IDecl, + SourceLocation AtEnd) { ObjCInterfaceDecl::PropertyMap PropMap; ObjCInterfaceDecl::PropertyDeclOrder PropertyOrder; IDecl->collectPropertiesToImplement(PropMap, PropertyOrder); @@ -1725,6 +1726,10 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, diag::warn_auto_synthesizing_protocol_property) << Prop << Proto; Diag(Prop->getLocation(), diag::note_property_declare); + std::string FixIt = + (Twine("@synthesize ") + Prop->getName() + ";\n\n").str(); + Diag(AtEnd, diag::note_add_synthesize_directive) + << FixItHint::CreateInsertion(AtEnd, FixIt); } continue; } @@ -1764,7 +1769,8 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, } } -void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) { +void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D, + SourceLocation AtEnd) { if (!LangOpts.ObjCDefaultSynthProperties || LangOpts.ObjCRuntime.isFragile()) return; ObjCImplementationDecl *IC=dyn_cast_or_null<ObjCImplementationDecl>(D); @@ -1772,7 +1778,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, Decl *D) { return; if (ObjCInterfaceDecl* IDecl = IC->getClassInterface()) if (!IDecl->isObjCRequiresPropertyDefs()) - DefaultSynthesizeProperties(S, IC, IDecl); + DefaultSynthesizeProperties(S, IC, IDecl, AtEnd); } static void DiagnoseUnimplementedAccessor( diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp index 49da0e499771a..1e0b6c158348f 100644 --- a/lib/Sema/SemaOpenMP.cpp +++ b/lib/Sema/SemaOpenMP.cpp @@ -6106,6 +6106,33 @@ static bool checkGrainsizeNumTasksClauses(Sema &S, return ErrorFound; } +static bool checkReductionClauseWithNogroup(Sema &S, + ArrayRef<OMPClause *> Clauses) { + OMPClause *ReductionClause = nullptr; + OMPClause *NogroupClause = nullptr; + for (auto *C : Clauses) { + if (C->getClauseKind() == OMPC_reduction) { + ReductionClause = C; + if (NogroupClause) + break; + continue; + } + if (C->getClauseKind() == OMPC_nogroup) { + NogroupClause = C; + if (ReductionClause) + break; + continue; + } + } + if (ReductionClause && NogroupClause) { + S.Diag(ReductionClause->getLocStart(), diag::err_omp_reduction_with_nogroup) + << SourceRange(NogroupClause->getLocStart(), + NogroupClause->getLocEnd()); + return true; + } + return false; +} + StmtResult Sema::ActOnOpenMPTaskLoopDirective( ArrayRef<OMPClause *> Clauses, Stmt *AStmt, SourceLocation StartLoc, SourceLocation EndLoc, @@ -6132,6 +6159,11 @@ StmtResult Sema::ActOnOpenMPTaskLoopDirective( // not appear on the same taskloop directive. if (checkGrainsizeNumTasksClauses(*this, Clauses)) return StmtError(); + // OpenMP, [2.9.2 taskloop Construct, Restrictions] + // If a reduction clause is present on the taskloop directive, the nogroup + // clause must not be specified. + if (checkReductionClauseWithNogroup(*this, Clauses)) + return StmtError(); getCurFunction()->setHasBranchProtectedScope(); return OMPTaskLoopDirective::Create(Context, StartLoc, EndLoc, @@ -6175,6 +6207,11 @@ StmtResult Sema::ActOnOpenMPTaskLoopSimdDirective( // not appear on the same taskloop directive. if (checkGrainsizeNumTasksClauses(*this, Clauses)) return StmtError(); + // OpenMP, [2.9.2 taskloop Construct, Restrictions] + // If a reduction clause is present on the taskloop directive, the nogroup + // clause must not be specified. + if (checkReductionClauseWithNogroup(*this, Clauses)) + return StmtError(); getCurFunction()->setHasBranchProtectedScope(); return OMPTaskLoopSimdDirective::Create(Context, StartLoc, EndLoc, @@ -9399,6 +9436,12 @@ OMPClause *Sema::ActOnOpenMPReductionClause( SimpleRefExpr, RefRes.get()); if (!PostUpdateRes.isUsable()) continue; + if (isOpenMPTaskingDirective(DSAStack->getCurrentDirective())) { + Diag(RefExpr->getExprLoc(), + diag::err_omp_reduction_non_addressable_expression) + << RefExpr->getSourceRange(); + continue; + } ExprPostUpdates.push_back( IgnoredValueConversions(PostUpdateRes.get()).get()); } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 5cc13f391d117..36f24fd9c463a 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -48,7 +48,7 @@ static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) { /// A convenience routine for creating a decayed reference to a function. static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, - bool HadMultipleCandidates, + const Expr *Base, bool HadMultipleCandidates, SourceLocation Loc = SourceLocation(), const DeclarationNameLoc &LocInfo = DeclarationNameLoc()){ if (S.DiagnoseUseOfDecl(FoundDecl, Loc)) @@ -68,7 +68,7 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, if (HadMultipleCandidates) DRE->setHadMultipleCandidates(true); - S.MarkDeclRefReferenced(DRE); + S.MarkDeclRefReferenced(DRE, Base); return S.ImpCastExprToType(DRE, S.Context.getPointerType(DRE->getType()), CK_FunctionToPointerDecay); } @@ -9830,6 +9830,15 @@ static void DiagnoseBadDeduction(Sema &S, NamedDecl *Found, Decl *Templated, return; } + // We found a specific requirement that disabled the enable_if. + if (PDiag && PDiag->second.getDiagID() == + diag::err_typename_nested_not_found_requirement) { + S.Diag(Templated->getLocation(), + diag::note_ovl_candidate_disabled_by_requirement) + << PDiag->second.getStringArg(0) << TemplateArgString; + return; + } + // Format the SFINAE diagnostic into the argument string. // FIXME: Add a general mechanism to include a PartialDiagnostic *'s // formatted message in another diagnostic. @@ -11937,6 +11946,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc, FunctionDecl *FnDecl = Best->Function; if (FnDecl) { + Expr *Base = nullptr; // We matched an overloaded operator. Build a call to that // operator. @@ -11949,7 +11959,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc, Best->FoundDecl, Method); if (InputRes.isInvalid()) return ExprError(); - Input = InputRes.get(); + Base = Input = InputRes.get(); } else { // Convert the arguments. ExprResult InputInit @@ -11965,7 +11975,8 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc, // Build the actual expression node. ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, Best->FoundDecl, - HadMultipleCandidates, OpLoc); + Base, HadMultipleCandidates, + OpLoc); if (FnExpr.isInvalid()) return ExprError(); @@ -12150,6 +12161,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, FunctionDecl *FnDecl = Best->Function; if (FnDecl) { + Expr *Base = nullptr; // We matched an overloaded operator. Build a call to that // operator. @@ -12171,7 +12183,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, Best->FoundDecl, Method); if (Arg0.isInvalid()) return ExprError(); - Args[0] = Arg0.getAs<Expr>(); + Base = Args[0] = Arg0.getAs<Expr>(); Args[1] = RHS = Arg1.getAs<Expr>(); } else { // Convert the arguments. @@ -12195,7 +12207,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, // Build the actual expression node. ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, - Best->FoundDecl, + Best->FoundDecl, Base, HadMultipleCandidates, OpLoc); if (FnExpr.isInvalid()) return ExprError(); @@ -12417,6 +12429,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, OpLocInfo.setCXXOperatorNameRange(SourceRange(LLoc, RLoc)); ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, Best->FoundDecl, + Base, HadMultipleCandidates, OpLocInfo.getLoc(), OpLocInfo.getInfo()); @@ -12975,7 +12988,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, Context.DeclarationNames.getCXXOperatorName(OO_Call), LParenLoc); OpLocInfo.setCXXOperatorNameRange(SourceRange(LParenLoc, RParenLoc)); ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl, - HadMultipleCandidates, + Obj, HadMultipleCandidates, OpLocInfo.getLoc(), OpLocInfo.getInfo()); if (NewFn.isInvalid()) @@ -13166,7 +13179,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc, // Build the operator call. ExprResult FnExpr = CreateFunctionRefExpr(*this, Method, Best->FoundDecl, - HadMultipleCandidates, OpLoc); + Base, HadMultipleCandidates, OpLoc); if (FnExpr.isInvalid()) return ExprError(); @@ -13225,7 +13238,7 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R, FunctionDecl *FD = Best->Function; ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl, - HadMultipleCandidates, + nullptr, HadMultipleCandidates, SuffixInfo.getLoc(), SuffixInfo.getInfo()); if (Fn.isInvalid()) diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index b6b429d1f25c4..d159172a69908 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -1176,8 +1176,6 @@ bool ObjCSubscriptOpBuilder::findAtIndexGetter() { AtIndexGetter = S.LookupMethodInObjectType(AtIndexGetterSelector, ResultType, true /*instance*/); - bool receiverIdType = (BaseT->isObjCIdType() || - BaseT->isObjCQualifiedIdType()); if (!AtIndexGetter && S.getLangOpts().DebuggerObjCLiteral) { AtIndexGetter = ObjCMethodDecl::Create(S.Context, SourceLocation(), @@ -1203,7 +1201,7 @@ bool ObjCSubscriptOpBuilder::findAtIndexGetter() { } if (!AtIndexGetter) { - if (!receiverIdType) { + if (!BaseT->isObjCIdType()) { S.Diag(BaseExpr->getExprLoc(), diag::err_objc_subscript_method_not_found) << BaseExpr->getType() << 0 << arrayRef; return false; @@ -1284,9 +1282,6 @@ bool ObjCSubscriptOpBuilder::findAtIndexSetter() { } AtIndexSetter = S.LookupMethodInObjectType(AtIndexSetterSelector, ResultType, true /*instance*/); - - bool receiverIdType = (BaseT->isObjCIdType() || - BaseT->isObjCQualifiedIdType()); if (!AtIndexSetter && S.getLangOpts().DebuggerObjCLiteral) { TypeSourceInfo *ReturnTInfo = nullptr; @@ -1321,7 +1316,7 @@ bool ObjCSubscriptOpBuilder::findAtIndexSetter() { } if (!AtIndexSetter) { - if (!receiverIdType) { + if (!BaseT->isObjCIdType()) { S.Diag(BaseExpr->getExprLoc(), diag::err_objc_subscript_method_not_found) << BaseExpr->getType() << 1 << arrayRef; diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index eed10b077eb88..2a38a1f8e1d87 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1544,23 +1544,78 @@ namespace { // A visitor to determine if a continue or break statement is a // subexpression. - class BreakContinueFinder : public EvaluatedExprVisitor<BreakContinueFinder> { + class BreakContinueFinder : public ConstEvaluatedExprVisitor<BreakContinueFinder> { SourceLocation BreakLoc; SourceLocation ContinueLoc; + bool InSwitch = false; + public: - BreakContinueFinder(Sema &S, Stmt* Body) : + BreakContinueFinder(Sema &S, const Stmt* Body) : Inherited(S.Context) { Visit(Body); } - typedef EvaluatedExprVisitor<BreakContinueFinder> Inherited; + typedef ConstEvaluatedExprVisitor<BreakContinueFinder> Inherited; - void VisitContinueStmt(ContinueStmt* E) { + void VisitContinueStmt(const ContinueStmt* E) { ContinueLoc = E->getContinueLoc(); } - void VisitBreakStmt(BreakStmt* E) { - BreakLoc = E->getBreakLoc(); + void VisitBreakStmt(const BreakStmt* E) { + if (!InSwitch) + BreakLoc = E->getBreakLoc(); + } + + void VisitSwitchStmt(const SwitchStmt* S) { + if (const Stmt *Init = S->getInit()) + Visit(Init); + if (const Stmt *CondVar = S->getConditionVariableDeclStmt()) + Visit(CondVar); + if (const Stmt *Cond = S->getCond()) + Visit(Cond); + + // Don't return break statements from the body of a switch. + InSwitch = true; + if (const Stmt *Body = S->getBody()) + Visit(Body); + InSwitch = false; + } + + void VisitForStmt(const ForStmt *S) { + // Only visit the init statement of a for loop; the body + // has a different break/continue scope. + if (const Stmt *Init = S->getInit()) + Visit(Init); + } + + void VisitWhileStmt(const WhileStmt *) { + // Do nothing; the children of a while loop have a different + // break/continue scope. + } + + void VisitDoStmt(const DoStmt *) { + // Do nothing; the children of a while loop have a different + // break/continue scope. + } + + void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { + // Only visit the initialization of a for loop; the body + // has a different break/continue scope. + if (const Stmt *Range = S->getRangeStmt()) + Visit(Range); + if (const Stmt *Begin = S->getBeginStmt()) + Visit(Begin); + if (const Stmt *End = S->getEndStmt()) + Visit(End); + } + + void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { + // Only visit the initialization of a for loop; the body + // has a different break/continue scope. + if (const Stmt *Element = S->getElement()) + Visit(Element); + if (const Stmt *Collection = S->getCollection()) + Visit(Collection); } bool ContinueFound() { return ContinueLoc.isValid(); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index a8923ce9e27df..e9b38551683cf 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2806,6 +2806,101 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, llvm_unreachable("unexpected BuiltinTemplateDecl!"); } +/// Determine whether this alias template is "enable_if_t". +static bool isEnableIfAliasTemplate(TypeAliasTemplateDecl *AliasTemplate) { + return AliasTemplate->getName().equals("enable_if_t"); +} + +/// Collect all of the separable terms in the given condition, which +/// might be a conjunction. +/// +/// FIXME: The right answer is to convert the logical expression into +/// disjunctive normal form, so we can find the first failed term +/// within each possible clause. +static void collectConjunctionTerms(Expr *Clause, + SmallVectorImpl<Expr *> &Terms) { + if (auto BinOp = dyn_cast<BinaryOperator>(Clause->IgnoreParenImpCasts())) { + if (BinOp->getOpcode() == BO_LAnd) { + collectConjunctionTerms(BinOp->getLHS(), Terms); + collectConjunctionTerms(BinOp->getRHS(), Terms); + } + + return; + } + + Terms.push_back(Clause); +} + +// The ranges-v3 library uses an odd pattern of a top-level "||" with +// a left-hand side that is value-dependent but never true. Identify +// the idiom and ignore that term. +static Expr *lookThroughRangesV3Condition(Preprocessor &PP, Expr *Cond) { + // Top-level '||'. + auto *BinOp = dyn_cast<BinaryOperator>(Cond->IgnoreParenImpCasts()); + if (!BinOp) return Cond; + + if (BinOp->getOpcode() != BO_LOr) return Cond; + + // With an inner '==' that has a literal on the right-hand side. + Expr *LHS = BinOp->getLHS(); + auto *InnerBinOp = dyn_cast<BinaryOperator>(LHS->IgnoreParenImpCasts()); + if (!InnerBinOp) return Cond; + + if (InnerBinOp->getOpcode() != BO_EQ || + !isa<IntegerLiteral>(InnerBinOp->getRHS())) + return Cond; + + // If the inner binary operation came from a macro expansion named + // CONCEPT_REQUIRES or CONCEPT_REQUIRES_, return the right-hand side + // of the '||', which is the real, user-provided condition. + SourceLocation Loc = InnerBinOp->getExprLoc(); + if (!Loc.isMacroID()) return Cond; + + StringRef MacroName = PP.getImmediateMacroName(Loc); + if (MacroName == "CONCEPT_REQUIRES" || MacroName == "CONCEPT_REQUIRES_") + return BinOp->getRHS(); + + return Cond; +} + +/// Find the failed subexpression within enable_if, and describe it +/// with a string. +static std::pair<Expr *, std::string> +findFailedEnableIfCondition(Sema &S, Expr *Cond) { + Cond = lookThroughRangesV3Condition(S.PP, Cond); + + // Separate out all of the terms in a conjunction. + SmallVector<Expr *, 4> Terms; + collectConjunctionTerms(Cond, Terms); + + // Determine which term failed. + Expr *FailedCond = nullptr; + for (Expr *Term : Terms) { + // The initialization of the parameter from the argument is + // a constant-evaluated context. + EnterExpressionEvaluationContext ConstantEvaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + + bool Succeeded; + if (Term->EvaluateAsBooleanCondition(Succeeded, S.Context) && + !Succeeded) { + FailedCond = Term->IgnoreParenImpCasts(); + break; + } + } + + if (!FailedCond) + FailedCond = Cond->IgnoreParenImpCasts(); + + std::string Description; + { + llvm::raw_string_ostream Out(Description); + FailedCond->printPretty(Out, nullptr, + PrintingPolicy(S.Context.getLangOpts())); + } + return { FailedCond, Description }; +} + QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs) { @@ -2852,12 +2947,12 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, if (Pattern->isInvalidDecl()) return QualType(); - TemplateArgumentList TemplateArgs(TemplateArgumentList::OnStack, - Converted); + TemplateArgumentList StackTemplateArgs(TemplateArgumentList::OnStack, + Converted); // Only substitute for the innermost template argument list. MultiLevelTemplateArgumentList TemplateArgLists; - TemplateArgLists.addOuterTemplateArguments(&TemplateArgs); + TemplateArgLists.addOuterTemplateArguments(&StackTemplateArgs); unsigned Depth = AliasTemplate->getTemplateParameters()->getDepth(); for (unsigned I = 0; I < Depth; ++I) TemplateArgLists.addOuterTemplateArguments(None); @@ -2870,8 +2965,42 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, CanonType = SubstType(Pattern->getUnderlyingType(), TemplateArgLists, AliasTemplate->getLocation(), AliasTemplate->getDeclName()); - if (CanonType.isNull()) + if (CanonType.isNull()) { + // If this was enable_if and we failed to find the nested type + // within enable_if in a SFINAE context, dig out the specific + // enable_if condition that failed and present that instead. + if (isEnableIfAliasTemplate(AliasTemplate)) { + if (auto DeductionInfo = isSFINAEContext()) { + if (*DeductionInfo && + (*DeductionInfo)->hasSFINAEDiagnostic() && + (*DeductionInfo)->peekSFINAEDiagnostic().second.getDiagID() == + diag::err_typename_nested_not_found_enable_if && + TemplateArgs[0].getArgument().getKind() + == TemplateArgument::Expression) { + Expr *FailedCond; + std::string FailedDescription; + std::tie(FailedCond, FailedDescription) = + findFailedEnableIfCondition( + *this, TemplateArgs[0].getSourceExpression()); + + // Remove the old SFINAE diagnostic. + PartialDiagnosticAt OldDiag = + {SourceLocation(), PartialDiagnostic::NullDiagnostic()}; + (*DeductionInfo)->takeSFINAEDiagnostic(OldDiag); + + // Add a new SFINAE diagnostic specifying which condition + // failed. + (*DeductionInfo)->addSFINAEDiagnostic( + OldDiag.first, + PDiag(diag::err_typename_nested_not_found_requirement) + << FailedDescription + << FailedCond->getSourceRange()); + } + } + } + return QualType(); + } } else if (Name.isDependent() || TemplateSpecializationType::anyDependentTemplateArguments( TemplateArgs, InstantiationDependent)) { @@ -5190,10 +5319,16 @@ enum NullPointerValueKind { /// value of the appropriate type. static NullPointerValueKind isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param, - QualType ParamType, Expr *Arg) { + QualType ParamType, Expr *Arg, + Decl *Entity = nullptr) { if (Arg->isValueDependent() || Arg->isTypeDependent()) return NPV_NotNullPointer; + // dllimport'd entities aren't constant but are available inside of template + // arguments. + if (Entity && Entity->hasAttr<DLLImportAttr>()) + return NPV_NotNullPointer; + if (!S.isCompleteType(Arg->getExprLoc(), ParamType)) llvm_unreachable( "Incomplete parameter type in isNullPointerValueTemplateArgument!"); @@ -5437,14 +5572,8 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, // If our parameter has pointer type, check for a null template value. if (ParamType->isPointerType() || ParamType->isNullPtrType()) { - NullPointerValueKind NPV; - // dllimport'd entities aren't constant but are available inside of template - // arguments. - if (Entity && Entity->hasAttr<DLLImportAttr>()) - NPV = NPV_NotNullPointer; - else - NPV = isNullPointerValueTemplateArgument(S, Param, ParamType, ArgIn); - switch (NPV) { + switch (isNullPointerValueTemplateArgument(S, Param, ParamType, ArgIn, + Entity)) { case NPV_NullPointer: S.Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); Converted = TemplateArgument(S.Context.getCanonicalType(ParamType), @@ -5636,39 +5765,8 @@ static bool CheckTemplateArgumentPointerToMember(Sema &S, TemplateArgument &Converted) { bool Invalid = false; - // Check for a null pointer value. Expr *Arg = ResultArg; - switch (isNullPointerValueTemplateArgument(S, Param, ParamType, Arg)) { - case NPV_Error: - return true; - case NPV_NullPointer: - S.Diag(Arg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); - Converted = TemplateArgument(S.Context.getCanonicalType(ParamType), - /*isNullPtr*/true); - return false; - case NPV_NotNullPointer: - break; - } - bool ObjCLifetimeConversion; - if (S.IsQualificationConversion(Arg->getType(), - ParamType.getNonReferenceType(), - false, ObjCLifetimeConversion)) { - Arg = S.ImpCastExprToType(Arg, ParamType, CK_NoOp, - Arg->getValueKind()).get(); - ResultArg = Arg; - } else if (!S.Context.hasSameUnqualifiedType(Arg->getType(), - ParamType.getNonReferenceType())) { - // We can't perform this conversion. - S.Diag(Arg->getLocStart(), diag::err_template_arg_not_convertible) - << Arg->getType() << ParamType << Arg->getSourceRange(); - S.Diag(Param->getLocation(), diag::note_template_param_here); - return true; - } - - // See through any implicit casts we added to fix the type. - while (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(Arg)) - Arg = Cast->getSubExpr(); // C++ [temp.arg.nontype]p1: // @@ -5725,6 +5823,37 @@ static bool CheckTemplateArgumentPointerToMember(Sema &S, DRE = nullptr; } + ValueDecl *Entity = DRE ? DRE->getDecl() : nullptr; + + // Check for a null pointer value. + switch (isNullPointerValueTemplateArgument(S, Param, ParamType, ResultArg, + Entity)) { + case NPV_Error: + return true; + case NPV_NullPointer: + S.Diag(ResultArg->getExprLoc(), diag::warn_cxx98_compat_template_arg_null); + Converted = TemplateArgument(S.Context.getCanonicalType(ParamType), + /*isNullPtr*/true); + return false; + case NPV_NotNullPointer: + break; + } + + if (S.IsQualificationConversion(ResultArg->getType(), + ParamType.getNonReferenceType(), false, + ObjCLifetimeConversion)) { + ResultArg = S.ImpCastExprToType(ResultArg, ParamType, CK_NoOp, + ResultArg->getValueKind()) + .get(); + } else if (!S.Context.hasSameUnqualifiedType( + ResultArg->getType(), ParamType.getNonReferenceType())) { + // We can't perform this conversion. + S.Diag(ResultArg->getLocStart(), diag::err_template_arg_not_convertible) + << ResultArg->getType() << ParamType << ResultArg->getSourceRange(); + S.Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + if (!DRE) return S.Diag(Arg->getLocStart(), diag::err_template_arg_not_pointer_to_member_form) @@ -9290,7 +9419,7 @@ Sema::ActOnTypenameType(Scope *S, /// Determine whether this failed name lookup should be treated as being /// disabled by a usage of std::enable_if. static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, - SourceRange &CondRange) { + SourceRange &CondRange, Expr *&Cond) { // We must be looking for a ::type... if (!II.isStr("type")) return false; @@ -9320,6 +9449,19 @@ static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, // Assume the first template argument is the condition. CondRange = EnableIfTSTLoc.getArgLoc(0).getSourceRange(); + + // Dig out the condition. + Cond = nullptr; + if (EnableIfTSTLoc.getArgLoc(0).getArgument().getKind() + != TemplateArgument::Expression) + return true; + + Cond = EnableIfTSTLoc.getArgLoc(0).getSourceExpression(); + + // Ignore Boolean literals; they add no value. + if (isa<CXXBoolLiteralExpr>(Cond->IgnoreParenCasts())) + Cond = nullptr; + return true; } @@ -9363,9 +9505,25 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // If we're looking up 'type' within a template named 'enable_if', produce // a more specific diagnostic. SourceRange CondRange; - if (isEnableIf(QualifierLoc, II, CondRange)) { + Expr *Cond = nullptr; + if (isEnableIf(QualifierLoc, II, CondRange, Cond)) { + // If we have a condition, narrow it down to the specific failed + // condition. + if (Cond) { + Expr *FailedCond; + std::string FailedDescription; + std::tie(FailedCond, FailedDescription) = + findFailedEnableIfCondition(*this, Cond); + + Diag(FailedCond->getExprLoc(), + diag::err_typename_nested_not_found_requirement) + << FailedDescription + << FailedCond->getSourceRange(); + return QualType(); + } + Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if) - << Ctx << CondRange; + << Ctx << CondRange; return QualType(); } |