diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4502,6 +4502,8 @@ def err_ovl_no_viable_function_in_call : Error< "no matching function for call to %0">; +def err_ovl_exposure_function_in_call : Error< + "the overload set for the call to %0 contains one or more exposures">; def err_ovl_no_viable_member_function_in_call : Error< "no matching member function for call to %0">; def err_ovl_ambiguous_call : Error< @@ -4589,6 +4591,7 @@ def note_ovl_candidate_has_pass_object_size_params: Note< "candidate address cannot be taken because parameter %0 has " "pass_object_size attribute">; +def note_ovl_candidate_is_exposure: Note<"candidate %0 is an exposure">; def err_diagnose_if_succeeded : Error<"%0">; def warn_diagnose_if_succeeded : Warning<"%0">, InGroup, ShowInSystemHeader; @@ -4724,6 +4727,8 @@ def note_ovl_builtin_candidate : Note<"built-in candidate %0">; def err_ovl_no_viable_function_in_init : Error< "no matching constructor for initialization of %0">; +def err_ovl_conversion_has_exposure : Error< + "the overload set for conversion from %1 to %2 contains an exposure">; def err_ovl_no_conversion_in_cast : Error< "cannot convert %1 to %2 without a conversion operator">; def err_ovl_no_viable_conversion_in_cast : Error< @@ -4790,6 +4795,8 @@ "reference to non-static member function must be called" "%select{|; did you mean to call it with no arguments?}0">; def note_possible_target_of_call : Note<"possible target for call">; +def err_ovl_destructor_has_exposure : Error< + "the overload set for class %0 destructor contains an exposure">; def err_no_viable_destructor : Error< "no viable destructor found for class %0">; def err_ambiguous_destructor : Error< @@ -9432,6 +9439,9 @@ "defaulted %0 is implicitly deleted because there is no viable " "%select{three-way comparison function|'operator=='}1 for " "%select{|member |base class }2%3">; +def err_ovl_defaulted_comparison_exposure : Error<"the overload set for " + "%select{three-way comparison function|'operator=='}1 for " + "%select{|member |base class }2%3 contains an exposure">; def note_defaulted_comparison_no_viable_function_synthesized : Note< "three-way comparison cannot be synthesized because there is no viable " "function for %select{'=='|'<'}0 comparison">; @@ -11320,6 +11330,9 @@ "'%0' included multiple times, additional include site in header from module '%1'">; def note_redefinition_include_same_file : Note< "'%0' included multiple times, additional include site here">; + +def err_use_is_exposure : Error<"use of TU-local entity %0 is an exposure">; + } let CategoryName = "Coroutines Issue" in { diff --git a/clang/include/clang/Sema/Overload.h b/clang/include/clang/Sema/Overload.h --- a/clang/include/clang/Sema/Overload.h +++ b/clang/include/clang/Sema/Overload.h @@ -47,328 +47,334 @@ /// OverloadingResult - Capture the result of performing overload /// resolution. - enum OverloadingResult { - /// Overload resolution succeeded. - OR_Success, +enum OverloadingResult { + /// Overload resolution succeeded. + OR_Success, - /// No viable function found. - OR_No_Viable_Function, + /// No viable function found. + OR_No_Viable_Function, - /// Ambiguous candidates found. - OR_Ambiguous, + /// Ambiguous candidates found. + OR_Ambiguous, - /// Succeeded, but refers to a deleted function. - OR_Deleted - }; + /// Succeeded, but refers to a deleted function. + OR_Deleted, - enum OverloadCandidateDisplayKind { - /// Requests that all candidates be shown. Viable candidates will - /// be printed first. - OCD_AllCandidates, + /// At least one candidate is an exposure. + OR_Exposure +}; - /// Requests that only viable candidates be shown. - OCD_ViableCandidates, +enum OverloadCandidateDisplayKind { + /// Requests that all candidates be shown. Viable candidates will + /// be printed first. + OCD_AllCandidates, - /// Requests that only tied-for-best candidates be shown. - OCD_AmbiguousCandidates - }; + /// Requests that only viable candidates be shown. + OCD_ViableCandidates, - /// The parameter ordering that will be used for the candidate. This is - /// used to represent C++20 binary operator rewrites that reverse the order - /// of the arguments. If the parameter ordering is Reversed, the Args list is - /// reversed (but obviously the ParamDecls for the function are not). - /// - /// After forming an OverloadCandidate with reversed parameters, the list - /// of conversions will (as always) be indexed by argument, so will be - /// in reverse parameter order. - enum class OverloadCandidateParamOrder : char { Normal, Reversed }; - - /// The kinds of rewrite we perform on overload candidates. Note that the - /// values here are chosen to serve as both bitflags and as a rank (lower - /// values are preferred by overload resolution). - enum OverloadCandidateRewriteKind : unsigned { - /// Candidate is not a rewritten candidate. - CRK_None = 0x0, - - /// Candidate is a rewritten candidate with a different operator name. - CRK_DifferentOperator = 0x1, - - /// Candidate is a rewritten candidate with a reversed order of parameters. - CRK_Reversed = 0x2, - }; + /// Requests that only tied-for-best candidates be shown. + OCD_AmbiguousCandidates, - /// ImplicitConversionKind - The kind of implicit conversion used to - /// convert an argument to a parameter's type. The enumerator values - /// match with the table titled 'Conversions' in [over.ics.scs] and are listed - /// such that better conversion kinds have smaller values. - enum ImplicitConversionKind { - /// Identity conversion (no conversion) - ICK_Identity = 0, + /// Requests that only candidates that are exposures be shown. + OCD_ExposureCandidates +}; - /// Lvalue-to-rvalue conversion (C++ [conv.lval]) - ICK_Lvalue_To_Rvalue, +/// The parameter ordering that will be used for the candidate. This is +/// used to represent C++20 binary operator rewrites that reverse the order +/// of the arguments. If the parameter ordering is Reversed, the Args list is +/// reversed (but obviously the ParamDecls for the function are not). +/// +/// After forming an OverloadCandidate with reversed parameters, the list +/// of conversions will (as always) be indexed by argument, so will be +/// in reverse parameter order. +enum class OverloadCandidateParamOrder : char { Normal, Reversed }; - /// Array-to-pointer conversion (C++ [conv.array]) - ICK_Array_To_Pointer, +/// The kinds of rewrite we perform on overload candidates. Note that the +/// values here are chosen to serve as both bitflags and as a rank (lower +/// values are preferred by overload resolution). +enum OverloadCandidateRewriteKind : unsigned { + /// Candidate is not a rewritten candidate. + CRK_None = 0x0, - /// Function-to-pointer (C++ [conv.array]) - ICK_Function_To_Pointer, + /// Candidate is a rewritten candidate with a different operator name. + CRK_DifferentOperator = 0x1, - /// Function pointer conversion (C++17 [conv.fctptr]) - ICK_Function_Conversion, + /// Candidate is a rewritten candidate with a reversed order of parameters. + CRK_Reversed = 0x2, +}; - /// Qualification conversions (C++ [conv.qual]) - ICK_Qualification, +/// ImplicitConversionKind - The kind of implicit conversion used to +/// convert an argument to a parameter's type. The enumerator values +/// match with the table titled 'Conversions' in [over.ics.scs] and are listed +/// such that better conversion kinds have smaller values. +enum ImplicitConversionKind { + /// Identity conversion (no conversion) + ICK_Identity = 0, - /// Integral promotions (C++ [conv.prom]) - ICK_Integral_Promotion, + /// Lvalue-to-rvalue conversion (C++ [conv.lval]) + ICK_Lvalue_To_Rvalue, - /// Floating point promotions (C++ [conv.fpprom]) - ICK_Floating_Promotion, + /// Array-to-pointer conversion (C++ [conv.array]) + ICK_Array_To_Pointer, - /// Complex promotions (Clang extension) - ICK_Complex_Promotion, + /// Function-to-pointer (C++ [conv.array]) + ICK_Function_To_Pointer, - /// Integral conversions (C++ [conv.integral]) - ICK_Integral_Conversion, + /// Function pointer conversion (C++17 [conv.fctptr]) + ICK_Function_Conversion, - /// Floating point conversions (C++ [conv.double] - ICK_Floating_Conversion, + /// Qualification conversions (C++ [conv.qual]) + ICK_Qualification, - /// Complex conversions (C99 6.3.1.6) - ICK_Complex_Conversion, + /// Integral promotions (C++ [conv.prom]) + ICK_Integral_Promotion, - /// Floating-integral conversions (C++ [conv.fpint]) - ICK_Floating_Integral, + /// Floating point promotions (C++ [conv.fpprom]) + ICK_Floating_Promotion, - /// Pointer conversions (C++ [conv.ptr]) - ICK_Pointer_Conversion, + /// Complex promotions (Clang extension) + ICK_Complex_Promotion, - /// Pointer-to-member conversions (C++ [conv.mem]) - ICK_Pointer_Member, + /// Integral conversions (C++ [conv.integral]) + ICK_Integral_Conversion, - /// Boolean conversions (C++ [conv.bool]) - ICK_Boolean_Conversion, + /// Floating point conversions (C++ [conv.double] + ICK_Floating_Conversion, - /// Conversions between compatible types in C99 - ICK_Compatible_Conversion, + /// Complex conversions (C99 6.3.1.6) + ICK_Complex_Conversion, - /// Derived-to-base (C++ [over.best.ics]) - ICK_Derived_To_Base, + /// Floating-integral conversions (C++ [conv.fpint]) + ICK_Floating_Integral, - /// Vector conversions - ICK_Vector_Conversion, + /// Pointer conversions (C++ [conv.ptr]) + ICK_Pointer_Conversion, - /// Arm SVE Vector conversions - ICK_SVE_Vector_Conversion, + /// Pointer-to-member conversions (C++ [conv.mem]) + ICK_Pointer_Member, - /// RISC-V RVV Vector conversions - ICK_RVV_Vector_Conversion, + /// Boolean conversions (C++ [conv.bool]) + ICK_Boolean_Conversion, - /// A vector splat from an arithmetic type - ICK_Vector_Splat, + /// Conversions between compatible types in C99 + ICK_Compatible_Conversion, - /// Complex-real conversions (C99 6.3.1.7) - ICK_Complex_Real, + /// Derived-to-base (C++ [over.best.ics]) + ICK_Derived_To_Base, - /// Block Pointer conversions - ICK_Block_Pointer_Conversion, + /// Vector conversions + ICK_Vector_Conversion, - /// Transparent Union Conversions - ICK_TransparentUnionConversion, + /// Arm SVE Vector conversions + ICK_SVE_Vector_Conversion, - /// Objective-C ARC writeback conversion - ICK_Writeback_Conversion, + /// RISC-V RVV Vector conversions + ICK_RVV_Vector_Conversion, - /// Zero constant to event (OpenCL1.2 6.12.10) - ICK_Zero_Event_Conversion, + /// A vector splat from an arithmetic type + ICK_Vector_Splat, - /// Zero constant to queue - ICK_Zero_Queue_Conversion, + /// Complex-real conversions (C99 6.3.1.7) + ICK_Complex_Real, - /// Conversions allowed in C, but not C++ - ICK_C_Only_Conversion, + /// Block Pointer conversions + ICK_Block_Pointer_Conversion, - /// C-only conversion between pointers with incompatible types - ICK_Incompatible_Pointer_Conversion, + /// Transparent Union Conversions + ICK_TransparentUnionConversion, - /// The number of conversion kinds - ICK_Num_Conversion_Kinds, - }; + /// Objective-C ARC writeback conversion + ICK_Writeback_Conversion, - /// ImplicitConversionRank - The rank of an implicit conversion - /// kind. The enumerator values match with Table 9 of (C++ - /// 13.3.3.1.1) and are listed such that better conversion ranks - /// have smaller values. - enum ImplicitConversionRank { - /// Exact Match - ICR_Exact_Match = 0, + /// Zero constant to event (OpenCL1.2 6.12.10) + ICK_Zero_Event_Conversion, - /// Promotion - ICR_Promotion, + /// Zero constant to queue + ICK_Zero_Queue_Conversion, - /// Conversion - ICR_Conversion, + /// Conversions allowed in C, but not C++ + ICK_C_Only_Conversion, - /// OpenCL Scalar Widening - ICR_OCL_Scalar_Widening, + /// C-only conversion between pointers with incompatible types + ICK_Incompatible_Pointer_Conversion, - /// Complex <-> Real conversion - ICR_Complex_Real_Conversion, + /// The number of conversion kinds + ICK_Num_Conversion_Kinds, +}; - /// ObjC ARC writeback conversion - ICR_Writeback_Conversion, +/// ImplicitConversionRank - The rank of an implicit conversion +/// kind. The enumerator values match with Table 9 of (C++ +/// 13.3.3.1.1) and are listed such that better conversion ranks +/// have smaller values. +enum ImplicitConversionRank { + /// Exact Match + ICR_Exact_Match = 0, - /// Conversion only allowed in the C standard (e.g. void* to char*). - ICR_C_Conversion, + /// Promotion + ICR_Promotion, - /// Conversion not allowed by the C standard, but that we accept as an - /// extension anyway. - ICR_C_Conversion_Extension - }; + /// Conversion + ICR_Conversion, - ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); + /// OpenCL Scalar Widening + ICR_OCL_Scalar_Widening, - /// NarrowingKind - The kind of narrowing conversion being performed by a - /// standard conversion sequence according to C++11 [dcl.init.list]p7. - enum NarrowingKind { - /// Not a narrowing conversion. - NK_Not_Narrowing, + /// Complex <-> Real conversion + ICR_Complex_Real_Conversion, - /// A narrowing conversion by virtue of the source and destination types. - NK_Type_Narrowing, + /// ObjC ARC writeback conversion + ICR_Writeback_Conversion, - /// A narrowing conversion, because a constant expression got narrowed. - NK_Constant_Narrowing, + /// Conversion only allowed in the C standard (e.g. void* to char*). + ICR_C_Conversion, - /// A narrowing conversion, because a non-constant-expression variable might - /// have got narrowed. - NK_Variable_Narrowing, + /// Conversion not allowed by the C standard, but that we accept as an + /// extension anyway. + ICR_C_Conversion_Extension +}; - /// Cannot tell whether this is a narrowing conversion because the - /// expression is value-dependent. - NK_Dependent_Narrowing, - }; +ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); - /// StandardConversionSequence - represents a standard conversion - /// sequence (C++ 13.3.3.1.1). A standard conversion sequence - /// contains between zero and three conversions. If a particular - /// conversion is not needed, it will be set to the identity conversion - /// (ICK_Identity). Note that the three conversions are - /// specified as separate members (rather than in an array) so that - /// we can keep the size of a standard conversion sequence to a - /// single word. - class StandardConversionSequence { - public: - /// First -- The first conversion can be an lvalue-to-rvalue - /// conversion, array-to-pointer conversion, or - /// function-to-pointer conversion. - ImplicitConversionKind First : 8; - - /// Second - The second conversion can be an integral promotion, - /// floating point promotion, integral conversion, floating point - /// conversion, floating-integral conversion, pointer conversion, - /// pointer-to-member conversion, or boolean conversion. - ImplicitConversionKind Second : 8; - - /// Third - The third conversion can be a qualification conversion - /// or a function conversion. - ImplicitConversionKind Third : 8; - - /// Whether this is the deprecated conversion of a - /// string literal to a pointer to non-const character data - /// (C++ 4.2p2). - unsigned DeprecatedStringLiteralToCharPtr : 1; - - /// Whether the qualification conversion involves a change in the - /// Objective-C lifetime (for automatic reference counting). - unsigned QualificationIncludesObjCLifetime : 1; - - /// IncompatibleObjC - Whether this is an Objective-C conversion - /// that we should warn about (if we actually use it). - unsigned IncompatibleObjC : 1; - - /// ReferenceBinding - True when this is a reference binding - /// (C++ [over.ics.ref]). - unsigned ReferenceBinding : 1; - - /// DirectBinding - True when this is a reference binding that is a - /// direct binding (C++ [dcl.init.ref]). - unsigned DirectBinding : 1; - - /// Whether this is an lvalue reference binding (otherwise, it's - /// an rvalue reference binding). - unsigned IsLvalueReference : 1; - - /// Whether we're binding to a function lvalue. - unsigned BindsToFunctionLvalue : 1; - - /// Whether we're binding to an rvalue. - unsigned BindsToRvalue : 1; - - /// Whether this binds an implicit object argument to a - /// non-static member function without a ref-qualifier. - unsigned BindsImplicitObjectArgumentWithoutRefQualifier : 1; - - /// Whether this binds a reference to an object with a different - /// Objective-C lifetime qualifier. - unsigned ObjCLifetimeConversionBinding : 1; - - /// FromType - The type that this conversion is converting - /// from. This is an opaque pointer that can be translated into a - /// QualType. - void *FromTypePtr; +/// NarrowingKind - The kind of narrowing conversion being performed by a +/// standard conversion sequence according to C++11 [dcl.init.list]p7. +enum NarrowingKind { + /// Not a narrowing conversion. + NK_Not_Narrowing, - /// ToType - The types that this conversion is converting to in - /// each step. This is an opaque pointer that can be translated - /// into a QualType. - void *ToTypePtrs[3]; + /// A narrowing conversion by virtue of the source and destination types. + NK_Type_Narrowing, - /// CopyConstructor - The copy constructor that is used to perform - /// this conversion, when the conversion is actually just the - /// initialization of an object via copy constructor. Such - /// conversions are either identity conversions or derived-to-base - /// conversions. - CXXConstructorDecl *CopyConstructor; - DeclAccessPair FoundCopyConstructor; + /// A narrowing conversion, because a constant expression got narrowed. + NK_Constant_Narrowing, - void setFromType(QualType T) { FromTypePtr = T.getAsOpaquePtr(); } + /// A narrowing conversion, because a non-constant-expression variable might + /// have got narrowed. + NK_Variable_Narrowing, - void setToType(unsigned Idx, QualType T) { - assert(Idx < 3 && "To type index is out of range"); - ToTypePtrs[Idx] = T.getAsOpaquePtr(); - } + /// Cannot tell whether this is a narrowing conversion because the + /// expression is value-dependent. + NK_Dependent_Narrowing, +}; - void setAllToTypes(QualType T) { - ToTypePtrs[0] = T.getAsOpaquePtr(); - ToTypePtrs[1] = ToTypePtrs[0]; - ToTypePtrs[2] = ToTypePtrs[0]; - } +/// StandardConversionSequence - represents a standard conversion +/// sequence (C++ 13.3.3.1.1). A standard conversion sequence +/// contains between zero and three conversions. If a particular +/// conversion is not needed, it will be set to the identity conversion +/// (ICK_Identity). Note that the three conversions are +/// specified as separate members (rather than in an array) so that +/// we can keep the size of a standard conversion sequence to a +/// single word. +class StandardConversionSequence { +public: + /// First -- The first conversion can be an lvalue-to-rvalue + /// conversion, array-to-pointer conversion, or + /// function-to-pointer conversion. + ImplicitConversionKind First : 8; + + /// Second - The second conversion can be an integral promotion, + /// floating point promotion, integral conversion, floating point + /// conversion, floating-integral conversion, pointer conversion, + /// pointer-to-member conversion, or boolean conversion. + ImplicitConversionKind Second : 8; + + /// Third - The third conversion can be a qualification conversion + /// or a function conversion. + ImplicitConversionKind Third : 8; + + /// Whether this is the deprecated conversion of a + /// string literal to a pointer to non-const character data + /// (C++ 4.2p2). + unsigned DeprecatedStringLiteralToCharPtr : 1; + + /// Whether the qualification conversion involves a change in the + /// Objective-C lifetime (for automatic reference counting). + unsigned QualificationIncludesObjCLifetime : 1; + + /// IncompatibleObjC - Whether this is an Objective-C conversion + /// that we should warn about (if we actually use it). + unsigned IncompatibleObjC : 1; + + /// ReferenceBinding - True when this is a reference binding + /// (C++ [over.ics.ref]). + unsigned ReferenceBinding : 1; + + /// DirectBinding - True when this is a reference binding that is a + /// direct binding (C++ [dcl.init.ref]). + unsigned DirectBinding : 1; + + /// Whether this is an lvalue reference binding (otherwise, it's + /// an rvalue reference binding). + unsigned IsLvalueReference : 1; + + /// Whether we're binding to a function lvalue. + unsigned BindsToFunctionLvalue : 1; + + /// Whether we're binding to an rvalue. + unsigned BindsToRvalue : 1; + + /// Whether this binds an implicit object argument to a + /// non-static member function without a ref-qualifier. + unsigned BindsImplicitObjectArgumentWithoutRefQualifier : 1; + + /// Whether this binds a reference to an object with a different + /// Objective-C lifetime qualifier. + unsigned ObjCLifetimeConversionBinding : 1; + + /// FromType - The type that this conversion is converting + /// from. This is an opaque pointer that can be translated into a + /// QualType. + void *FromTypePtr; + + /// ToType - The types that this conversion is converting to in + /// each step. This is an opaque pointer that can be translated + /// into a QualType. + void *ToTypePtrs[3]; + + /// CopyConstructor - The copy constructor that is used to perform + /// this conversion, when the conversion is actually just the + /// initialization of an object via copy constructor. Such + /// conversions are either identity conversions or derived-to-base + /// conversions. + CXXConstructorDecl *CopyConstructor; + DeclAccessPair FoundCopyConstructor; + + void setFromType(QualType T) { FromTypePtr = T.getAsOpaquePtr(); } + + void setToType(unsigned Idx, QualType T) { + assert(Idx < 3 && "To type index is out of range"); + ToTypePtrs[Idx] = T.getAsOpaquePtr(); + } - QualType getFromType() const { - return QualType::getFromOpaquePtr(FromTypePtr); - } + void setAllToTypes(QualType T) { + ToTypePtrs[0] = T.getAsOpaquePtr(); + ToTypePtrs[1] = ToTypePtrs[0]; + ToTypePtrs[2] = ToTypePtrs[0]; + } - QualType getToType(unsigned Idx) const { - assert(Idx < 3 && "To type index is out of range"); - return QualType::getFromOpaquePtr(ToTypePtrs[Idx]); - } + QualType getFromType() const { + return QualType::getFromOpaquePtr(FromTypePtr); + } - void setAsIdentityConversion(); + QualType getToType(unsigned Idx) const { + assert(Idx < 3 && "To type index is out of range"); + return QualType::getFromOpaquePtr(ToTypePtrs[Idx]); + } - bool isIdentityConversion() const { - return Second == ICK_Identity && Third == ICK_Identity; - } + void setAsIdentityConversion(); - ImplicitConversionRank getRank() const; - NarrowingKind - getNarrowingKind(ASTContext &Context, const Expr *Converted, - APValue &ConstantValue, QualType &ConstantType, - bool IgnoreFloatToIntegralConversion = false) const; - bool isPointerConversionToBool() const; - bool isPointerConversionToVoidPointer(ASTContext& Context) const; - void dump() const; - }; + bool isIdentityConversion() const { + return Second == ICK_Identity && Third == ICK_Identity; + } + + ImplicitConversionRank getRank() const; + NarrowingKind + getNarrowingKind(ASTContext &Context, const Expr *Converted, + APValue &ConstantValue, QualType &ConstantType, + bool IgnoreFloatToIntegralConversion = false) const; + bool isPointerConversionToBool() const; + bool isPointerConversionToVoidPointer(ASTContext &Context) const; + void dump() const; +}; /// UserDefinedConversionSequence - Represents a user-defined /// conversion sequence (C++ 13.3.3.1.2). @@ -809,9 +815,13 @@ /// not satisfied. ovl_fail_constraints_not_satisfied, - /// This candidate was not viable because it has internal linkage and is - /// from a different module unit than the use. + /// This candidate was not viable because it has module linkage and is + /// from a different module than the use. ovl_fail_module_mismatched, + + /// This candidate was not viable because it has internal linkage and is + /// is an exposure of a declaration in a different TU of this module. + ovl_fail_module_exposure, }; /// A list of implicit conversion sequences for the arguments of an diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2345,6 +2345,44 @@ bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind); public: + /// Determine of a Decl is TU-local. + bool isTULocal(const NamedDecl *ND) const { + if (ND->getFormalLinkage() == Linkage::InternalLinkage) { +#ifdef PRE_P2788R0 + if (auto *VD = dyn_cast(ND)) { + QualType VT = VD->getType(); + if (!VT.isConstQualified() || VT.isVolatileQualified()) + return true; + if (VD->hasConstantInitialization()) + return false; // non-volatile, const with const init. + } +#endif + return true; + } + return false; + } + + bool isTULocal(const Decl *D) const { + if (auto *ND = dyn_cast(D)) + return isTULocal(ND); + return true; + } + + /// Diagnose cases where use of a TU-local entity in a function body is an + /// 'exposure'. + bool diagnoseFunctionBodyExposures(const NamedDecl *D, SourceLocation Loc); + + /// Diagnose cases where use of a TU-local entity in a variable initializer is + /// an 'exposure'. + bool diagnoseVarInitExposures(const VarDecl *VDecl, const Expr *Init, + bool OnlyLambdaExpr); + + /// Diagnose 'exposure's in an expression. + bool diagnoseExprExposure(const Expr *E); + + /// Diagnose 'exposure's in a function instantiation. + bool diagnoseInstantiationExposure(const FunctionDecl *F, SourceLocation L); + /// Get the module unit whose scope we are currently within. Module *getCurrentModule() const { return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -484,6 +484,10 @@ msg = diag::err_ovl_deleted_conversion_in_cast; howManyCandidates = OCD_ViableCandidates; break; + case OR_Exposure: + msg = diag::err_ovl_conversion_has_exposure; + howManyCandidates = OCD_ExposureCandidates; + break; } candidates.NoteCandidates( diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -8785,6 +8785,22 @@ if (T->isRVVType()) checkRVVTypeSupport(T, NewVD->getLocation(), cast(CurContext)); + + // C++ [basic.link]/14 check exposures where the type of a variable is + // specified by a decltype(). + if (getLangOpts().CPlusPlusModules && isCurrentModulePurview() && + NewVD->getFormalLinkage() >= Linkage::ModuleLinkage) { + QualType Ty = T; + while (Ty->isPointerType() || Ty->isReferenceType()) + Ty = Ty->getPointeeType(); + if (Ty->isDecltypeType()) { + const Expr *E = Ty->getAs()->getUnderlyingExpr(); + if (diagnoseExprExposure(E)) { + NewVD->setInvalidDecl(); + return; + } + } + } } /// Perform semantic checking on a newly-created variable @@ -12834,6 +12850,30 @@ MergeVarDeclTypes(VDecl, Old, /*MergeTypeWithPrevious*/ false); } + // C++ [basic.link]/14 check exposures in the type deduced for a variable. + // We cannot check this in CheckVariableDeclarationType() since auto-ness is + // discarded above. + if (getLangOpts().CPlusPlusModules && !VDecl->isInvalidDecl() && + isCurrentModulePurview() && + (((VDecl->isInline() || VDecl->isConstexpr() || + (VDecl->getType()->getAsCXXRecordDecl() && + VDecl->getType()->getAsCXXRecordDecl()->isLambda())) && + VDecl->getFormalLinkage() >= Linkage::ModuleLinkage) || + (VDecl->isConstexpr() && VDecl->getStorageClass() != SC_Static))) { + const Expr *E = Init; + if (auto *DRE = dyn_cast(Init)) { + // look through references. + if (auto *VD = dyn_cast(DRE->getDecl())) { + if (VD->getType()->isReferenceType()) { + assert(VD->hasInit()); + E = VD->getInit(); + } + } + } + if (E && diagnoseVarInitExposures(VDecl, E, false)) + VDecl->setInvalidDecl(); + } + // Check the deduced type is valid for a variable declaration. CheckVariableDeclarationType(VDecl); return VDecl->isInvalidDecl(); @@ -13258,6 +13298,26 @@ VDecl->setInvalidDecl(); return; } + + // C++ [basic.link]/14 : handle exposures in variable initializers. + // A declaration is an exposure if it either names a TU-local entity + // ... + // or defines a constexpr variable initialized to a TU-local value. + if (getLangOpts().CPlusPlusModules && isCurrentModulePurview()) { + if ((((VDecl->isInline() || VDecl->isConstexpr()) && + VDecl->getFormalLinkage() >= Linkage::ModuleLinkage) || + (VDecl->isConstexpr() && VDecl->getStorageClass() != SC_Static)) && + diagnoseVarInitExposures(VDecl, Init, /*OnlyLambdaExpr*/ false)) { + VDecl->setInvalidDecl(); + return; + } else if (VDecl->getFormalLinkage() >= Linkage::ModuleLinkage && + !VDecl->isTemplated() && + diagnoseVarInitExposures(VDecl, Init, + /*OnlyLambdaExpr*/ true)) { + VDecl->setInvalidDecl(); + return; + } + } } // OpenCL 1.1 6.5.2: "Variables allocated in the __local address space inside @@ -15348,6 +15408,16 @@ AbstractReturnType))) FD->setInvalidDecl(); + // C++ [basic.link]/14 : Diagnose exposure of TU-local entities in function + // return types specified with decltype(). + if (getLangOpts().CPlusPlusModules && !FD->isInvalidDecl() && + !FD->isStatic() && isCurrentModulePurview() && + ResultType->isDecltypeType()) { + const Expr *E = ResultType->getAs()->getUnderlyingExpr(); + if (diagnoseExprExposure(E)) + FD->setInvalidDecl(); + } + if (FnBodyScope) PushDeclContext(FnBodyScope, FD); @@ -18539,6 +18609,11 @@ DisplayKind = OCD_AmbiguousCandidates; break; + case OR_Exposure: + Msg = diag::err_ovl_destructor_has_exposure; + DisplayKind = OCD_ExposureCandidates; + break; + case OR_No_Viable_Function: Msg = diag::err_no_viable_destructor; DisplayKind = OCD_AllCandidates; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -8194,6 +8194,14 @@ R = Result::deleted(); break; + case OR_Exposure: + S.Diag(Subobj.Loc, diag::err_ovl_defaulted_comparison_exposure) + << (OO == OO_ExclaimEqual) << Subobj.Kind << Subobj.Decl; + CandidateSet.NoteCandidates( + S, Args, + CandidateSet.CompleteCandidates(S, OCD_ExposureCandidates, Args, + FD->getLocation())); + break; case OR_No_Viable_Function: // If there's no usable candidate, we're done unless we can rewrite a // '<=>' in terms of '==' and '<'. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -156,16 +156,15 @@ static void diagnoseUseOfInternalDeclInInlineFunction(Sema &S, const NamedDecl *D, SourceLocation Loc) { - // This is disabled under C++; there are too many ways for this to fire in - // contexts where the warning is a false positive, or where it is technically - // correct but benign. - if (S.getLangOpts().CPlusPlus) - return; // Check if this is an inlined function or method. FunctionDecl *Current = S.getCurFunctionDecl(); if (!Current) return; + + // As noted above, this test should not be applied to C++. + assert(!S.getLangOpts().CPlusPlus); + if (!Current->isInlined()) return; if (!Current->isExternallyVisible()) @@ -372,7 +371,12 @@ DiagnoseUnusedOfDecl(*this, D, Loc); - diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); + if (getLangOpts().CPlusPlus) { + // C++ [basic.link]/14 : handle exposures in function bodies. + if (getLangOpts().CPlusPlusModules && diagnoseFunctionBodyExposures(D, Loc)) + return true; + } else + diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); if (D->hasAttr()) { if (getLangOpts().getFPEvalMethod() != diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -2522,6 +2522,7 @@ return false; } + case OR_Exposure: case OR_No_Viable_Function: // C++17 [expr.new]p13: // If no matching function is found and the allocated object type has @@ -3863,6 +3864,7 @@ return false; } + case OR_Exposure: case OR_No_Viable_Function: Candidates.NoteCandidates( PartialDiagnosticAt(R.getNameLoc(), @@ -6243,6 +6245,7 @@ return false; } + case OR_Exposure: case OR_No_Viable_Function: // Emit a better diagnostic if one of the expressions is a null pointer diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6768,6 +6768,7 @@ case OR_Success: break; + case OR_Exposure: case OR_No_Viable_Function: CandidateSet.NoteCandidates( PartialDiagnosticAt( @@ -6922,6 +6923,7 @@ // FIXME: Check default arguments as far as that's possible. break; + case OR_Exposure: case OR_No_Viable_Function: CandidateSet.NoteCandidates(PartialDiagnosticAt(Loc, Diag), S, OCD_AllCandidates, CurInitExpr); @@ -9641,6 +9643,7 @@ S, OCD_AmbiguousCandidates, Args); break; + case OR_Exposure: case OR_No_Viable_Function: { auto Cands = FailedCandidateSet.CompleteCandidates(S, OCD_AllCandidates, Args); if (!S.RequireCompleteType(Kind.getLocation(), @@ -9846,6 +9849,7 @@ S, OCD_AmbiguousCandidates, Args); break; + case OR_Exposure: case OR_No_Viable_Function: if (Kind.getKind() == InitializationKind::IK_Default && (Entity.getKind() == InitializedEntity::EK_Base || @@ -10843,6 +10847,7 @@ *this, OCD_AmbiguousCandidates, Inits); return QualType(); + case OR_Exposure: case OR_No_Viable_Function: { CXXRecordDecl *Primary = cast(Template)->getTemplatedDecl(); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -3551,6 +3551,7 @@ Result->setKind(SpecialMemberOverloadResult::Ambiguous); break; + case OR_Exposure: case OR_No_Viable_Function: Result->setMethod(nullptr); Result->setKind(SpecialMemberOverloadResult::NoMemberOrDeleted); diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" +#include "clang/AST/StmtVisitor.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaInternal.h" @@ -977,3 +978,149 @@ return M->isSubModuleOf(CurrentModuleUnit->getTopLevelModule()); } + +bool Sema::diagnoseFunctionBodyExposures(const NamedDecl *D, + SourceLocation Loc) { + + FunctionDecl *Current = getCurFunctionDecl(); + + if (!Current || Current->isInvalidDecl() || D->isInvalidDecl()) + return false; + + if (Current->isStatic()) + return false; + + if (auto *PD = dyn_cast(D)) + return false; + if (auto *VD = dyn_cast(D)) { + if (VD->getType()->isDependentType()) + return false; + } + + if ((Current->isInlineSpecified() || Current->isConstexpr()) && + isCurrentModulePurview() && + Current->getTemplatedKind() == + FunctionDecl::TemplatedKind::TK_NonTemplate) { + if (isTULocal(D)) { + Diag(Loc, diag::err_use_is_exposure) << D; + return true; + } + } + return false; +} + +class DeclRefIsExposure : public ConstStmtVisitor { +public: + /// Find a DeclRefExpr in the given expression. + static bool check(Sema &S, const Expr *E, SourceLocation L) { + DeclRefIsExposure R(S, L); + R.Visit(E); + return R.getResult(); + } + + /// Find a DeclRefExpr in the given statment. + static bool check(Sema &S, const Stmt *C, SourceLocation L) { + DeclRefIsExposure R(S, L); + R.Visit(C); + return R.getResult(); + } + + void VisitDeclRefExpr(const DeclRefExpr *DR) { + const ValueDecl *V = DR->getDecl(); + + if (S.isTULocal(V)) { + S.Diag(L, diag::err_use_is_exposure) << V; + Result = true; + } + } + void VisitExpr(const Expr *E) { + for (auto *Ch : E->children()) + if (Ch) + Visit(Ch); + } + + void VisitStmt(const Stmt *S) { + for (auto *Ch : S->children()) + if (Ch) + Visit(Ch); + } + + bool getResult() { return Result; } + +private: + DeclRefIsExposure(Sema &S, SourceLocation L) : S(S), L(L) {} + Sema &S; + SourceLocation L; + bool Result = false; +}; + +class LambdaExprIsExposure + : public ConstStmtVisitor { +public: + /// Find a LambdaExpr in the given expression. + static bool check(Sema &S, const Expr *E, SourceLocation L) { + LambdaExprIsExposure R(S, L); + R.Visit(E); + return R.getResult(); + } + + void VisitLambdaExpr(const LambdaExpr *LM) { + auto *LD = LM->getLambdaClass(); + S.Diag(L, diag::err_use_is_exposure) << LD; + Result = true; + } + + void VisitExpr(const Expr *E) { + for (auto *Ch : E->children()) + if (Ch) + Visit(Ch); + } + + void VisitStmt(const Stmt *S) { + for (auto *Ch : S->children()) + if (Ch) + Visit(Ch); + } + + bool getResult() { return Result; } + +private: + LambdaExprIsExposure(Sema &S, SourceLocation L) : S(S), L(L) {} + Sema &S; + SourceLocation L; + bool Result = false; +}; + +bool Sema::diagnoseVarInitExposures(const VarDecl *VDecl, const Expr *Init, + bool OnlyLambdaExpr) { + + bool Res; + if (OnlyLambdaExpr) { + Res = LambdaExprIsExposure::check(*this, Init, VDecl->getLocation()); + } else { + Res = DeclRefIsExposure::check(*this, Init, VDecl->getLocation()); + } + return Res; +} + +bool Sema::diagnoseExprExposure(const Expr *E) { + + if (DeclRefIsExposure::check(*this, E, E->getExprLoc())) { + // llvm::dbgs() << "expr exposure: "; E->dump(); + return true; + } + return false; +} + +bool Sema::diagnoseInstantiationExposure(const FunctionDecl *F, + SourceLocation L) { + + if (F->getOwningModule() && + (F->getOwningModule() != getCurrentModule() || + !currentModuleIsInterface()) && + DeclRefIsExposure::check(*this, F->getBody(), L)) { + // llvm::dbgs() << "expr exposure: "; E->dump(); + return true; + } + return false; +} diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1497,6 +1497,7 @@ break; // Fall through. + case OR_Exposure: case OR_No_Viable_Function: ICS.setBad(BadConversionSequence::no_conversion, From, ToType); break; @@ -3523,6 +3524,8 @@ return Result; } + case OR_Exposure: + return OR_Exposure; case OR_No_Viable_Function: return OR_No_Viable_Function; case OR_Ambiguous: @@ -3749,6 +3752,9 @@ case OR_No_Viable_Function: return OR_No_Viable_Function; + case OR_Exposure: + return OR_Exposure; + case OR_Ambiguous: return OR_Ambiguous; } @@ -4847,6 +4853,7 @@ ICS.Ambiguous.addConversion(Cand->FoundDecl, Cand->Function); return true; + case OR_Exposure: case OR_No_Viable_Function: case OR_Deleted: // There was no suitable conversion, or we found a deleted @@ -6378,6 +6385,7 @@ case OR_Ambiguous: return diagnoseAmbiguousConversion(*this, Loc, From, Converter, T, ViableConversions); + case OR_Exposure: case OR_No_Viable_Function: if (diagnoseNoViableConversion(*this, Loc, From, Converter, T, HadMultipleCandidates, @@ -6542,21 +6550,37 @@ return; } - // Functions with internal linkage are only viable in the same module unit. - if (getLangOpts().CPlusPlusModules && Function->isInAnotherModuleUnit()) { - /// FIXME: Currently, the semantics of linkage in clang is slightly - /// different from the semantics in C++ spec. In C++ spec, only names - /// have linkage. So that all entities of the same should share one - /// linkage. But in clang, different entities of the same could have - /// different linkage. - NamedDecl *ND = Function; - if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) - ND = SpecInfo->getTemplate(); - - if (ND->getFormalLinkage() == Linkage::InternalLinkage) { - Candidate.Viable = false; - Candidate.FailureKind = ovl_fail_module_mismatched; - return; + if (auto *MF = Function->getOwningModule()) { + if (getLangOpts().CPlusPlusModules && !MF->isModuleMapModule()) { + /// FIXME: Currently, the semantics of linkage in clang is slightly + /// different from the semantics in C++ spec. In C++ spec, only names + /// have linkage. So that all entities of the same should share one + /// linkage. But in clang, different entities of the same could have + /// different linkage. + NamedDecl *ND = Function; + if (auto *SpecInfo = Function->getTemplateSpecializationInfo()) + ND = SpecInfo->getTemplate(); + + // We need only check cases that originate from a different TU (i.e. have + // been imported) + if (ND->isInAnotherModuleUnit()) { + if (ND->getFormalLinkage() == Linkage::InternalLinkage) { + // Functions with internal linkage are only viable in the same TU. + // llvm::dbgs() << "exposure: "; + // Function->dump(); + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_module_exposure; + return; + } else if (ND->getFormalLinkage() == Linkage::ModuleLinkage && + !arePartOfSameNamedModule(ND->getOwningModule(), MF)) { + // Functions with module linkage are only viable in the same module. + // llvm::dbgs() << "not part of this module: "; + // Function->dump(); + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_module_mismatched; + return; + } + } } } @@ -10334,6 +10358,10 @@ if (Best == end() || isBetterOverloadCandidate(S, *Cand, *Best, Loc, Kind)) Best = Cand; + } else if (Cand->FailureKind == ovl_fail_module_exposure) { + // This candidate is an exposure and invalidates the whole set. + Best = end(); + return OR_Exposure; } else if (Cand->NotValidBecauseConstraintExprHasError()) { // This candidate has constraint that we were unable to evaluate because // it referenced an expression that contained an error. Rather than fall @@ -11585,7 +11613,12 @@ if (S.CheckFunctionConstraints(Fn, Satisfaction)) break; S.DiagnoseUnsatisfiedConstraint(Satisfaction); + break; } + case ovl_fail_module_exposure: + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_is_exposure) << Fn; + return; + break; } } @@ -11975,6 +12008,11 @@ if (!Cand->Best) continue; break; + + case OCD_ExposureCandidates: + if (Cand->Viable || Cand->FailureKind != ovl_fail_module_exposure) + continue; + break; } Cands.push_back(Cand); @@ -13452,6 +13490,7 @@ (*Best)->IsADLCandidate); } + case OR_Exposure: case OR_No_Viable_Function: { // Try to recover by looking for viable functions which the user might // have meant to call. @@ -13480,9 +13519,14 @@ CandidateSet->NoteCandidates( PartialDiagnosticAt( Fn->getBeginLoc(), - SemaRef.PDiag(diag::err_ovl_no_viable_function_in_call) + SemaRef.PDiag(OverloadResult == OR_Exposure + ? diag::err_ovl_exposure_function_in_call + : diag::err_ovl_no_viable_function_in_call) << ULE->getName() << Fn->getSourceRange()), - SemaRef, OCD_AllCandidates, Args); + SemaRef, + OverloadResult == OR_Exposure ? OCD_ExposureCandidates + : OCD_AllCandidates, + Args); break; } @@ -13734,6 +13778,7 @@ } } + case OR_Exposure: case OR_No_Viable_Function: // This is an erroneous use of an operator which can be overloaded by // a non-member function. Check for non-member operators which were @@ -14181,6 +14226,7 @@ } } + case OR_Exposure: case OR_No_Viable_Function: { // C++ [over.match.oper]p9: // If the operator is the operator , [...] and there are no @@ -14554,6 +14600,7 @@ } } + case OR_Exposure: case OR_No_Viable_Function: { PartialDiagnostic PD = CandidateSet.empty() @@ -14773,6 +14820,7 @@ Succeeded = true; break; + case OR_Exposure: case OR_No_Viable_Function: CandidateSet.NoteCandidates( PartialDiagnosticAt( @@ -15001,6 +15049,7 @@ // below. break; + case OR_Exposure: case OR_No_Viable_Function: { PartialDiagnostic PD = CandidateSet.empty() @@ -15201,6 +15250,7 @@ // Overload resolution succeeded; we'll build the call below. break; + case OR_Exposure: case OR_No_Viable_Function: { auto Cands = CandidateSet.CompleteCandidates(*this, OCD_AllCandidates, Base); if (CandidateSet.empty()) { @@ -15297,6 +15347,7 @@ case OR_Deleted: break; + case OR_Exposure: case OR_No_Viable_Function: CandidateSet.NoteCandidates( PartialDiagnosticAt(UDSuffixLoc, @@ -15403,7 +15454,8 @@ OverloadingResult OverloadResult = CandidateSet->BestViableFunction(*this, Fn->getBeginLoc(), Best); - if (OverloadResult == OR_No_Viable_Function) { + if (OverloadResult == OR_No_Viable_Function || + OverloadResult == OR_Exposure) { *CallExpr = ExprError(); return FRS_NoViableFunction; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5120,6 +5120,9 @@ ActOnFinishFunctionBody(Function, Body.get(), /*IsInstantiation=*/true); PerformDependentDiagnostics(PatternDecl, TemplateArgs); + if (getLangOpts().CPlusPlusModules && !Function->isInvalidDecl() && + diagnoseInstantiationExposure(Function, PointOfInstantiation)) + Function->setInvalidDecl(); if (auto *Listener = getASTMutationListener()) Listener->FunctionDefinitionInstantiated(Function); diff --git a/clang/test/CXX/basic/basic.link/p14-additions.cpp b/clang/test/CXX/basic/basic.link/p14-additions.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.link/p14-additions.cpp @@ -0,0 +1,46 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/A.cpp -fsyntax-only -Wno-unused-value -verify + +//--- A.cpp + +export module A; + +static constexpr int f() { return 0; } +int a = 10; + +// extra decltype() tests + +static decltype(f()) s; // OK + decltype(f()) g; // expected-error {{use of TU-local entity 'f' is an exposure}} +export decltype(f()) e; // expected-error {{use of TU-local entity 'f' is an exposure}} + +static decltype(f()) *fps; // OK + decltype(f()) *fpg; // expected-error {{use of TU-local entity 'f' is an exposure}} +export decltype(f()) *fpe; // expected-error {{use of TU-local entity 'f' is an exposure}} + +static decltype(f()) &frs = a; // OK + decltype(f()) &frg = a; // expected-error {{use of TU-local entity 'f' is an exposure}} +export decltype(f()) &fre = a; // expected-error {{use of TU-local entity 'f' is an exposure}} + +// auto cases .. + +static auto fa_internal() { return f(); } // OK + auto fa_module() { return f(); } // OK +export auto fa_export() { return f(); } // OK + +static auto inline fai_internal() { return f(); } // OK + auto inline fai_module() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} +export auto inline fai_export() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}}K + +static auto constexpr fac_internal() { return f(); } // OK + auto constexpr fac_module() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} +export auto constexpr fac_export() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}}K + +// extra constexpr variable checks. + +static constexpr int v_internal_constexpr = f(); // OK + constexpr int v_module_constexpr = f(); // expected-error {{use of TU-local entity 'f' is an exposure}} +export constexpr int v_exported_constexpr = f(); // expected-error {{use of TU-local entity 'f' is an exposure}} diff --git a/clang/test/CXX/basic/basic.link/p1498r1-2-2-1.cpp b/clang/test/CXX/basic/basic.link/p1498r1-2-2-1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.link/p1498r1-2-2-1.cpp @@ -0,0 +1,31 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/A.cpp -fsyntax-only -Wno-unused-value -verify + +//--- A.cpp + +export module A; + +static constexpr int f() { return 0; } + +static int f_internal() { return f(); } // OK + int f_module() { return f(); } // OK +export int f_exported() { return f(); } // OK + +static inline int f_internal_inline() { return f(); } // OK + inline int f_module_inline() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} +export inline int f_exported_inline() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} + +static constexpr int f_internal_constexpr() { return f(); } // OK + constexpr int f_module_constexpr() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} +export constexpr int f_exported_constexpr() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} + +static consteval int f_internal_consteval() { return f(); } // OK + consteval int f_module_consteval() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} +export consteval int f_exported_consteval() { return f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} + +static decltype(f()) f_internal_decltype() { return 0; } // OK + decltype(f()) f_module_decltype() { return 0; } // expected-error {{use of TU-local entity 'f' is an exposure}} +export decltype(f()) f_exported_decltype() { return 0; } // expected-error {{use of TU-local entity 'f' is an exposure}} diff --git a/clang/test/CXX/basic/basic.link/p1498r1-2-2-5.cpp b/clang/test/CXX/basic/basic.link/p1498r1-2-2-5.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.link/p1498r1-2-2-5.cpp @@ -0,0 +1,26 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/A.cpp -fsyntax-only -Wno-unused-value -verify + +//--- A.cpp + +export module A; + +static constexpr int f() { return 0; } + +static int v_internal = f(); // OK + int v_module = f(); // OK +export int v_exported = f(); // OK + +static inline int v_internal_inline = f(); // OK + inline int v_module_inline = f(); // expected-error {{use of TU-local entity 'f' is an exposure}} +export inline int v_exported_inline = f(); // expected-error {{use of TU-local entity 'f' is an exposure}} + +struct c_sdm_module { + static int sdm_module; + static constexpr int sdm_module_constexpr = f(); // expected-error {{use of TU-local entity 'f' is an exposure}} +}; + +int c_sdm_module::sdm_module = f(); // OK diff --git a/clang/test/CXX/basic/basic.link/p19-ex4.cpp b/clang/test/CXX/basic/basic.link/p19-ex4.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.link/p19-ex4.cpp @@ -0,0 +1,67 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: cd %t +// +// RUN: %clang_cc1 -std=c++20 A.cpp -fsyntax-only -DTEST_INTERFACE \ +// RUN: -Wno-unused-value -verify +// RUN: %clang_cc1 -std=c++20 A.cpp -emit-module-interface -o A.pcm \ +// RUN: -Wno-unused-value +// RUN: %clang_cc1 -std=c++20 A-impl.cpp -fsyntax-only -fmodule-file=A=A.pcm \ +// RUN: -Wno-unused-value -verify + +//--- A.cpp +export module A; +static void f() {} +static int fi() { return 1; } +#if TEST_INTERFACE +inline void it() { f(); } // expected-error {{use of TU-local entity 'f' is an exposure}} +#endif +static inline void its() { f(); } // OK +template void g() { its(); } // OK +template void g<0>(); + +#if TEST_INTERFACE + // error: f (though not its type) is TU-local +decltype(f) *fp; // expected-error {{use of TU-local entity 'f' is an exposure}} +#endif +auto &fr = f; // OK +#if TEST_INTERFACE +constexpr auto &fr2 = fr; // expected-error {{use of TU-local entity 'f' is an exposure}} +#endif +constexpr static auto fp2 = fr; // OK + +struct S { void (&ref)(); } s{f}; // OK, value is TU-local +constexpr extern struct W { S &s; } wrap{s}; // OK, value is not TU-local + +static auto x = []{f();}; // OK +#if TEST_INTERFACE +auto x2 = x; // expected-error {{use of TU-local entity 'x' is an exposure}} +int y = ([]{f();}(),0); // expected-error-re {{use of TU-local entity '(lambda at {{.*}})' is an exposure}} +#endif +int y2 = (x,0); // OK + +namespace N { + struct A {}; + void adl(A); + static void adl(int); +} +void adl(double); + +inline void h(auto x) { adl(x); } // OK, but a specialization might be an exposure + +//--- A-impl.cpp +module A; +void other() { + g<0>(); // OK, specialization is explicitly instantiated + g<1>(); // expected-error {{use of TU-local entity 'its' is an exposure}} + // expected-note@-1 {{in instantiation of function template specialization 'g<1>' requested here}} + h(N::A{}); // expected-error@A.cpp:38 {{the overload set for the call to 'adl' contains one or more exposures}} + // expected-note@-1 {{in instantiation of function template specialization 'h' requested here}} + // expected-note@A.cpp:34 {{candidate 'adl' is an exposure}} + h(0); // OK, calls adl(double) + adl(N::A{}); // OK; N::adl(int) not found, calls N::adl(N::A) + fr(); // OK, calls f + // error: fr is not usable in constant expressions here + constexpr auto ptr = fr; // expected-error {{use of TU-local entity 'f' is an exposure}} +} diff --git a/clang/test/CXX/basic/basic.link/p19-inline-vars.cpp b/clang/test/CXX/basic/basic.link/p19-inline-vars.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/basic/basic.link/p19-inline-vars.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -std=c++20 %s -fsyntax-only -Wno-unused-value -verify + +export module A; + +static int fi() { return 1; } +static int x = 10; + +inline int bad_f0() { return x; } // expected-error {{use of TU-local entity 'x' is an exposure}} + +static inline int ok_f0() { return x; } // OK + +inline int bad_v0 = fi(); // expected-error {{use of TU-local entity 'fi' is an exposure}} +inline int bad_v1 = 5 + x - fi(); // expected-error {{use of TU-local entity 'x' is an exposure}} + // expected-error@-1 {{use of TU-local entity 'fi' is an exposure}} + +static inline int ok_v0 = fi(); // OK +static inline int ok_v1 = x; // OK diff --git a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp --- a/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp +++ b/clang/test/CXX/basic/basic.lookup/basic.lookup.argdep/p5-ex2.cpp @@ -63,6 +63,7 @@ // error: S::f is visible in instantiation context, but R::g has internal // linkage and cannot be used outside N.cpp - apply(x, S::Z()); // expected-error@N.cpp:10 {{no matching function for call to 'g'}} + apply(x, S::Z()); // expected-error@N.cpp:10 {{the overload set for the call to 'g' contains one or more exposures}} // expected-note@-1 {{in instantiation of function template specialization 'apply' requested here}} + // expected-note@N.cpp:5 {{candidate 'g' is an exposure}} } diff --git a/clang/test/Modules/lambdas.cppm b/clang/test/Modules/lambdas.cppm --- a/clang/test/Modules/lambdas.cppm +++ b/clang/test/Modules/lambdas.cppm @@ -6,11 +6,6 @@ // RUN: -o %t/lambdas.pcm // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \ // RUN: -verify -// -// RUN: %clang_cc1 -std=c++20 %t/lambdas2.cppm -emit-module-interface \ -// RUN: -o %t/lambdas2.pcm -// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \ -// RUN: -verify -DUSE_LAMBDA2 //--- lambdas.h auto l1 = []() constexpr -> int { @@ -38,19 +33,9 @@ export using ::l3; export using ::l4; -//--- lambdas2.cppm -export module lambdas2; -export { -#include "lambdas.h" -} - //--- Use.cpp // expected-no-diagnostics -#ifndef USE_LAMBDA2 import lambdas; -#else -import lambdas2; -#endif static_assert(l1.operator()() == 43); diff --git a/clang/test/Modules/template-lambdas.cppm b/clang/test/Modules/template-lambdas.cppm --- a/clang/test/Modules/template-lambdas.cppm +++ b/clang/test/Modules/template-lambdas.cppm @@ -6,11 +6,6 @@ // RUN: -o %t/lambdas.pcm // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \ // RUN: -verify -// -// RUN: %clang_cc1 -std=c++20 %t/template_lambdas2.cppm -emit-module-interface \ -// RUN: -o %t/lambdas2.pcm -// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -fsyntax-only \ -// RUN: -verify -DUSE_LAMBDA2 //--- lambdas.h auto l1 = []() constexpr -> int { @@ -38,19 +33,9 @@ export using ::l3; export using ::l4; -//--- template_lambdas2.cppm -export module lambdas2; -export { -#include "lambdas.h" -} - //--- Use.cpp // expected-no-diagnostics -#ifndef USE_LAMBDA2 import lambdas; -#else -import lambdas2; -#endif static_assert(l1.operator()<5>() == 5); static_assert(l1.operator()<6>() == 6);