diff options
Diffstat (limited to 'clang/lib/Sema')
23 files changed, 720 insertions, 139 deletions
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 829c81b..35ad0b5 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -503,8 +503,12 @@ static bool areAllValuesNoReturn(const VarDecl *VD, const CFGBlock &VarBlk, TransferFunctions TF(VD); BackwardDataflowWorklist Worklist(*AC.getCFG(), AC); + llvm::DenseSet<const CFGBlock *> Visited; Worklist.enqueueBlock(&VarBlk); while (const CFGBlock *B = Worklist.dequeue()) { + if (Visited.contains(B)) + continue; + Visited.insert(B); // First check the current block. for (CFGBlock::const_reverse_iterator ri = B->rbegin(), re = B->rend(); ri != re; ++ri) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 56608e9..43a7f9e 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1616,6 +1616,8 @@ void Sema::ActOnEndOfTranslationUnit() { if (!PP.isIncrementalProcessingEnabled()) TUScope = nullptr; + + checkExposure(Context.getTranslationUnitDecl()); } @@ -2249,16 +2251,15 @@ void Sema::checkTypeSupport(QualType Ty, SourceLocation Loc, ValueDecl *D) { } // Don't allow SVE types in functions without a SVE target. - if (Ty->isSVESizelessBuiltinType() && FD) { + if (Ty->isSVESizelessBuiltinType() && FD && !FD->getType().isNull()) { llvm::StringMap<bool> CallerFeatureMap; Context.getFunctionFeatureMap(CallerFeatureMap, FD); if (!Builtin::evaluateRequiredTargetFeatures("sve", CallerFeatureMap)) { if (!Builtin::evaluateRequiredTargetFeatures("sme", CallerFeatureMap)) Diag(Loc, diag::err_sve_vector_in_non_sve_target) << Ty; else if (!IsArmStreamingFunction(FD, - /*IncludeLocallyStreaming=*/true)) { + /*IncludeLocallyStreaming=*/true)) Diag(Loc, diag::err_sve_vector_in_non_streaming_function) << Ty; - } } } diff --git a/clang/lib/Sema/SemaARM.cpp b/clang/lib/Sema/SemaARM.cpp index 8e27fab..e09c352 100644 --- a/clang/lib/Sema/SemaARM.cpp +++ b/clang/lib/Sema/SemaARM.cpp @@ -846,9 +846,9 @@ bool SemaARM::CheckARMCoprocessorImmediate(const TargetInfo &TI, return false; } -bool SemaARM::CheckARMBuiltinExclusiveCall(unsigned BuiltinID, - CallExpr *TheCall, - unsigned MaxWidth) { +bool SemaARM::CheckARMBuiltinExclusiveCall(const TargetInfo &TI, + unsigned BuiltinID, + CallExpr *TheCall) { assert((BuiltinID == ARM::BI__builtin_arm_ldrex || BuiltinID == ARM::BI__builtin_arm_ldaex || BuiltinID == ARM::BI__builtin_arm_strex || @@ -923,12 +923,56 @@ bool SemaARM::CheckARMBuiltinExclusiveCall(unsigned BuiltinID, return true; } - // But ARM doesn't have instructions to deal with 128-bit versions. - if (Context.getTypeSize(ValType) > MaxWidth) { - assert(MaxWidth == 64 && "Diagnostic unexpectedly inaccurate"); - Diag(DRE->getBeginLoc(), diag::err_atomic_exclusive_builtin_pointer_size) - << PointerArg->getType() << PointerArg->getSourceRange(); - return true; + // Check whether the size of the type can be handled atomically on this + // target. + if (!TI.getTriple().isAArch64()) { + unsigned Mask = TI.getARMLDREXMask(); + unsigned Bits = Context.getTypeSize(ValType); + bool Supported = + (llvm::isPowerOf2_64(Bits)) && Bits >= 8 && (Mask & (Bits / 8)); + + if (!Supported) { + // Emit a diagnostic saying that this size isn't available. If _no_ size + // of exclusive access is supported on this target, we emit a diagnostic + // with special wording for that case, but otherwise, we emit + // err_atomic_exclusive_builtin_pointer_size and loop over `Mask` to + // control what subset of sizes it lists as legal. + if (Mask) { + auto D = Diag(DRE->getBeginLoc(), + diag::err_atomic_exclusive_builtin_pointer_size) + << PointerArg->getType(); + bool Started = false; + for (unsigned Size = 1; Size <= 8; Size <<= 1) { + // For each of the sizes 1,2,4,8, pass two integers into the + // diagnostic. The first selects a separator from the previous + // number: 0 for no separator at all, 1 for a comma, 2 for " or " + // which appears before the final number in a list of more than one. + // The second integer just indicates whether we print this size in + // the message at all. + if (!(Mask & Size)) { + // This size isn't one of the supported ones, so emit no separator + // text and don't print the size itself. + D << 0 << 0; + } else { + // This size is supported, so print it, and an appropriate + // separator. + Mask &= ~Size; + if (!Started) + D << 0; // No separator if this is the first size we've printed + else if (Mask) + D << 1; // "," if there's still another size to come + else + D << 2; // " or " if the size we're about to print is the last + D << 1; // print the size itself + Started = true; + } + } + } else { + Diag(DRE->getBeginLoc(), + diag::err_atomic_exclusive_builtin_pointer_size_none) + << PointerArg->getSourceRange(); + } + } } switch (ValType.getObjCLifetime()) { @@ -972,7 +1016,7 @@ bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI, BuiltinID == ARM::BI__builtin_arm_ldaex || BuiltinID == ARM::BI__builtin_arm_strex || BuiltinID == ARM::BI__builtin_arm_stlex) { - return CheckARMBuiltinExclusiveCall(BuiltinID, TheCall, 64); + return CheckARMBuiltinExclusiveCall(TI, BuiltinID, TheCall); } if (BuiltinID == ARM::BI__builtin_arm_prefetch) { @@ -1053,7 +1097,7 @@ bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI, BuiltinID == AArch64::BI__builtin_arm_ldaex || BuiltinID == AArch64::BI__builtin_arm_strex || BuiltinID == AArch64::BI__builtin_arm_stlex) { - return CheckARMBuiltinExclusiveCall(BuiltinID, TheCall, 128); + return CheckARMBuiltinExclusiveCall(TI, BuiltinID, TheCall); } if (BuiltinID == AArch64::BI__builtin_arm_prefetch) { diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index 8c6a173..68a698f 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -547,6 +547,12 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, return; } case AR_Deprecated: + // Suppress -Wdeprecated-declarations in implicit + // functions. + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(S.getCurFunctionDecl()); + FD && FD->isImplicit()) + return; + if (ObjCPropertyAccess) diag = diag::warn_property_method_deprecated; else if (S.currentEvaluationContext().IsCaseExpr) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index c74b671..bc87611 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -15893,9 +15893,7 @@ ExprResult Sema::BuiltinMatrixTranspose(CallExpr *TheCall, // Get and verify the matrix dimensions. static std::optional<unsigned> getAndVerifyMatrixDimension(Expr *Expr, StringRef Name, Sema &S) { - SourceLocation ErrorPos; - std::optional<llvm::APSInt> Value = - Expr->getIntegerConstantExpr(S.Context, &ErrorPos); + std::optional<llvm::APSInt> Value = Expr->getIntegerConstantExpr(S.Context); if (!Value) { S.Diag(Expr->getBeginLoc(), diag::err_builtin_matrix_scalar_unsigned_arg) << Name; diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index a43ac9e..0de5580 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -4034,6 +4034,14 @@ static void AddOverloadParameterChunks( return; } + // C++23 introduces an explicit object parameter, a.k.a. "deducing this" + // Skip it for autocomplete and treat the next parameter as the first + // parameter + if (Function && FirstParameter && + Function->getParamDecl(P)->isExplicitObjectParameter()) { + continue; + } + if (FirstParameter) FirstParameter = false; else diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 044cf5c..da85959 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1105,10 +1105,6 @@ static bool CheckFunctionConstraintsWithoutInstantiation( } Sema::ContextRAII SavedContext(SemaRef, FD); - std::optional<Sema::CXXThisScopeRAII> ThisScope; - if (auto *Method = dyn_cast<CXXMethodDecl>(FD)) - ThisScope.emplace(SemaRef, /*Record=*/Method->getParent(), - /*ThisQuals=*/Method->getMethodQualifiers()); return SemaRef.CheckConstraintSatisfaction( Template, TemplateAC, MLTAL, PointOfInstantiation, Satisfaction); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fd22e01..c7e7507 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -3267,6 +3267,14 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old, if (isa<UsedAttr>(I) || isa<RetainAttr>(I)) continue; + if (isa<InferredNoReturnAttr>(I)) { + if (auto *FD = dyn_cast<FunctionDecl>(New)) { + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + continue; // Don't propagate inferred noreturn attributes to explicit + // specializations. + } + } + if (mergeDeclAttribute(*this, New, I, LocalAMK)) foundAny = true; } @@ -12578,9 +12586,9 @@ static bool isDefaultStdCall(FunctionDecl *FD, Sema &S) { if (FD->getName() == "main" || FD->getName() == "wmain") return false; - // Default calling convention for MinGW is __cdecl + // Default calling convention for MinGW and Cygwin is __cdecl const llvm::Triple &T = S.Context.getTargetInfo().getTriple(); - if (T.isWindowsGNUEnvironment()) + if (T.isOSCygMing()) return false; // Default calling convention for WinMain, wWinMain and DllMain @@ -20573,7 +20581,8 @@ TopLevelStmtDecl *Sema::ActOnStartTopLevelStmtDecl(Scope *S) { } void Sema::ActOnFinishTopLevelStmtDecl(TopLevelStmtDecl *D, Stmt *Statement) { - D->setStmt(Statement); + if (Statement) + D->setStmt(Statement); PopCompoundScope(); PopFunctionScopeInfo(); PopDeclContext(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9a2950c..16b18bc 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1970,6 +1970,13 @@ void clang::inferNoReturnAttr(Sema &S, const Decl *D) { if (!FD) return; + // Skip explicit specializations here as they may have + // a user-provided definition that may deliberately differ from the primary + // template. If an explicit specialization truly never returns, the user + // should explicitly mark it with [[noreturn]]. + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return; + auto *NonConstFD = const_cast<FunctionDecl *>(FD); DiagnosticsEngine &Diags = S.getDiagnostics(); if (Diags.isIgnored(diag::warn_falloff_nonvoid, FD->getLocation()) && @@ -2034,7 +2041,8 @@ bool Sema::CheckAttrTarget(const ParsedAttr &AL) { // Check whether the attribute is valid on the current target. if (!AL.existsInTarget(Context.getTargetInfo())) { if (AL.isRegularKeywordAttribute()) - Diag(AL.getLoc(), diag::err_keyword_not_supported_on_target); + Diag(AL.getLoc(), diag::err_keyword_not_supported_on_target) + << AL << AL.getRange(); else DiagnoseUnknownAttribute(AL); AL.setInvalid(); @@ -4797,10 +4805,10 @@ void Sema::AddModeAttr(Decl *D, const AttributeCommonInfo &CI, static void handleNonStringAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // This only applies to fields and variable declarations which have an array - // type. + // type or pointer type, with character elements. QualType QT = cast<ValueDecl>(D)->getType(); - if (!QT->isArrayType() || - !QT->getBaseElementTypeUnsafe()->isAnyCharacterType()) { + if ((!QT->isArrayType() && !QT->isPointerType()) || + !QT->getPointeeOrArrayElementType()->isAnyCharacterType()) { S.Diag(D->getBeginLoc(), diag::warn_attribute_non_character_array) << AL << AL.isRegularKeywordAttribute() << QT << AL.getRange(); return; diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 7c982bc..b137549 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/ParsedAttr.h" @@ -485,6 +486,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, // implementation unit importing its interface). Make this module visible // and return the import decl to be added to the current TU. if (Interface) { + HadImportedNamedModules = true; makeTransitiveImportsVisible(getASTContext(), VisibleModules, Interface, Mod, ModuleLoc, @@ -728,6 +730,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, getCurrentModule()->Imports.insert(Mod); } + HadImportedNamedModules = true; + return Import; } @@ -1102,3 +1106,467 @@ bool Sema::isCurrentModulePurview() const { return false; } } + +//===----------------------------------------------------------------------===// +// Checking Exposure in modules // +//===----------------------------------------------------------------------===// + +namespace { +class ExposureChecker { +public: + ExposureChecker(Sema &S) : SemaRef(S) {} + + bool checkExposure(const VarDecl *D, bool Diag); + bool checkExposure(const CXXRecordDecl *D, bool Diag); + bool checkExposure(const Stmt *S, bool Diag); + bool checkExposure(const FunctionDecl *D, bool Diag); + bool checkExposure(const NamedDecl *D, bool Diag); + void checkExposureInContext(const DeclContext *DC); + bool isExposureCandidate(const NamedDecl *D); + + bool isTULocal(QualType Ty); + bool isTULocal(const NamedDecl *ND); + bool isTULocal(const Expr *E); + + Sema &SemaRef; + +private: + llvm::DenseSet<const NamedDecl *> ExposureSet; + llvm::DenseSet<const NamedDecl *> KnownNonExposureSet; +}; + +bool ExposureChecker::isTULocal(QualType Ty) { + // [basic.link]p15: + // An entity is TU-local if it is + // - a type, type alias, namespace, namespace alias, function, variable, or + // template that + // -- has internal linkage, or + return Ty->getLinkage() == Linkage::Internal; + + // TODO: + // [basic.link]p15.2: + // a type with no name that is defined outside a class-specifier, function + // body, or initializer or is introduced by a defining-type-specifier that + // is used to declare only TU-local entities, +} + +bool ExposureChecker::isTULocal(const NamedDecl *D) { + if (!D) + return false; + + // [basic.link]p15: + // An entity is TU-local if it is + // - a type, type alias, namespace, namespace alias, function, variable, or + // template that + // -- has internal linkage, or + if (D->getLinkageInternal() == Linkage::Internal) + return true; + + if (D->isInAnonymousNamespace()) + return true; + + // [basic.link]p15.1.2: + // does not have a name with linkage and is declared, or introduced by a + // lambda-expression, within the definition of a TU-local entity, + if (D->getLinkageInternal() == Linkage::None) + if (auto *ND = dyn_cast<NamedDecl>(D->getDeclContext()); + ND && isTULocal(ND)) + return true; + + // [basic.link]p15.3, p15.4: + // - a specialization of a TU-local template, + // - a specialization of a template with any TU-local template argument, or + ArrayRef<TemplateArgument> TemplateArgs; + NamedDecl *PrimaryTemplate = nullptr; + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + TemplateArgs = CTSD->getTemplateArgs().asArray(); + PrimaryTemplate = CTSD->getSpecializedTemplate(); + if (isTULocal(PrimaryTemplate)) + return true; + } else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D)) { + TemplateArgs = VTSD->getTemplateArgs().asArray(); + PrimaryTemplate = VTSD->getSpecializedTemplate(); + if (isTULocal(PrimaryTemplate)) + return true; + } else if (auto *FD = dyn_cast<FunctionDecl>(D)) { + if (auto *TAList = FD->getTemplateSpecializationArgs()) + TemplateArgs = TAList->asArray(); + + PrimaryTemplate = FD->getPrimaryTemplate(); + if (isTULocal(PrimaryTemplate)) + return true; + } + + if (!PrimaryTemplate) + // Following off, we only check for specializations. + return false; + + if (KnownNonExposureSet.count(D)) + return false; + + for (auto &TA : TemplateArgs) { + switch (TA.getKind()) { + case TemplateArgument::Type: + if (isTULocal(TA.getAsType())) + return true; + break; + case TemplateArgument::Declaration: + if (isTULocal(TA.getAsDecl())) + return true; + break; + default: + break; + } + } + + // [basic.link]p15.5 + // - a specialization of a template whose (possibly instantiated) declaration + // is an exposure. + if (checkExposure(PrimaryTemplate, /*Diag=*/false)) + return true; + + // Avoid calling checkExposure again since it is expensive. + KnownNonExposureSet.insert(D); + return false; +} + +bool ExposureChecker::isTULocal(const Expr *E) { + if (!E) + return false; + + // [basic.link]p16: + // A value or object is TU-local if either + // - it is of TU-local type, + if (isTULocal(E->getType())) + return true; + + E = E->IgnoreParenImpCasts(); + // [basic.link]p16.2: + // - it is, or is a pointer to, a TU-local function or the object associated + // with a TU-local variable, + // - it is an object of class or array type and any of its subobjects or any + // of the objects or functions to which its non-static data members of + // reference type refer is TU-local and is usable in constant expressions, or + // FIXME: But how can we know the value of pointers or arrays at compile time? + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + if (auto *FD = dyn_cast_or_null<FunctionDecl>(DRE->getFoundDecl())) + return isTULocal(FD); + else if (auto *VD = dyn_cast_or_null<VarDecl>(DRE->getFoundDecl())) + return isTULocal(VD); + else if (auto *RD = dyn_cast_or_null<CXXRecordDecl>(DRE->getFoundDecl())) + return isTULocal(RD); + } + + // TODO: + // [basic.link]p16.4: + // it is a reflection value that represents... + + return false; +} + +bool ExposureChecker::isExposureCandidate(const NamedDecl *D) { + if (!D) + return false; + + // [basic.link]p17: + // If a (possibly instantiated) declaration of, or a deduction guide for, + // a non-TU-local entity in a module interface unit + // (outside the private-module-fragment, if any) or + // module partition is an exposure, the program is ill-formed. + Module *M = D->getOwningModule(); + if (!M || !M->isInterfaceOrPartition()) + return false; + + if (D->isImplicit()) + return false; + + // [basic.link]p14: + // A declaration is an exposure if it either names a TU-local entity + // (defined below), ignoring: + // ... + // - friend declarations in a class definition + if (D->getFriendObjectKind() && + isa<CXXRecordDecl>(D->getLexicalDeclContext())) + return false; + + return true; +} + +bool ExposureChecker::checkExposure(const NamedDecl *D, bool Diag) { + if (!isExposureCandidate(D)) + return false; + + if (auto *FD = dyn_cast<FunctionDecl>(D)) + return checkExposure(FD, Diag); + if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) + return checkExposure(FTD->getTemplatedDecl(), Diag); + + if (auto *VD = dyn_cast<VarDecl>(D)) + return checkExposure(VD, Diag); + if (auto *VTD = dyn_cast<VarTemplateDecl>(D)) + return checkExposure(VTD->getTemplatedDecl(), Diag); + + if (auto *RD = dyn_cast<CXXRecordDecl>(D)) + return checkExposure(RD, Diag); + + if (auto *CTD = dyn_cast<ClassTemplateDecl>(D)) + return checkExposure(CTD->getTemplatedDecl(), Diag); + + return false; +} + +bool ExposureChecker::checkExposure(const FunctionDecl *FD, bool Diag) { + bool IsExposure = false; + if (isTULocal(FD->getReturnType())) { + IsExposure = true; + if (Diag) + SemaRef.Diag(FD->getReturnTypeSourceRange().getBegin(), + diag::warn_exposure) + << FD->getReturnType(); + } + + for (ParmVarDecl *Parms : FD->parameters()) + if (isTULocal(Parms->getType())) { + IsExposure = true; + if (Diag) + SemaRef.Diag(Parms->getLocation(), diag::warn_exposure) + << Parms->getType(); + } + + bool IsImplicitInstantiation = + FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation; + + // [basic.link]p14: + // A declaration is an exposure if it either names a TU-local entity + // (defined below), ignoring: + // - the function-body for a non-inline function or function template + // (but not the deduced return + // type for a (possibly instantiated) definition of a function with a + // declared return type that uses a placeholder type + // ([dcl.spec.auto])), + Diag &= + (FD->isInlined() || IsImplicitInstantiation) && !FD->isDependentContext(); + + IsExposure |= checkExposure(FD->getBody(), Diag); + if (IsExposure) + ExposureSet.insert(FD); + + return IsExposure; +} + +bool ExposureChecker::checkExposure(const VarDecl *VD, bool Diag) { + bool IsExposure = false; + // [basic.link]p14: + // A declaration is an exposure if it either names a TU-local entity (defined + // below), ignoring: + // ... + // or defines a constexpr variable initialized to a TU-local value (defined + // below). + if (VD->isConstexpr() && isTULocal(VD->getInit())) { + IsExposure = true; + if (Diag) + SemaRef.Diag(VD->getInit()->getExprLoc(), diag::warn_exposure) + << VD->getInit(); + } + + if (isTULocal(VD->getType())) { + IsExposure = true; + if (Diag) + SemaRef.Diag(VD->getLocation(), diag::warn_exposure) << VD->getType(); + } + + // [basic.link]p14: + // ..., ignoring: + // - the initializer for a variable or variable template (but not the + // variable's type), + // + // Note: although the spec says to ignore the initializer for all variable, + // for the code we generated now for inline variables, it is dangerous if the + // initializer of an inline variable is TULocal. + Diag &= !VD->getDeclContext()->isDependentContext() && VD->isInline(); + IsExposure |= checkExposure(VD->getInit(), Diag); + if (IsExposure) + ExposureSet.insert(VD); + + return IsExposure; +} + +bool ExposureChecker::checkExposure(const CXXRecordDecl *RD, bool Diag) { + if (!RD->hasDefinition()) + return false; + + bool IsExposure = false; + for (CXXMethodDecl *Method : RD->methods()) + IsExposure |= checkExposure(Method, Diag); + + for (FieldDecl *FD : RD->fields()) { + if (isTULocal(FD->getType())) { + IsExposure = true; + if (Diag) + SemaRef.Diag(FD->getLocation(), diag::warn_exposure) << FD->getType(); + } + } + + for (const CXXBaseSpecifier &Base : RD->bases()) { + if (isTULocal(Base.getType())) { + IsExposure = true; + if (Diag) + SemaRef.Diag(Base.getBaseTypeLoc(), diag::warn_exposure) + << Base.getType(); + } + } + + if (IsExposure) + ExposureSet.insert(RD); + + return IsExposure; +} + +class ReferenceTULocalChecker : public DynamicRecursiveASTVisitor { +public: + using CallbackTy = std::function<void(DeclRefExpr *, ValueDecl *)>; + + ReferenceTULocalChecker(ExposureChecker &C, CallbackTy &&Callback) + : Checker(C), Callback(std::move(Callback)) {} + + bool VisitDeclRefExpr(DeclRefExpr *DRE) override { + ValueDecl *Referenced = DRE->getDecl(); + if (!Referenced) + return true; + + if (!Checker.isTULocal(Referenced)) + // We don't care if the referenced declaration is not TU-local. + return true; + + Qualifiers Qual = DRE->getType().getQualifiers(); + // [basic.link]p14: + // A declaration is an exposure if it either names a TU-local entity + // (defined below), ignoring: + // ... + // - any reference to a non-volatile const object ... + if (Qual.hasConst() && !Qual.hasVolatile()) + return true; + + // [basic.link]p14: + // ..., ignoring: + // ... + // (p14.4) - ... or reference with internal or no linkage initialized with + // a constant expression that is not an odr-use + ASTContext &Context = Referenced->getASTContext(); + Linkage L = Referenced->getLinkageInternal(); + if (DRE->isNonOdrUse() && (L == Linkage::Internal || L == Linkage::None)) + if (auto *VD = dyn_cast<VarDecl>(Referenced); + VD && VD->getInit() && !VD->getInit()->isValueDependent() && + VD->getInit()->isConstantInitializer(Context, /*IsForRef=*/false)) + return true; + + Callback(DRE, Referenced); + return true; + } + + ExposureChecker &Checker; + CallbackTy Callback; +}; + +bool ExposureChecker::checkExposure(const Stmt *S, bool Diag) { + if (!S) + return false; + + bool HasReferencedTULocals = false; + ReferenceTULocalChecker Checker( + *this, [this, &HasReferencedTULocals, Diag](DeclRefExpr *DRE, + ValueDecl *Referenced) { + if (Diag) { + SemaRef.Diag(DRE->getExprLoc(), diag::warn_exposure) << Referenced; + } + HasReferencedTULocals = true; + }); + Checker.TraverseStmt(const_cast<Stmt *>(S)); + return HasReferencedTULocals; +} + +void ExposureChecker::checkExposureInContext(const DeclContext *DC) { + for (auto *TopD : DC->noload_decls()) { + auto *TopND = dyn_cast<NamedDecl>(TopD); + if (!TopND) + continue; + + if (auto *Namespace = dyn_cast<NamespaceDecl>(TopND)) { + checkExposureInContext(Namespace); + continue; + } + + // [basic.link]p17: + // If a (possibly instantiated) declaration of, or a deduction guide for, + // a non-TU-local entity in a module interface unit + // (outside the private-module-fragment, if any) or + // module partition is an exposure, the program is ill-formed. + if (!TopND->isFromASTFile() && isExposureCandidate(TopND) && + !isTULocal(TopND)) + checkExposure(TopND, /*Diag=*/true); + } +} + +} // namespace + +void Sema::checkExposure(const TranslationUnitDecl *TU) { + if (!TU) + return; + + ExposureChecker Checker(*this); + + Module *M = TU->getOwningModule(); + if (M && M->isInterfaceOrPartition()) + Checker.checkExposureInContext(TU); + + // [basic.link]p18: + // If a declaration that appears in one translation unit names a TU-local + // entity declared in another translation unit that is not a header unit, + // the program is ill-formed. + for (auto FDAndInstantiationLocPair : PendingCheckReferenceForTULocal) { + FunctionDecl *FD = FDAndInstantiationLocPair.first; + SourceLocation PointOfInstantiation = FDAndInstantiationLocPair.second; + + if (!FD->hasBody()) + continue; + + ReferenceTULocalChecker(Checker, [&, this](DeclRefExpr *DRE, + ValueDecl *Referenced) { + // A "defect" in current implementation. Now an implicit instantiation of + // a template, the instantiation is considered to be in the same module + // unit as the template instead of the module unit where the instantiation + // happens. + // + // See test/Modules/Exposre-2.cppm for example. + if (!Referenced->isFromASTFile()) + return; + + if (!Referenced->isInAnotherModuleUnit()) + return; + + // This is not standard conforming. But given there are too many static + // (inline) functions in headers in existing code, it is more user + // friendly to ignore them temporarily now. maybe we can have another flag + // for this. + if (Referenced->getOwningModule()->isExplicitGlobalModule() && + isa<FunctionDecl>(Referenced)) + return; + + Diag(PointOfInstantiation, + diag::warn_reference_tu_local_entity_in_other_tu) + << FD << Referenced + << Referenced->getOwningModule()->getTopLevelModuleName(); + }).TraverseStmt(FD->getBody()); + } +} + +void Sema::checkReferenceToTULocalFromOtherTU( + FunctionDecl *FD, SourceLocation PointOfInstantiation) { + // Checking if a declaration have any reference to TU-local entities in other + // TU is expensive. Try to avoid it as much as possible. + if (!FD || !HadImportedNamedModules) + return; + + PendingCheckReferenceForTULocal.push_back( + std::make_pair(FD, PointOfInstantiation)); +} diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index 128a5db..8bfea62 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -699,11 +699,19 @@ ExprResult SemaOpenACC::ActOnVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK, // OpenACC3.3 2.13: // A 'var' in a 'declare' directive must be a variable or array name. if ((CK == OpenACCClauseKind::UseDevice || - DK == OpenACCDirectiveKind::Declare) && - isa<ArraySectionExpr, ArraySubscriptExpr>(CurVarExpr)) { - Diag(VarExpr->getExprLoc(), diag::err_acc_not_a_var_ref_use_device_declare) - << (DK == OpenACCDirectiveKind::Declare); - return ExprError(); + DK == OpenACCDirectiveKind::Declare)) { + if (isa<ArraySubscriptExpr>(CurVarExpr)) { + Diag(VarExpr->getExprLoc(), + diag::err_acc_not_a_var_ref_use_device_declare) + << (DK == OpenACCDirectiveKind::Declare); + return ExprError(); + } + // As an extension, we allow 'array sections'/'sub-arrays' here, as that is + // effectively defining an array, and are in common use. + if (isa<ArraySectionExpr>(CurVarExpr)) + Diag(VarExpr->getExprLoc(), + diag::ext_acc_array_section_use_device_declare) + << (DK == OpenACCDirectiveKind::Declare); } // Sub-arrays/subscript-exprs are fine as long as the base is a diff --git a/clang/lib/Sema/SemaOpenACCAtomic.cpp b/clang/lib/Sema/SemaOpenACCAtomic.cpp index 9c8c8d1..a9319dc 100644 --- a/clang/lib/Sema/SemaOpenACCAtomic.cpp +++ b/clang/lib/Sema/SemaOpenACCAtomic.cpp @@ -576,6 +576,11 @@ class AtomicOperandChecker { return AssocStmt; } + const Expr *IgnoreBeforeCompare(const Expr *E) { + return E->IgnoreParenImpCasts()->IgnoreParenNoopCasts( + SemaRef.getASTContext()); + } + bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX, IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) { llvm::FoldingSetNodeID First_ID, Second_ID; @@ -648,8 +653,10 @@ class AtomicOperandChecker { if (CheckOperandVariable(AssignRes->RHS, PD)) return getRecoveryExpr(); - if (CheckVarRefsSame(FirstExprResults.ExprKind, FirstExprResults.X_Var, - IDACInfo::SimpleAssign, AssignRes->RHS)) + if (CheckVarRefsSame(FirstExprResults.ExprKind, + IgnoreBeforeCompare(FirstExprResults.X_Var), + IDACInfo::SimpleAssign, + IgnoreBeforeCompare(AssignRes->RHS))) return getRecoveryExpr(); break; } @@ -660,9 +667,10 @@ class AtomicOperandChecker { if (SecondExprResults.Failed) return getRecoveryExpr(); - if (CheckVarRefsSame(FirstExprResults.ExprKind, FirstExprResults.X_Var, + if (CheckVarRefsSame(FirstExprResults.ExprKind, + IgnoreBeforeCompare(FirstExprResults.X_Var), SecondExprResults.ExprKind, - SecondExprResults.X_Var)) + IgnoreBeforeCompare(SecondExprResults.X_Var))) return getRecoveryExpr(); break; } diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp index 3f90fe8..b54a012 100644 --- a/clang/lib/Sema/SemaOpenACCClause.cpp +++ b/clang/lib/Sema/SemaOpenACCClause.cpp @@ -1919,6 +1919,14 @@ ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind, << EltTy << /*Sub array base type*/ 1; return ExprError(); } + } else if (VarExpr->getType()->isArrayType()) { + // Arrays are considered an 'aggregate variable' explicitly, so are OK, no + // additional checking required. + // + // Glossary: Aggregate variables – a variable of any non-scalar datatype, + // including array or composite variables. + // + // The next branch (record decl) checks for composite variables. } else if (auto *RD = VarExpr->getType()->getAsRecordDecl()) { if (!RD->isStruct() && !RD->isClass()) { Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type) @@ -2246,7 +2254,13 @@ bool SemaOpenACC::CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause, continue; } } else { - const auto *DRE = cast<DeclRefExpr>(VarExpr); + + const Expr *VarExprTemp = VarExpr; + + while (const auto *ASE = dyn_cast<ArraySectionExpr>(VarExprTemp)) + VarExprTemp = ASE->getBase()->IgnoreParenImpCasts(); + + const auto *DRE = cast<DeclRefExpr>(VarExprTemp); if (const auto *Var = dyn_cast<VarDecl>(DRE->getDecl())) { CurDecl = Var->getCanonicalDecl(); diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 4ecc9b0..2c5d97c 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -2829,7 +2829,7 @@ static void checkReductionClauses(Sema &S, DSAStackTy *Stack, continue; } for (Expr *Ref : RC->varlist()) { - assert(Ref && "NULL expr in OpenMP nontemporal clause."); + assert(Ref && "NULL expr in OpenMP reduction clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = Ref; @@ -7612,6 +7612,23 @@ void SemaOpenMP::ActOnOpenMPDeclareVariantDirective( return; } + // OpenMP 6.0 [9.6.2 (page 332, line 31-33, adjust_args clause, Restrictions] + // If the `need_device_addr` adjust-op modifier is present, each list item + // that appears in the clause must refer to an argument in the declaration of + // the function variant that has a reference type + if (getLangOpts().OpenMP >= 60) { + for (Expr *E : AdjustArgsNeedDeviceAddr) { + E = E->IgnoreParenImpCasts(); + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + if (!VD->getType()->isReferenceType()) + Diag(E->getExprLoc(), + diag::err_omp_non_by_ref_need_device_addr_modifier_argument); + } + } + } + } + auto *NewAttr = OMPDeclareVariantAttr::CreateImplicit( getASTContext(), VariantRef, &TI, const_cast<Expr **>(AdjustArgsNothing.data()), AdjustArgsNothing.size(), @@ -18344,7 +18361,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPSharedClause(ArrayRef<Expr *> VarList, SourceLocation EndLoc) { SmallVector<Expr *, 8> Vars; for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP lastprivate clause."); + assert(RefExpr && "NULL expr in OpenMP shared clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = RefExpr; @@ -19991,7 +20008,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPAlignedClause( SourceLocation LParenLoc, SourceLocation ColonLoc, SourceLocation EndLoc) { SmallVector<Expr *, 8> Vars; for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP linear clause."); + assert(RefExpr && "NULL expr in OpenMP aligned clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = RefExpr; @@ -20167,7 +20184,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPCopyprivateClause(ArrayRef<Expr *> VarList, SmallVector<Expr *, 8> DstExprs; SmallVector<Expr *, 8> AssignmentOps; for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP linear clause."); + assert(RefExpr && "NULL expr in OpenMP copyprivate clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = RefExpr; @@ -20526,7 +20543,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPDependClause( TotalDepCount = VarOffset.TotalDepCount; } else { for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP shared clause."); + assert(RefExpr && "NULL expr in OpenMP depend clause."); if (isa<DependentScopeDeclRefExpr>(RefExpr)) { // It will be analyzed later. Vars.push_back(RefExpr); @@ -23737,7 +23754,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPAllocateClause( // Analyze and build list of variables. SmallVector<Expr *, 8> Vars; for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP private clause."); + assert(RefExpr && "NULL expr in OpenMP allocate clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = RefExpr; @@ -23829,7 +23846,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPInclusiveClause(ArrayRef<Expr *> VarList, SourceLocation EndLoc) { SmallVector<Expr *, 8> Vars; for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP nontemporal clause."); + assert(RefExpr && "NULL expr in OpenMP inclusive clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = RefExpr; @@ -23870,7 +23887,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPExclusiveClause(ArrayRef<Expr *> VarList, SourceLocation EndLoc) { SmallVector<Expr *, 8> Vars; for (Expr *RefExpr : VarList) { - assert(RefExpr && "NULL expr in OpenMP nontemporal clause."); + assert(RefExpr && "NULL expr in OpenMP exclusive clause."); SourceLocation ELoc; SourceRange ERange; Expr *SimpleRefExpr = RefExpr; @@ -24063,7 +24080,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPAffinityClause( SourceLocation EndLoc, Expr *Modifier, ArrayRef<Expr *> Locators) { SmallVector<Expr *, 8> Vars; for (Expr *RefExpr : Locators) { - assert(RefExpr && "NULL expr in OpenMP shared clause."); + assert(RefExpr && "NULL expr in OpenMP affinity clause."); if (isa<DependentScopeDeclRefExpr>(RefExpr) || RefExpr->isTypeDependent()) { // It will be analyzed later. Vars.push_back(RefExpr); @@ -24375,7 +24392,7 @@ ExprResult SemaOpenMP::ActOnOMPArraySectionExpr( return ExprError(); } } - } else if (ColonLocFirst.isValid() && + } else if (SemaRef.getLangOpts().OpenMP < 60 && ColonLocFirst.isValid() && (OriginalTy.isNull() || (!OriginalTy->isConstantArrayType() && !OriginalTy->isVariableArrayType()))) { // OpenMP 5.0, [2.1.5 Array Sections] diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 5dd5b49..76e189d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -8042,8 +8042,8 @@ static void AddTemplateOverloadCandidateImmediately( Candidate.IgnoreObjectArgument = isa<CXXMethodDecl>(Candidate.Function) && - cast<CXXMethodDecl>(Candidate.Function) - ->isImplicitObjectMemberFunction() && + !cast<CXXMethodDecl>(Candidate.Function) + ->isExplicitObjectMemberFunction() && !isa<CXXConstructorDecl>(Candidate.Function); Candidate.ExplicitCallArguments = Args.size(); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index f85826a..3f89843 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -295,8 +295,7 @@ void DiagnoseUnused(Sema &S, const Expr *E, std::optional<unsigned> DiagID) { return; auto [OffendingDecl, A] = CE->getUnusedResultAttr(S.Context); - if (DiagnoseNoDiscard(S, OffendingDecl, - cast_or_null<WarnUnusedResultAttr>(A), Loc, R1, R2, + if (DiagnoseNoDiscard(S, OffendingDecl, A, Loc, R1, R2, /*isCtor=*/false)) return; @@ -344,13 +343,11 @@ void DiagnoseUnused(Sema &S, const Expr *E, std::optional<unsigned> DiagID) { S.Diag(Loc, diag::err_arc_unused_init_message) << R1; return; } - const ObjCMethodDecl *MD = ME->getMethodDecl(); - if (MD) { - if (DiagnoseNoDiscard(S, nullptr, MD->getAttr<WarnUnusedResultAttr>(), - Loc, R1, R2, - /*isCtor=*/false)) - return; - } + + auto [OffendingDecl, A] = ME->getUnusedResultAttr(S.Context); + if (DiagnoseNoDiscard(S, OffendingDecl, A, Loc, R1, R2, + /*isCtor=*/false)) + return; } else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) { const Expr *Source = POE->getSyntacticForm(); // Handle the actually selected call of an OpenMP specialized call. diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 857d46a..77aa716 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -795,6 +795,10 @@ ExprResult Sema::BuildCXXAssumeExpr(Expr *Assumption, if (Res.isInvalid()) return ExprError(); + Res = ActOnFinishFullExpr(Res.get(), /*DiscardedValue=*/false); + if (Res.isInvalid()) + return ExprError(); + Assumption = Res.get(); if (Assumption->HasSideEffects(Context)) Diag(Assumption->getBeginLoc(), diag::warn_assume_side_effects) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 698d127..21fed2e 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4749,8 +4749,6 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS, EnterExpressionEvaluationContext EECtx{ *this, ExpressionEvaluationContext::Unevaluated, CSD}; - ContextRAII CurContext(*this, CSD->getDeclContext(), - /*NewThisContext=*/false); if (!AreArgsDependent && CheckConstraintSatisfaction( NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()), diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index e1a975b..9e56e697 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5523,6 +5523,15 @@ static TemplateDeductionResult CheckDeductionConsistency( // FIXME: A substitution can be incomplete on a non-structural part of the // type. Use the canonical type for now, until the TemplateInstantiator can // deal with that. + + // Workaround: Implicit deduction guides use InjectedClassNameTypes, whereas + // the explicit guides don't. The substitution doesn't transform these types, + // so let it transform their specializations instead. + bool IsDeductionGuide = isa<CXXDeductionGuideDecl>(FTD->getTemplatedDecl()); + if (IsDeductionGuide) { + if (auto *Injected = P->getAs<InjectedClassNameType>()) + P = Injected->getInjectedSpecializationType(); + } QualType InstP = S.SubstType(P.getCanonicalType(), MLTAL, FTD->getLocation(), FTD->getDeclName(), &IsIncompleteSubstitution); if (InstP.isNull() && !IsIncompleteSubstitution) @@ -5537,9 +5546,15 @@ static TemplateDeductionResult CheckDeductionConsistency( if (auto *PA = dyn_cast<PackExpansionType>(A); PA && !isa<PackExpansionType>(InstP)) A = PA->getPattern(); - if (!S.Context.hasSameType( - S.Context.getUnqualifiedArrayType(InstP.getNonReferenceType()), - S.Context.getUnqualifiedArrayType(A.getNonReferenceType()))) + auto T1 = S.Context.getUnqualifiedArrayType(InstP.getNonReferenceType()); + auto T2 = S.Context.getUnqualifiedArrayType(A.getNonReferenceType()); + if (IsDeductionGuide) { + if (auto *Injected = T1->getAs<InjectedClassNameType>()) + T1 = Injected->getInjectedSpecializationType(); + if (auto *Injected = T2->getAs<InjectedClassNameType>()) + T2 = Injected->getInjectedSpecializationType(); + } + if (!S.Context.hasSameType(T1, T2)) return TemplateDeductionResult::NonDeducedMismatch; return TemplateDeductionResult::Success; } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 20bac0e..d84d0ca1 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2270,11 +2270,6 @@ TemplateInstantiator::TransformCXXAssumeAttr(const CXXAssumeAttr *AA) { if (!Res.isUsable()) return AA; - Res = getSema().ActOnFinishFullExpr(Res.get(), - /*DiscardedValue=*/false); - if (!Res.isUsable()) - return AA; - if (!(Res.get()->getDependence() & ExprDependence::TypeValueInstantiation)) { Res = getSema().BuildCXXAssumeExpr(Res.get(), AA->getAttrName(), AA->getRange()); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e2c3cdc..233bb65 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5853,6 +5853,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, // context seems wrong. Investigate more. ActOnFinishFunctionBody(Function, Body.get(), /*IsInstantiation=*/true); + checkReferenceToTULocalFromOtherTU(Function, PointOfInstantiation); + PerformDependentDiagnostics(PatternDecl, TemplateArgs); if (auto *Listener = getASTMutationListener()) diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 1d8687e..c2f0600 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -11,9 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/DeclCXX.h" -#include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" -#include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/DiagnosticParse.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/TypeTraits.h" @@ -1965,7 +1963,6 @@ static std::optional<TypeTrait> StdNameToTypeTrait(StringRef Name) { .Case("is_assignable", TypeTrait::BTT_IsAssignable) .Case("is_empty", TypeTrait::UTT_IsEmpty) .Case("is_standard_layout", TypeTrait::UTT_IsStandardLayout) - .Case("is_constructible", TypeTrait::TT_IsConstructible) .Default(std::nullopt); } @@ -2002,16 +1999,8 @@ static ExtractedTypeTraitInfo ExtractTypeTraitFromExpression(const Expr *E) { Trait = StdNameToTypeTrait(Name); if (!Trait) return std::nullopt; - for (const auto &Arg : VD->getTemplateArgs().asArray()) { - if (Arg.getKind() == TemplateArgument::ArgKind::Pack) { - for (const auto &InnerArg : Arg.pack_elements()) - Args.push_back(InnerArg.getAsType()); - } else if (Arg.getKind() == TemplateArgument::ArgKind::Type) { - Args.push_back(Arg.getAsType()); - } else { - llvm_unreachable("Unexpected kind"); - } - } + for (const auto &Arg : VD->getTemplateArgs().asArray()) + Args.push_back(Arg.getAsType()); return {{Trait.value(), std::move(Args)}}; } @@ -2284,60 +2273,6 @@ static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef, } } -static void DiagnoseNonConstructibleReason( - Sema &SemaRef, SourceLocation Loc, - const llvm::SmallVector<clang::QualType, 1> &Ts) { - if (Ts.empty()) { - return; - } - - bool ContainsVoid = false; - for (const QualType &ArgTy : Ts) { - ContainsVoid |= ArgTy->isVoidType(); - } - - if (ContainsVoid) - SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) - << diag::TraitNotSatisfiedReason::CVVoidType; - - QualType T = Ts[0]; - if (T->isFunctionType()) - SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) - << diag::TraitNotSatisfiedReason::FunctionType; - - if (T->isIncompleteArrayType()) - SemaRef.Diag(Loc, diag::note_unsatisfied_trait_reason) - << diag::TraitNotSatisfiedReason::IncompleteArrayType; - - const CXXRecordDecl *D = T->getAsCXXRecordDecl(); - if (!D || D->isInvalidDecl() || !D->hasDefinition()) - return; - - llvm::BumpPtrAllocator OpaqueExprAllocator; - SmallVector<Expr *, 2> ArgExprs; - ArgExprs.reserve(Ts.size() - 1); - for (unsigned I = 1, N = Ts.size(); I != N; ++I) { - QualType ArgTy = Ts[I]; - if (ArgTy->isObjectType() || ArgTy->isFunctionType()) - ArgTy = SemaRef.Context.getRValueReferenceType(ArgTy); - ArgExprs.push_back( - new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>()) - OpaqueValueExpr(Loc, ArgTy.getNonLValueExprType(SemaRef.Context), - Expr::getValueKindForType(ArgTy))); - } - - EnterExpressionEvaluationContext Unevaluated( - SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::ContextRAII TUContext(SemaRef, - SemaRef.Context.getTranslationUnitDecl()); - InitializedEntity To(InitializedEntity::InitializeTemporary(T)); - InitializationKind InitKind(InitializationKind::CreateDirect(Loc, Loc, Loc)); - InitializationSequence Init(SemaRef, To, InitKind, ArgExprs); - - Init.Diagnose(SemaRef, To, InitKind, ArgExprs); - SemaRef.Diag(D->getLocation(), diag::note_defined_here) << D; -} - static void DiagnoseNonTriviallyCopyableReason(Sema &SemaRef, SourceLocation Loc, QualType T) { SemaRef.Diag(Loc, diag::note_unsatisfied_trait) @@ -2624,9 +2559,6 @@ void Sema::DiagnoseTypeTraitDetails(const Expr *E) { case UTT_IsStandardLayout: DiagnoseNonStandardLayoutReason(*this, E->getBeginLoc(), Args[0]); break; - case TT_IsConstructible: - DiagnoseNonConstructibleReason(*this, E->getBeginLoc(), Args); - break; default: break; } diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 6faea24..8998492 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -227,6 +227,53 @@ bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) { return false; } +bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + + Expr *FuncPtrArg = TheCall->getArg(0); + QualType ArgType = FuncPtrArg->getType(); + + // Check that the argument is a function pointer + const PointerType *PtrTy = ArgType->getAs<PointerType>(); + if (!PtrTy) { + return Diag(FuncPtrArg->getBeginLoc(), + diag::err_typecheck_expect_function_pointer) + << ArgType << FuncPtrArg->getSourceRange(); + } + + const FunctionProtoType *FuncTy = + PtrTy->getPointeeType()->getAs<FunctionProtoType>(); + if (!FuncTy) { + return Diag(FuncPtrArg->getBeginLoc(), + diag::err_typecheck_expect_function_pointer) + << ArgType << FuncPtrArg->getSourceRange(); + } + + // Check that the function pointer doesn't use reference types + if (FuncTy->getReturnType().isWebAssemblyReferenceType()) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag::err_wasm_builtin_test_fp_sig_cannot_include_reference_type) + << 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange(); + } + auto NParams = FuncTy->getNumParams(); + for (unsigned I = 0; I < NParams; I++) { + if (FuncTy->getParamType(I).isWebAssemblyReferenceType()) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag:: + err_wasm_builtin_test_fp_sig_cannot_include_reference_type) + << 1 << FuncPtrArg->getSourceRange(); + } + } + + // Set return type to int (the result of the test) + TheCall->setType(getASTContext().IntTy); + + return false; +} + bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { @@ -249,6 +296,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, return BuiltinWasmTableFill(TheCall); case WebAssembly::BI__builtin_wasm_table_copy: return BuiltinWasmTableCopy(TheCall); + case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: + return BuiltinWasmTestFunctionPointerSignature(TheCall); } return false; |