diff --git a/include/swift/ClangImporter/ClangImporterRequests.h b/include/swift/ClangImporter/ClangImporterRequests.h index d435571c12272..c8f9ac83ffd79 100644 --- a/include/swift/ClangImporter/ClangImporterRequests.h +++ b/include/swift/ClangImporter/ClangImporterRequests.h @@ -531,10 +531,6 @@ enum class CxxEscapability { Escapable, NonEscapable, Unknown }; struct EscapabilityLookupDescriptor final { const clang::Type *type; ClangImporter::Implementation *impl; - // Only explicitly ~Escapable annotated types are considered ~Escapable. - // This is for backward compatibility, so we continue to import aggregates - // containing pointers as Escapable types. - bool annotationOnly = true; friend llvm::hash_code hash_value(const EscapabilityLookupDescriptor &desc) { return llvm::hash_combine(desc.type); @@ -542,7 +538,7 @@ struct EscapabilityLookupDescriptor final { friend bool operator==(const EscapabilityLookupDescriptor &lhs, const EscapabilityLookupDescriptor &rhs) { - return lhs.type == rhs.type && lhs.annotationOnly == rhs.annotationOnly; + return lhs.type == rhs.type; } friend bool operator!=(const EscapabilityLookupDescriptor &lhs, @@ -575,15 +571,7 @@ SourceLoc extractNearestSourceLoc(EscapabilityLookupDescriptor desc); // When a reference type is copied, the pointer’s value is copied rather than // the object’s storage. This means reference types can be imported as // copyable to Swift, even when they are non-copyable in C++. -enum class CxxValueSemanticsKind { - Unknown, - Copyable, - MoveOnly, - // A record that is either not copyable/movable or not destructible. - MissingLifetimeOperation, - // A record that has no copy and no move operations - UnavailableConstructors, -}; +enum class CxxValueSemanticsKind { Unknown, Copyable, MoveOnly }; struct CxxValueSemanticsDescriptor final { const clang::Type *type; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index ab6eedc32fa62..e5c41362957a6 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -5381,11 +5381,11 @@ static const llvm::StringMap> STLConditionalParams{ }; template -static std::optional checkConditionalParams( - clang::RecordDecl *recordDecl, const std::vector &STLParams, - std::set &conditionalParams, - std::function(clang::TemplateArgument &, StringRef)> - &checkArg) { +static bool checkConditionalParams( + const clang::RecordDecl *recordDecl, ClangImporter::Implementation *impl, + const std::vector &STLParams, std::set &conditionalParams, + llvm::function_ref maybePushToStack) { + bool foundErrors = false; auto specDecl = cast(recordDecl); SmallVector, 4> argumentsToCheck; bool hasInjectedSTLAnnotation = !STLParams.empty(); @@ -5413,14 +5413,21 @@ static std::optional checkConditionalParams( } else nonPackArgs.push_back(arg); for (auto nonPackArg : nonPackArgs) { - auto result = checkArg(nonPackArg, argToCheck.second); - if (result.has_value()) - return result.value(); + if (nonPackArg.getKind() != clang::TemplateArgument::Type) { + if (impl) + impl->diagnose(HeaderLoc(recordDecl->getLocation()), + diag::type_template_parameter_expected, + argToCheck.second); + foundErrors = true; + } else { + maybePushToStack( + nonPackArg.getAsType()->getUnqualifiedDesugaredType()); + } } } if (hasInjectedSTLAnnotation) break; - clang::DeclContext *dc = specDecl; + const clang::DeclContext *dc = specDecl; specDecl = nullptr; while ((dc = dc->getParent())) { specDecl = dyn_cast(dc); @@ -5428,7 +5435,7 @@ static std::optional checkConditionalParams( break; } } - return std::nullopt; + return foundErrors; } static std::set @@ -5467,112 +5474,104 @@ getConditionalCopyableAttrParams(const clang::RecordDecl *decl) { CxxEscapability ClangTypeEscapability::evaluate(Evaluator &evaluator, EscapabilityLookupDescriptor desc) const { - bool hadUnknown = false; - auto evaluateEscapability = [&](const clang::Type *type) { - auto escapability = evaluateOrDefault( - evaluator, - ClangTypeEscapability({type, desc.impl, desc.annotationOnly}), - CxxEscapability::Unknown); - if (escapability == CxxEscapability::Unknown) - hadUnknown = true; - return escapability; - }; + // Escapability inference rules: + // - array and vector types have the same escapability as their element type + // - pointer and reference types are currently imported as unknown + // (importing them as non-escapable broke backward compatibility) + // - a record type is escapable or non-escapable if it is explicitly annotated + // as such + // - a record type is escapable if it is annotated with SWIFT_ESCAPABLE_IF() + // and none of the annotation arguments are non-escapable + // - an aggregate or a record that is not a cxxrecord are escapable if none of + // their fields or bases are non-escapable (as long as they have a definition) + // - in all other cases, the record has unknown escapability (e.g. no + // escapability annotations, malformed escapability annotations) + + bool hasUnknown = false; auto desugared = desc.type->getUnqualifiedDesugaredType(); if (const auto *recordType = desugared->getAs()) { auto recordDecl = recordType->getDecl(); - if (hasNonEscapableAttr(recordDecl)) - return CxxEscapability::NonEscapable; + // If the root type has a SWIFT_ESCAPABLE annotation, we import the type as + // Escapable without entering the loop if (hasEscapableAttr(recordDecl)) return CxxEscapability::Escapable; - auto injectedStlAnnotation = - recordDecl->isInStdNamespace() - ? STLConditionalParams.find(recordDecl->getName()) - : STLConditionalParams.end(); - auto STLParams = injectedStlAnnotation != STLConditionalParams.end() - ? injectedStlAnnotation->second - : std::vector(); - auto conditionalParams = getConditionalEscapableAttrParams(recordDecl); - - if (!STLParams.empty() || !conditionalParams.empty()) { - HeaderLoc loc{recordDecl->getLocation()}; - std::function checkArgEscapability = - [&](clang::TemplateArgument &arg, - StringRef argToCheck) -> std::optional { - if (arg.getKind() != clang::TemplateArgument::Type && desc.impl) { - desc.impl->diagnose(loc, diag::type_template_parameter_expected, - argToCheck); - return CxxEscapability::Unknown; - } + } + + llvm::SmallVector stack; + // Keep track of Types we've seen to avoid cycles + llvm::SmallDenseSet seen; - auto argEscapability = evaluateEscapability( - arg.getAsType()->getUnqualifiedDesugaredType()); - if (argEscapability == CxxEscapability::NonEscapable) - return CxxEscapability::NonEscapable; - return std::nullopt; - }; - - auto result = checkConditionalParams( - recordDecl, STLParams, conditionalParams, checkArgEscapability); - if (result.has_value()) - return result.value(); - - if (desc.impl) - for (auto name : conditionalParams) - desc.impl->diagnose(loc, diag::unknown_template_parameter, name); - - return hadUnknown ? CxxEscapability::Unknown : CxxEscapability::Escapable; - } - if (desc.annotationOnly) - return CxxEscapability::Unknown; - auto cxxRecordDecl = dyn_cast(recordDecl); - if (recordDecl->getDefinition() && - (!cxxRecordDecl || cxxRecordDecl->isAggregate())) { - if (cxxRecordDecl) { - for (auto base : cxxRecordDecl->bases()) { - auto baseEscapability = evaluateEscapability( - base.getType()->getUnqualifiedDesugaredType()); - if (baseEscapability == CxxEscapability::NonEscapable) - return CxxEscapability::NonEscapable; + auto maybePushToStack = [&](const clang::Type *type) { + auto desugared = type->getUnqualifiedDesugaredType(); + if (seen.insert(desugared).second) + stack.push_back(desugared); + }; + + maybePushToStack(desc.type); + while (!stack.empty()) { + auto type = stack.back(); + stack.pop_back(); + if (const auto *recordType = type->getAs()) { + auto recordDecl = recordType->getDecl(); + if (hasNonEscapableAttr(recordDecl)) + return CxxEscapability::NonEscapable; + if (hasEscapableAttr(recordDecl)) + continue; + auto injectedStlAnnotation = + recordDecl->isInStdNamespace() + ? STLConditionalParams.find(recordDecl->getName()) + : STLConditionalParams.end(); + auto STLParams = injectedStlAnnotation != STLConditionalParams.end() + ? injectedStlAnnotation->second + : std::vector(); + auto conditionalParams = getConditionalEscapableAttrParams(recordDecl); + + if (!STLParams.empty() || !conditionalParams.empty()) { + hasUnknown &= checkConditionalParams( + recordDecl, desc.impl, STLParams, conditionalParams, + maybePushToStack); + + if (desc.impl) { + HeaderLoc loc{recordDecl->getLocation()}; + for (auto name : conditionalParams) + desc.impl->diagnose(loc, diag::unknown_template_parameter, name); } - } - for (auto field : recordDecl->fields()) { - auto fieldEscapability = evaluateEscapability( - field->getType()->getUnqualifiedDesugaredType()); - if (fieldEscapability == CxxEscapability::NonEscapable) - return CxxEscapability::NonEscapable; + continue; } - - return hadUnknown ? CxxEscapability::Unknown : CxxEscapability::Escapable; + // Only try to infer escapability if the record doesn't have any + // escapability annotations + auto cxxRecordDecl = dyn_cast(recordDecl); + if (recordDecl->getDefinition() && + (!cxxRecordDecl || cxxRecordDecl->isAggregate())) { + if (cxxRecordDecl) { + for (auto base : cxxRecordDecl->bases()) + maybePushToStack(base.getType()->getUnqualifiedDesugaredType()); + } + for (auto field : recordDecl->fields()) + maybePushToStack(field->getType()->getUnqualifiedDesugaredType()); + } else { + // We only infer escapability for simple types, such as aggregates and + // RecordDecls that are not CxxRecordDecls. For more complex + // CxxRecordDecls, we rely solely on escapability annotations. + hasUnknown = true; + } + } else if (type->isArrayType()) { + auto elemTy = cast(type) + ->getElementType() + ->getUnqualifiedDesugaredType(); + maybePushToStack(elemTy); + } else if (const auto *vecTy = type->getAs()) { + maybePushToStack(vecTy->getElementType()->getUnqualifiedDesugaredType()); + } else if (type->isAnyPointerType() || type->isBlockPointerType() || + type->isMemberPointerType() || type->isReferenceType()) { + // pointer and reference types are currently imported as unknown + // (importing them as non-escapable broke backward compatibility) + hasUnknown = true; } } - if (desugared->isArrayType()) { - auto elemTy = cast(desugared) - ->getElementType() - ->getUnqualifiedDesugaredType(); - return evaluateOrDefault( - evaluator, - ClangTypeEscapability({elemTy, desc.impl, desc.annotationOnly}), - CxxEscapability::Unknown); - } - if (const auto *vecTy = desugared->getAs()) { - return evaluateOrDefault( - evaluator, - ClangTypeEscapability( - {vecTy->getElementType()->getUnqualifiedDesugaredType(), desc.impl, - desc.annotationOnly}), - CxxEscapability::Unknown); - } - - // Base cases - if (desugared->isAnyPointerType() || desugared->isBlockPointerType() || - desugared->isMemberPointerType() || desugared->isReferenceType()) - return desc.annotationOnly ? CxxEscapability::Unknown - : CxxEscapability::NonEscapable; - if (desugared->isScalarType()) - return CxxEscapability::Escapable; - return CxxEscapability::Unknown; + return hasUnknown ? CxxEscapability::Unknown : CxxEscapability::Escapable; } void swift::simple_display(llvm::raw_ostream &out, @@ -8459,103 +8458,113 @@ CxxValueSemanticsKind CxxValueSemantics::evaluate(Evaluator &evaluator, CxxValueSemanticsDescriptor desc) const { - const auto *type = desc.type; + // A C++ type can be imported to Swift as Copyable or ~Copyable. + // We assume a type can be imported as Copyable unless: + // - There's no copy constructor + // - The type has a SWIFT_NONCOPYABLE annotation + // - The type has a SWIFT_COPYABLE_IF(T...) annotation, where at least one of + // T is ~Copyable + // - It is one of the STL types in `STLConditionalParams`, and at least one of + // its revelant types is ~Copyable + + const auto *type = desc.type->getUnqualifiedDesugaredType(); auto *importerImpl = desc.importerImpl; - auto desugared = type->getUnqualifiedDesugaredType(); - const auto *recordType = desugared->getAs(); - if (!recordType) - return CxxValueSemanticsKind::Copyable; - - auto recordDecl = recordType->getDecl(); - - // When a reference type is copied, the pointer’s value is copied rather than - // the object’s storage. This means reference types can be imported as - // copyable to Swift, even when they are non-copyable in C++. - if (recordHasReferenceSemantics(recordDecl, importerImpl)) - return CxxValueSemanticsKind::Copyable; - - if (recordDecl->isInStdNamespace()) { - // Hack for a base type of std::optional from the Microsoft standard - // library. - if (recordDecl->getIdentifier() && - recordDecl->getName() == "_Optional_construct_base") - return CxxValueSemanticsKind::Copyable; - } - - if (!hasNonCopyableAttr(recordDecl)) { - auto injectedStlAnnotation = - recordDecl->isInStdNamespace() - ? STLConditionalParams.find(recordDecl->getName()) - : STLConditionalParams.end(); - auto STLParams = injectedStlAnnotation != STLConditionalParams.end() - ? injectedStlAnnotation->second - : std::vector(); - auto conditionalParams = getConditionalCopyableAttrParams(recordDecl); - - if (!STLParams.empty() || !conditionalParams.empty()) { - HeaderLoc loc{recordDecl->getLocation()}; - std::function checkArgValueSemantics = - [&](clang::TemplateArgument &arg, - StringRef argToCheck) -> std::optional { - if (arg.getKind() != clang::TemplateArgument::Type && importerImpl) { - importerImpl->diagnose(loc, diag::type_template_parameter_expected, - argToCheck); - return CxxValueSemanticsKind::Unknown; - } + bool hasUnknown = false; + llvm::SmallVector stack; + // Keep track of Decls we've seen to avoid cycles + llvm::SmallDenseSet seen; - auto argValueSemantics = evaluateOrDefault( - evaluator, - CxxValueSemantics( - {arg.getAsType()->getUnqualifiedDesugaredType(), importerImpl}), - {}); - if (argValueSemantics != CxxValueSemanticsKind::Copyable) - return argValueSemantics; - return std::nullopt; - }; + auto maybePushToStack = [&](const clang::Type *type) { + auto recordType = type->getAs(); + if (!recordType) + return; - auto result = checkConditionalParams( - recordDecl, STLParams, conditionalParams, checkArgValueSemantics); - if (result.has_value()) - return result.value(); + auto recordDecl = recordType->getDecl(); + if (seen.insert(recordDecl).second) { + // When a reference type is copied, the pointer’s value is copied rather + // than the object’s storage. This means reference types can be imported + // as copyable to Swift, even when they are non-copyable in C++. + if (recordHasReferenceSemantics(recordDecl, importerImpl)) + return; - if (importerImpl) - for (auto name : conditionalParams) - importerImpl->diagnose(loc, diag::unknown_template_parameter, name); + if (recordDecl->isInStdNamespace()) { + // Hack for a base type of std::optional from the Microsoft standard + // library. + if (recordDecl->getIdentifier() && + recordDecl->getName() == "_Optional_construct_base") + return; + } + stack.push_back(recordDecl); + } + }; - return CxxValueSemanticsKind::Copyable; + maybePushToStack(type); + while (!stack.empty()) { + const clang::RecordDecl *recordDecl = stack.back(); + stack.pop_back(); + + if (!hasNonCopyableAttr(recordDecl)) { + auto injectedStlAnnotation = + recordDecl->isInStdNamespace() + ? STLConditionalParams.find(recordDecl->getName()) + : STLConditionalParams.end(); + auto STLParams = injectedStlAnnotation != STLConditionalParams.end() + ? injectedStlAnnotation->second + : std::vector(); + auto conditionalParams = getConditionalCopyableAttrParams(recordDecl); + + if (!STLParams.empty() || !conditionalParams.empty()) { + hasUnknown &= checkConditionalParams( + recordDecl, importerImpl, STLParams, conditionalParams, + maybePushToStack); + + if (importerImpl) { + HeaderLoc loc{recordDecl->getLocation()}; + for (auto name : conditionalParams) + importerImpl->diagnose(loc, diag::unknown_template_parameter, name); + } + + continue; + } } - } - const auto cxxRecordDecl = dyn_cast(recordDecl); - if (!cxxRecordDecl || !cxxRecordDecl->isCompleteDefinition()) { - if (hasNonCopyableAttr(recordDecl)) - return CxxValueSemanticsKind::MoveOnly; - return CxxValueSemanticsKind::Copyable; - } + const auto cxxRecordDecl = dyn_cast(recordDecl); + if (!cxxRecordDecl || !recordDecl->isCompleteDefinition()) { + if (hasNonCopyableAttr(recordDecl)) + return CxxValueSemanticsKind::MoveOnly; + continue; + } - bool isCopyable = !hasNonCopyableAttr(cxxRecordDecl) && - hasCopyTypeOperations(cxxRecordDecl); - bool isMovable = hasMoveTypeOperations(cxxRecordDecl); + bool isCopyable = !hasNonCopyableAttr(cxxRecordDecl) && + hasCopyTypeOperations(cxxRecordDecl); + bool isMovable = hasMoveTypeOperations(cxxRecordDecl); - if (!hasDestroyTypeOperations(cxxRecordDecl) || (!isCopyable && !isMovable)) { + if (!hasDestroyTypeOperations(cxxRecordDecl) || + (!isCopyable && !isMovable)) { - if (hasConstructorWithUnsupportedDefaultArgs(cxxRecordDecl)) - return CxxValueSemanticsKind::UnavailableConstructors; + if (hasConstructorWithUnsupportedDefaultArgs(cxxRecordDecl)) + importerImpl->addImportDiagnostic( + cxxRecordDecl, Diagnostic(diag::record_unsupported_default_args), + cxxRecordDecl->getLocation()); + hasUnknown = true; + continue; + } - return CxxValueSemanticsKind::MissingLifetimeOperation; - } + if (hasNonCopyableAttr(cxxRecordDecl) && isMovable) + return CxxValueSemanticsKind::MoveOnly; - if (hasNonCopyableAttr(cxxRecordDecl) && isMovable) - return CxxValueSemanticsKind::MoveOnly; + if (isCopyable) + continue; - if (isCopyable) - return CxxValueSemanticsKind::Copyable; + if (isMovable) + return CxxValueSemanticsKind::MoveOnly; - if (isMovable) - return CxxValueSemanticsKind::MoveOnly; + llvm_unreachable("Could not classify C++ type."); + } - llvm_unreachable("Could not classify C++ type."); + return hasUnknown ? CxxValueSemanticsKind::Unknown + : CxxValueSemanticsKind::Copyable; } void swift::simple_display(llvm::raw_ostream &out, diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index e1fbb751bd557..cfee7b70209d6 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2103,11 +2103,11 @@ namespace { return importer::recordHasReferenceSemantics(decl, &Impl); } - bool recordHasMoveOnlySemantics(const clang::RecordDecl *decl) { + bool recordIsCopyable(const clang::RecordDecl *decl) { auto semanticsKind = evaluateOrDefault( Impl.SwiftContext.evaluator, CxxValueSemantics({decl->getTypeForDecl(), &Impl}), {}); - return semanticsKind == CxxValueSemanticsKind::MoveOnly; + return semanticsKind == CxxValueSemanticsKind::Copyable; } void markReturnsUnsafeNonescapable(AbstractFunctionDecl *fd) { @@ -2286,7 +2286,7 @@ namespace { loc, ArrayRef(), nullptr, dc); Impl.ImportedDecls[{decl->getCanonicalDecl(), getVersion()}] = result; - if (recordHasMoveOnlySemantics(decl)) { + if (!recordIsCopyable(decl)) { if (decl->isInStdNamespace() && decl->getName() == "promise") { // Do not import std::promise. return nullptr; @@ -3170,8 +3170,7 @@ namespace { auto valueSemanticsKind = evaluateOrDefault( Impl.SwiftContext.evaluator, CxxValueSemantics({decl->getTypeForDecl(), &Impl}), {}); - if (valueSemanticsKind == CxxValueSemanticsKind::MissingLifetimeOperation || - valueSemanticsKind == CxxValueSemanticsKind::UnavailableConstructors) { + if (valueSemanticsKind == CxxValueSemanticsKind::Unknown) { HeaderLoc loc(decl->getLocation()); if (hasUnsafeAPIAttr(decl)) @@ -3184,12 +3183,6 @@ namespace { Impl.diagnose(loc, diag::api_pattern_attr_ignored, "import_iterator", decl->getNameAsString()); - if (valueSemanticsKind == CxxValueSemanticsKind::UnavailableConstructors) { - Impl.addImportDiagnostic( - decl, Diagnostic(diag::record_unsupported_default_args), - decl->getLocation()); - } - Impl.addImportDiagnostic( decl, Diagnostic(diag::record_not_automatically_importable, @@ -3426,7 +3419,7 @@ namespace { auto semanticsKind = evaluateOrDefault( Impl.SwiftContext.evaluator, CxxValueSemantics({parent->getTypeForDecl(), &Impl}), {}); - if (semanticsKind == CxxValueSemanticsKind::MissingLifetimeOperation) + if (semanticsKind == CxxValueSemanticsKind::Unknown) return nullptr; } diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 19b774af92d83..0e5f9742e2280 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -3074,8 +3074,7 @@ FuncDecl *SwiftDeclSynthesizer::findExplicitDestroy( ctx.evaluator, CxxValueSemantics({clangType->getTypeForDecl(), &ImporterImpl}), {}); - if (valueSemanticsKind != CxxValueSemanticsKind::Copyable && - valueSemanticsKind != CxxValueSemanticsKind::MoveOnly) + if (valueSemanticsKind == CxxValueSemanticsKind::Unknown) return nullptr; auto cxxRecordSemanticsKind = evaluateOrDefault( diff --git a/test/Interop/Cxx/class/nonescapable-errors.swift b/test/Interop/Cxx/class/nonescapable-errors.swift index e8e7e6b6a0329..830cf70c640ac 100644 --- a/test/Interop/Cxx/class/nonescapable-errors.swift +++ b/test/Interop/Cxx/class/nonescapable-errors.swift @@ -147,6 +147,30 @@ using HasAnonStructNonEscapable = HasAnonStruct; using NonEscapableHasAnonUnionNonEscapable = NonEscapableHasAnonUnion; using NonEscapableOptional = std::optional; +// Infered as non-escapable +struct Aggregate { + int a; + View b; + bool c; + + void someMethod() {} +}; + +// This is a complex record, so we don't infer escapability. +// By default, it's imported as escapable, which generates an error +// because of the non-escapable field 'View' +struct ComplexRecord { + int a; + View b; + bool c; + + ComplexRecord() {} + ComplexRecord(const ComplexRecord &other) = default; +}; + +Aggregate m1(); +ComplexRecord m2(); + //--- test.swift import Test import CxxStdlib @@ -193,10 +217,10 @@ public func noAnnotations() -> View { // CHECK-NO-LIFETIMES: nonescapable.h:56:39: error: template parameter 'Missing' does not exist i2() // CHECK: nonescapable.h:62:33: error: template parameter 'S' expected to be a type parameter - // CHECK: nonescapable.h:80:41: error: a function with a ~Escapable result needs a parameter to depend on - // CHECK: note: '@_lifetime(immortal)' can be used to indicate that values produced // CHECK-NO-LIFETIMES: nonescapable.h:62:33: error: template parameter 'S' expected to be a type parameter j1() + // CHECK: nonescapable.h:80:41: error: a function with a ~Escapable result needs a parameter to depend on + // CHECK: note: '@_lifetime(immortal)' can be used to indicate that values produced // CHECK-NO-LIFETIMES: nonescapable.h:80:41: error: a function cannot return a ~Escapable result j2() // CHECK: nonescapable.h:81:41: error: a function with a ~Escapable result needs a parameter to depend on @@ -235,11 +259,7 @@ public func test3(_ x: inout View) { public func anonymousUnions() { _ = HasAnonUnionNonEscapable() - // CHECK: error: cannot find 'HasAnonUnionNonEscapable' in scope - // CHECK-NO-LIFETIMES: error: cannot find 'HasAnonUnionNonEscapable' in scope _ = HasAnonStructNonEscapable() - // CHECK: error: cannot find 'HasAnonStructNonEscapable' in scope - // CHECK-NO-LIFETIMES: error: cannot find 'HasAnonStructNonEscapable' in scope _ = NonEscapableHasAnonUnionNonEscapable() _ = NonEscapableOptional() // CHECK: error: cannot infer the lifetime dependence scope on an initializer with a ~Escapable parameter, specify '@_lifetime(borrow {{.*}})' or '@_lifetime(copy {{.*}})' @@ -247,6 +267,18 @@ public func anonymousUnions() { // CHECK-NO-LIFETIMES: error: an initializer cannot return a ~Escapable result } +public func inferedEscapability() { + m1() + // CHECK: nonescapable.h:157:11: error: a function with a ~Escapable result needs a parameter to depend on + // CHECK-NO-LIFETIMES: nonescapable.h:157:11: error: a function cannot return a ~Escapable result + m2() + // CHECK: error: 'm2()' is unavailable: return type is unavailable in Swift + // CHECK: note: 'm2()' has been explicitly marked unavailable here + // CHECK-NO-LIFETIMES: error: 'm2()' is unavailable: return type is unavailable in Swift + // CHECK-NO-LIFETIMES: note: 'm2()' has been explicitly marked unavailable here + +} + // CHECK-NOT: error // CHECK-NOT: warning // CHECK-NO-LIFETIMES-NOT: error diff --git a/test/Interop/Cxx/class/safe-interop-mode.swift b/test/Interop/Cxx/class/safe-interop-mode.swift index ab664d16bb6a0..d379cb1658212 100644 --- a/test/Interop/Cxx/class/safe-interop-mode.swift +++ b/test/Interop/Cxx/class/safe-interop-mode.swift @@ -101,8 +101,10 @@ struct HoldsShared { SWIFT_RETURNS_UNRETAINED; }; -template struct TTake2 {}; -template struct PassThru {}; +template struct SWIFT_ESCAPABLE_IF(F, S) TTake2 {}; +template struct PassThru { + T field; +}; struct IsUnsafe { int *p; }; struct HasUnsafe : TTake2, IsUnsafe> {}; using AlsoUnsafe = PassThru; @@ -207,8 +209,7 @@ func useTTakeInt(x: TTakeInt) { } func useTTakePtr(x: TTakePtr) { - // expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}} - _ = x // expected-note{{reference to parameter 'x' involves unsafe type}} + _ = x } func useTTakeSafeTuple(x: TTakeSafeTuple) { @@ -216,8 +217,7 @@ func useTTakeSafeTuple(x: TTakeSafeTuple) { } func useTTakeUnsafeTuple(x: TTakeUnsafeTuple) { - // expected-warning@+1{{expression uses unsafe constructs but is not marked with 'unsafe'}} - _ = x // expected-note{{reference to parameter 'x' involves unsafe type}} + _ = x } func useTTakeUnsafeTuple(x: HasUnsafe) {