From 38f178838e4d20fcd4ad54409c15a391f0ac55db Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 11 Sep 2025 18:01:02 -0400 Subject: [PATCH 1/5] Sema: Extract resolveGenericArguments() from applyGenericArguments() --- lib/Sema/TypeCheckType.cpp | 231 ++++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 104 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 49179465b47eb..89ce69ff428f5 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -827,6 +827,130 @@ namespace { }; } +/// Returns true on error. +static bool resolveGenericArguments(ValueDecl *decl, + const GenericContext *genCtx, + const TypeResolution &resolution, + SILTypeResolutionContext *silContext, + DeclRefTypeRepr *repr, + SmallVectorImpl &args) { + auto options = resolution.getOptions(); + auto &ctx = decl->getASTContext(); + + auto genericParams = genCtx->getGenericParams(); + auto hasParameterPack = llvm::any_of(*genericParams, [](auto *paramDecl) { + return paramDecl->isParameterPack(); + }); + auto hasValueParam = llvm::any_of(*genericParams, [](auto *paramDecl) { + return paramDecl->isValue(); + }); + + // If the type declares at least one parameter pack, allow pack expansions + // anywhere in the argument list. We'll use the PackMatcher to ensure that + // everything lines up. Otherwise, don't allow pack expansions to appear + // at all. + auto argOptions = options.withoutContext().withContext( + hasParameterPack + ? TypeResolverContext::VariadicGenericArgument + : TypeResolverContext::ScalarGenericArgument); + if (hasValueParam) + argOptions = argOptions.withContext(TypeResolverContext::ValueGenericArgument); + auto genericResolution = resolution.withOptions(argOptions); + + // In SIL mode, Optional interprets T as a SIL type. + if (options.contains(TypeResolutionFlags::SILType)) { + if (auto nominal = dyn_cast(genCtx)) { + if (nominal->isOptionalDecl()) { + genericResolution = resolution; + } + } + } + + auto genericArgs = repr->getGenericArgs(); + auto loc = repr->getNameLoc().getBaseNameLoc(); + + // Resolve the types of the generic arguments. + for (auto tyR : genericArgs) { + // Propagate failure. + Type substTy = genericResolution.resolveType(tyR, silContext); + if (!substTy || substTy->hasError()) + return true; + + args.push_back(substTy); + } + + // Make sure we have the right number of generic arguments. + if (!hasParameterPack) { + // For generic types without type parameter packs, we require + // the number of declared generic parameters match the number of + // arguments. + if (genericArgs.size() != genericParams->size()) { + if (!options.contains(TypeResolutionFlags::SilenceErrors)) { + diagnoseInvalidGenericArguments( + loc, decl, genericArgs.size(), genericParams->size(), + /*hasParameterPack=*/false, repr->getAngleBrackets()); + } + return true; + } + + return false; + } + + // For generic types with type parameter packs, we only require + // that the number of arguments is enough to saturate the number of + // regular generic parameters. The parameter pack will absorb + // zero or arguments. + SmallVector params; + for (auto paramDecl : genericParams->getParams()) { + auto paramType = paramDecl->getDeclaredInterfaceType(); + params.push_back(paramDecl->isParameterPack() + ? PackExpansionType::get(paramType, paramType) + : paramType); + } + + PackMatcher matcher(params, args, ctx); + if (matcher.match() || matcher.pairs.size() != params.size()) { + if (!options.contains(TypeResolutionFlags::SilenceErrors)) { + diagnoseInvalidGenericArguments( + loc, decl, genericArgs.size(), genericParams->size(), + /*hasParameterPack=*/true, repr->getAngleBrackets()); + } + return true; + } + + args.clear(); + for (unsigned i : indices(params)) { + auto found = std::find_if(matcher.pairs.begin(), + matcher.pairs.end(), + [&](const MatchedPair &pair) -> bool { + return pair.lhsIdx == i; + }); + assert(found != matcher.pairs.end()); + + auto arg = found->rhs; + + // PackMatcher will always produce a PackExpansionType as the + // arg for a pack parameter, if necessary by wrapping a PackType + // in one. (It's a weird representation.) Look for that pattern + // and unwrap the pack. Otherwise, we must have matched with a + // single component which happened to be an expansion; wrap that + // in a PackType. In either case, we always want arg to end up + // a PackType. + if (auto *expansionType = arg->getAs()) { + auto pattern = expansionType->getPatternType(); + if (auto pack = pattern->getAs()) { + arg = pack; + } else { + arg = PackType::get(ctx, {expansionType}); + } + } + + args.push_back(arg); + } + + return false; +} + /// Apply generic arguments to the given type. /// /// If the type is itself not generic, this does nothing. @@ -1005,112 +1129,10 @@ static Type applyGenericArguments(Type type, auto *unboundType = type->castTo(); auto *decl = unboundType->getDecl(); - auto genericParams = decl->getGenericParams(); - auto hasParameterPack = llvm::any_of(*genericParams, [](auto *paramDecl) { - return paramDecl->isParameterPack(); - }); - auto hasValueParam = llvm::any_of(*genericParams, [](auto *paramDecl) { - return paramDecl->isValue(); - }); - - // If the type declares at least one parameter pack, allow pack expansions - // anywhere in the argument list. We'll use the PackMatcher to ensure that - // everything lines up. Otherwise, don't allow pack expansions to appear - // at all. - auto argOptions = options.withoutContext().withContext( - hasParameterPack - ? TypeResolverContext::VariadicGenericArgument - : TypeResolverContext::ScalarGenericArgument); - if (hasValueParam) - argOptions = argOptions.withContext(TypeResolverContext::ValueGenericArgument); - auto genericResolution = resolution.withOptions(argOptions); - - // In SIL mode, Optional interprets T as a SIL type. - if (options.contains(TypeResolutionFlags::SILType)) { - if (auto nominal = dyn_cast(decl)) { - if (nominal->isOptionalDecl()) { - genericResolution = resolution; - } - } - } - // Resolve the types of the generic arguments. SmallVector args; - for (auto tyR : genericArgs) { - // Propagate failure. - Type substTy = genericResolution.resolveType(tyR, silContext); - if (!substTy || substTy->hasError()) - return ErrorType::get(ctx); - - args.push_back(substTy); - } - - // Make sure we have the right number of generic arguments. - if (!hasParameterPack) { - // For generic types without type parameter packs, we require - // the number of declared generic parameters match the number of - // arguments. - if (genericArgs.size() != genericParams->size()) { - if (!options.contains(TypeResolutionFlags::SilenceErrors)) { - diagnoseInvalidGenericArguments( - loc, decl, genericArgs.size(), genericParams->size(), - /*hasParameterPack=*/false, repr->getAngleBrackets()); - } - return ErrorType::get(ctx); - } - } else { - // For generic types with type parameter packs, we only require - // that the number of arguments is enough to saturate the number of - // regular generic parameters. The parameter pack will absorb - // zero or arguments. - SmallVector params; - for (auto paramDecl : genericParams->getParams()) { - auto paramType = paramDecl->getDeclaredInterfaceType(); - params.push_back(paramDecl->isParameterPack() - ? PackExpansionType::get(paramType, paramType) - : paramType); - } - - PackMatcher matcher(params, args, ctx); - if (matcher.match() || matcher.pairs.size() != params.size()) { - if (!options.contains(TypeResolutionFlags::SilenceErrors)) { - diagnoseInvalidGenericArguments( - loc, decl, genericArgs.size(), genericParams->size(), - /*hasParameterPack=*/true, repr->getAngleBrackets()); - } - return ErrorType::get(ctx); - } - - args.clear(); - for (unsigned i : indices(params)) { - auto found = std::find_if(matcher.pairs.begin(), - matcher.pairs.end(), - [&](const MatchedPair &pair) -> bool { - return pair.lhsIdx == i; - }); - assert(found != matcher.pairs.end()); - - auto arg = found->rhs; - - // PackMatcher will always produce a PackExpansionType as the - // arg for a pack parameter, if necessary by wrapping a PackType - // in one. (It's a weird representation.) Look for that pattern - // and unwrap the pack. Otherwise, we must have matched with a - // single component which happened to be an expansion; wrap that - // in a PackType. In either case, we always want arg to end up - // a PackType. - if (auto *expansionType = arg->getAs()) { - auto pattern = expansionType->getPatternType(); - if (auto pack = pattern->getAs()) { - arg = pack; - } else { - arg = PackType::get(ctx, {expansionType}); - } - } - - args.push_back(arg); - } - } + if (resolveGenericArguments(decl, decl, resolution, silContext, repr, args)) + return ErrorType::get(ctx); // Construct the substituted type. const auto result = resolution.applyUnboundGenericArguments( @@ -1130,6 +1152,7 @@ static Type applyGenericArguments(Type type, if (auto clangDecl = decl->getClangDecl()) { if (auto classTemplateDecl = dyn_cast(clangDecl)) { + // FIXME: Why does this resolve the types twice? SmallVector typesOfGenericArgs; for (auto typeRepr : genericArgs) { typesOfGenericArgs.push_back(resolution.resolveType(typeRepr)); From bd3b3ea0099f0b7f8d948abd607f90d7396a646c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 11 Sep 2025 18:36:30 -0400 Subject: [PATCH 2/5] ASTDemangler: Split off resolveOpaqueTypeDecl() from resolveOpaqueType() --- include/swift/AST/ASTDemangler.h | 2 + lib/AST/ASTDemangler.cpp | 96 +++++++++++++++----------------- 2 files changed, 47 insertions(+), 51 deletions(-) diff --git a/include/swift/AST/ASTDemangler.h b/include/swift/AST/ASTDemangler.h index 3218d5ed448b2..58bcf68281a23 100644 --- a/include/swift/AST/ASTDemangler.h +++ b/include/swift/AST/ASTDemangler.h @@ -158,6 +158,8 @@ class ASTBuilder { Type createBoundGenericType(GenericTypeDecl *decl, ArrayRef args); + OpaqueTypeDecl *resolveOpaqueTypeDecl(NodePointer opaqueDescriptor); + Type resolveOpaqueType(NodePointer opaqueDescriptor, ArrayRef> args, unsigned ordinal); diff --git a/lib/AST/ASTDemangler.cpp b/lib/AST/ASTDemangler.cpp index b7900ee2723da..65e846435d357 100644 --- a/lib/AST/ASTDemangler.cpp +++ b/lib/AST/ASTDemangler.cpp @@ -332,8 +332,7 @@ Type ASTBuilder::createTypeAliasType(GenericTypeDecl *decl, Type parent) { static SubstitutionMap createSubstitutionMapFromGenericArgs(GenericSignature genericSig, - ArrayRef args, - LookupConformanceFn lookupConformance) { + ArrayRef args) { if (!genericSig) return SubstitutionMap(); @@ -341,15 +340,7 @@ createSubstitutionMapFromGenericArgs(GenericSignature genericSig, return SubstitutionMap(); return SubstitutionMap::get( - genericSig, - [&](SubstitutableType *t) -> Type { - auto *gp = cast(t); - unsigned ordinal = genericSig->getGenericParamOrdinal(gp); - if (ordinal < args.size()) - return args[ordinal]; - return Type(); - }, - lookupConformance); + genericSig, args, LookUpConformanceInModule()); } Type ASTBuilder::createBoundGenericType(GenericTypeDecl *decl, @@ -364,8 +355,7 @@ Type ASTBuilder::createBoundGenericType(GenericTypeDecl *decl, // Build a SubstitutionMap. auto genericSig = nominalDecl->getGenericSignature(); - auto subs = createSubstitutionMapFromGenericArgs( - genericSig, args, LookUpConformanceInModule()); + auto subs = createSubstitutionMapFromGenericArgs(genericSig, args); if (!subs) return Type(); auto origType = nominalDecl->getDeclaredInterfaceType(); @@ -375,48 +365,53 @@ Type ASTBuilder::createBoundGenericType(GenericTypeDecl *decl, return origType.subst(subs); } -Type ASTBuilder::resolveOpaqueType(NodePointer opaqueDescriptor, - ArrayRef> args, - unsigned ordinal) { - if (opaqueDescriptor->getKind() == Node::Kind::OpaqueReturnTypeOf) { - auto definingDecl = opaqueDescriptor->getChild(0); - auto definingGlobal = Factory.createNode(Node::Kind::Global); - definingGlobal->addChild(definingDecl, Factory); - auto mangling = mangleNode(definingGlobal, ManglingFlavor); - if (!mangling.isSuccess()) - return Type(); - auto mangledName = mangling.result(); +OpaqueTypeDecl *ASTBuilder::resolveOpaqueTypeDecl(NodePointer opaqueDescriptor) { + if (opaqueDescriptor->getKind() != Node::Kind::OpaqueReturnTypeOf) + return nullptr; - auto moduleNode = findModuleNode(definingDecl); - if (!moduleNode) - return Type(); + auto definingDecl = opaqueDescriptor->getChild(0); + auto definingGlobal = Factory.createNode(Node::Kind::Global); + definingGlobal->addChild(definingDecl, Factory); + auto mangling = mangleNode(definingGlobal, ManglingFlavor); + if (!mangling.isSuccess()) + return nullptr; + auto mangledName = mangling.result(); - ModuleDecl *scratch; - auto potentialParentModules = findPotentialModules(moduleNode, scratch); - if (potentialParentModules.empty()) - return Type(); + auto moduleNode = findModuleNode(definingDecl); + if (!moduleNode) + return nullptr; - OpaqueTypeDecl *opaqueDecl = nullptr; - for (auto module : potentialParentModules) - if (auto decl = module->lookupOpaqueResultType(mangledName)) - opaqueDecl = decl; + ModuleDecl *scratch; + auto potentialParentModules = findPotentialModules(moduleNode, scratch); + if (potentialParentModules.empty()) + return nullptr; - if (!opaqueDecl) - return Type(); - SmallVector allArgs; - for (auto argSet : args) { - allArgs.append(argSet.begin(), argSet.end()); - } + for (auto module : potentialParentModules) + if (auto decl = module->lookupOpaqueResultType(mangledName)) + return decl; + + return nullptr; +} + +Type ASTBuilder::resolveOpaqueType(NodePointer opaqueDescriptor, + ArrayRef> args, + unsigned ordinal) { + OpaqueTypeDecl *opaqueDecl = resolveOpaqueTypeDecl(opaqueDescriptor); + if (!opaqueDecl) + return Type(); - SubstitutionMap subs = createSubstitutionMapFromGenericArgs( - opaqueDecl->getGenericSignature(), allArgs, - LookUpConformanceInModule()); - Type interfaceType = opaqueDecl->getOpaqueGenericParams()[ordinal]; - return OpaqueTypeArchetypeType::get(opaqueDecl, interfaceType, subs); + SmallVector allArgs; + for (auto argSet : args) { + allArgs.append(argSet.begin(), argSet.end()); } - - // TODO: named opaque types - return Type(); + + if (ordinal >= opaqueDecl->getOpaqueGenericParams().size()) + return Type(); + + SubstitutionMap subs = createSubstitutionMapFromGenericArgs( + opaqueDecl->getGenericSignature(), allArgs); + Type interfaceType = opaqueDecl->getOpaqueGenericParams()[ordinal]; + return OpaqueTypeArchetypeType::get(opaqueDecl, interfaceType, subs); } Type ASTBuilder::createBoundGenericType(GenericTypeDecl *decl, @@ -1143,8 +1138,7 @@ Type ASTBuilder::createSILBoxTypeWithLayout( SubstitutionMap substs; if (signature) substs = createSubstitutionMapFromGenericArgs( - signature, replacements, - LookUpConformanceInModule()); + signature, replacements); return SILBoxType::get(Ctx, layout, substs); } From af78807313c85ddc3da7bd60bee8ad0fbd63eaa3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 11 Sep 2025 18:36:58 -0400 Subject: [PATCH 3/5] Sema: Generalize @_opaqueReturnTypeOf syntax to correctly handle parameter packs We now allow the dummy identifier to be a qualified reference, so that we can reconstruct generic parameter lists from multiple levels: @_opaqueReturnTypeOf(...) __..__ This fixes an ambiguity with parameter packs, where flattening the packs from multiple levels of nested types no longer produced an unambiguous result. To maintain backward compatibility, we still accept the old "flat" form when no parameter packs are present. --- lib/Sema/TypeCheckType.cpp | 111 ++++++++++++++---- .../invalid-opaque-result-types.swift | 1 - 2 files changed, 91 insertions(+), 21 deletions(-) diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 89ce69ff428f5..e3707e4e56003 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -4018,25 +4018,12 @@ TypeResolver::resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr, return elements; } +/// Implement the special @_opaqueReturnTypeOf attribute syntax in +/// module interface files. NeverNullType TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, StringRef mangledName, unsigned ordinal, TypeResolutionOptions options) { - // The type representation should be an unqualified identifier. We don't - // really use the identifier for anything, but we do resolve any generic - // arguments to instantiate the possibly-generic opaque type. - SmallVector TypeArgsBuf; - if (auto *unqualIdentRepr = dyn_cast(repr)) { - for (auto argRepr : unqualIdentRepr->getGenericArgs()) { - auto argTy = resolveType(argRepr, options); - // If we cannot resolve the generic parameter, propagate the error out. - if (argTy->hasError()) { - return ErrorType::get(getASTContext()); - } - TypeArgsBuf.push_back(argTy); - } - } - // Use type reconstruction to summon the opaque type decl. Demangler demangle; auto definingDeclNode = demangle.demangleSymbol(mangledName); @@ -4050,14 +4037,98 @@ TypeResolver::resolveOpaqueReturnType(TypeRepr *repr, StringRef mangledName, auto opaqueNode = builder.getNodeFactory().createNode(Node::Kind::OpaqueReturnTypeOf); opaqueNode->addChild(definingDeclNode, builder.getNodeFactory()); - - auto TypeArgs = ArrayRef(TypeArgsBuf); - auto ty = builder.resolveOpaqueType(opaqueNode, TypeArgs, ordinal); - if (!ty || ty->hasError()) { + auto *opaqueDecl = builder.resolveOpaqueTypeDecl(opaqueNode); + + auto *ownerDecl = opaqueDecl->getNamingDecl(); + if (!ownerDecl) { diagnose(repr->getLoc(), diag::no_opaque_return_type_of); return ErrorType::get(getASTContext()); } - return ty; + + auto genericSig = ownerDecl->getInnermostDeclContext() + ->getGenericSignatureOfContext(); + + SubstitutionMap subs; + if (genericSig) { + SmallVector args; + + // The type representation should either be a single identifier, or a + // series of member references. We don't use the identifiers for + // anything, but we do resolve the generic arguments at each level + // to instantiate the possibly-generic opaque type. + if (isa(repr) && + !genericSig->hasParameterPack()) { + // When there are no parameter packs and we just have a single + // unqualified identifier, we fall back to the legacy behavior, + // which collects the generic arguments for all levels of nesting + // in a flat list. + // + // This matches the old behavior of the ASTPrinter. + auto *unqualIdentRepr = cast(repr); + + for (auto argRepr : unqualIdentRepr->getGenericArgs()) { + auto argTy = resolveType(argRepr, options); + // If we cannot resolve the generic parameter, propagate the error out. + if (argTy->hasError()) { + return ErrorType::get(getASTContext()); + } + args.push_back(argTy); + } + + if (args.size() != genericSig.getGenericParams().size()) { + diagnose(repr->getLoc(), diag::no_opaque_return_type_of); + return ErrorType::get(getASTContext()); + } + } else { + // Correct handling of nested types. We interpret a qualified + // TypeRepr with a generic argument list at each level, like + // __.__. + SmallVector, 2> nestedArgs; + + auto *dc = ownerDecl->getInnermostDeclContext(); + while (!dc->isModuleScopeContext()) { + if (dc->isInnermostContextGeneric()) { + if (repr == nullptr || !isa(repr)) { + diagnose(repr->getLoc(), diag::no_opaque_return_type_of); + return ErrorType::get(getASTContext()); + } + + auto *identRepr = cast(repr); + nestedArgs.emplace_back(); + + auto *decl = dyn_cast(dc->getAsDecl()); + if (decl == nullptr) + decl = dc->getSelfNominalTypeDecl(); + ASSERT(decl); + + resolveGenericArguments(decl, + decl->getAsGenericContext(), + resolution, + silContext, + identRepr, + nestedArgs.back()); + repr = identRepr->getBase(); + } + + dc = dc->getParent(); + } + + for (auto &subArgs : llvm::reverse(nestedArgs)) { + args.append(subArgs.begin(), subArgs.end()); + } + } + + subs = SubstitutionMap::get(genericSig, args, + LookUpConformanceInModule()); + } + + if (ordinal >= opaqueDecl->getOpaqueGenericParams().size()) { + diagnose(repr->getLoc(), diag::no_opaque_return_type_of); + return ErrorType::get(getASTContext()); + } + + Type interfaceType = opaqueDecl->getOpaqueGenericParams()[ordinal]; + return OpaqueTypeArchetypeType::get(opaqueDecl, interfaceType, subs); } NeverNullType TypeResolver::resolveASTFunctionType( diff --git a/test/ModuleInterface/invalid-opaque-result-types.swift b/test/ModuleInterface/invalid-opaque-result-types.swift index fffc5dc985ade..40a13fca65840 100644 --- a/test/ModuleInterface/invalid-opaque-result-types.swift +++ b/test/ModuleInterface/invalid-opaque-result-types.swift @@ -17,7 +17,6 @@ // // RUN: not %target-swift-frontend -typecheck %s -I %t 2>&1 | %FileCheck %s -// CHECK: cannot find type 'InvalidParameter' in scope // CHECK: unable to resolve type for _opaqueReturnTypeOf attribute // CHECK: failed to build module 'InvalidOpaqueResultType' for importation import InvalidOpaqueResultType From 87cbb7d2d0ecd0df9edca630154576399cd42010 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 11 Sep 2025 18:00:48 -0400 Subject: [PATCH 4/5] AST: New way of printing @_opaqueReturnTypeOf when parameter packs are involved This is the ASTPrinter change to go with the Sema change. We now print @_opaqueReturnTypeOf using the new nested syntax when parameter packs are involved. Fixes rdar://problem/151171381. --- lib/AST/ASTPrinter.cpp | 33 ++++- .../variadic-opaque-result-types.swift | 122 ++++++++++++++++++ 2 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 test/ModuleInterface/variadic-opaque-result-types.swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index d23bcd445deec..99fce169bc559 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -7515,10 +7515,37 @@ class TypePrinter : public TypeVisitorgetASTContext(), - genericSig.getGenericParams(), - T->getSubstitutions().getReplacementTypes()); + auto &ctx = decl->getASTContext(); + auto params = genericSig.getGenericParams(); + auto args = T->getSubstitutions().getReplacementTypes(); + + // Use the new "nested" syntax if there is at least one parameter pack, + // because that case didn't round trip at all before anyway. Otherwise, + // use the old "flat" syntax, even when the owner declaration is in a + // nested generic context, because we want the generated swiftinterface + // to continue to work on old compilers. + if (genericSig->hasParameterPack()) { + bool first = true; + + while (!params.empty()) { + if (!first) { Printer << ".__"; } + first = false; + + unsigned end = 1; + unsigned depth = params.front()->getDepth(); + while (end < params.size() && params[end]->getDepth() == depth) { + ++end; + } + + printGenericArgs(ctx, params.take_front(end), args.take_front(end)); + params = params.slice(end); + args = args.slice(end); + } + } else { + printGenericArgs(ctx, params, args); + } } + return; } case PrintOptions::OpaqueReturnTypePrintingMode::Description: { diff --git a/test/ModuleInterface/variadic-opaque-result-types.swift b/test/ModuleInterface/variadic-opaque-result-types.swift new file mode 100644 index 0000000000000..7874a76d9b426 --- /dev/null +++ b/test/ModuleInterface/variadic-opaque-result-types.swift @@ -0,0 +1,122 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-emit-module-interface(%t/VariadicOpaqueResultTypes.swiftinterface) %s -module-name VariadicOpaqueResultTypes -target %target-swift-5.9-abi-triple +// RUN: %target-swift-typecheck-module-from-interface(%t/VariadicOpaqueResultTypes.swiftinterface) -module-name VariadicOpaqueResultTypes +// RUN: %FileCheck %s < %t/VariadicOpaqueResultTypes.swiftinterface + + +/// +/// First, make sure pack expansions can appear in the generic argument list of an opaque return type. +/// + +public struct I1: IteratorProtocol { + public mutating func next() -> (some Any)? { return 3 } +} + +// CHECK: public struct I1 : Swift.IteratorProtocol { +// CHECK: public mutating func next() -> (some Any)? +// CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes2I1V4nextQrSgyF", 0) __ +// CHECK: } + + +public struct S1: Sequence { + public func makeIterator() -> some IteratorProtocol { + return I1() + } +} + +// CHECK: public struct S1 : Swift.Sequence { +// CHECK: public func makeIterator() -> some Swift.IteratorProtocol +// CHECK: public typealias Element = (@_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes2S1V12makeIteratorQryF", 0) __).Element +// CHECK: public typealias Iterator = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes2S1V12makeIteratorQryF", 0) __ +// CHECK: } + + +public struct Scalar { + public struct I2: IteratorProtocol { + public mutating func next() -> (some Any)? { return 3 } + } +} + + +/// +/// Now, test nested types. The next example uses the old "flat" syntax, because parameter packs are not involved. +/// + + +// CHECK: public struct Scalar { +// CHECK: public struct I2 : Swift.IteratorProtocol { +// CHECK: public mutating func next() -> (some Any)? +// CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes6ScalarV2I2V4nextQrSgyF", 0) __ +// CHECK: } +// CHECK: } + + +public struct S2: Sequence { + public func makeIterator() -> Scalar.I2 { + return .init() + } +} + +// CHECK: public struct S2 : Swift.Sequence { +// CHECK: public func makeIterator() -> VariadicOpaqueResultTypes.Scalar.I2 +// CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes6ScalarV2I2V4nextQrSgyF", 0) __ +// CHECK: public typealias Iterator = VariadicOpaqueResultTypes.Scalar.I2 +// CHECK: } + + +/// +/// The remaining examples use the new nested syntax. +/// + +// CHECK: public struct Variadic { +public struct Variadic { + public struct I3: IteratorProtocol { + public mutating func next() -> (some Any)? { return 3 } + } + + // CHECK: public struct I3 : Swift.IteratorProtocol { + // CHECK: public mutating func next() -> (some Any)? + // CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes0A0V2I3V4nextQrSgyF", 0) __.__ + // CHECK: } + + public struct Middle { + public struct Inner { + public struct I4: IteratorProtocol { + public mutating func next() -> (some Any)? { return 3 } + } + } + } + + // CHECK: public struct Middle { + // CHECK: public struct Inner { + // CHECK: public struct I4 : Swift.IteratorProtocol { + // CHECK: public mutating func next() -> (some Any)? + // CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes0A0V6MiddleV5InnerV2I4V4nextQrSgyF", 0) __.__ + // CHECK: } + // CHECK: } + // CHECK: } +} +// CHECK: } + +// CHECK: public struct S3 : Swift.Sequence { +// CHECK: public func makeIterator() -> VariadicOpaqueResultTypes.Variadic.I3 +// CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes0A0V2I3V4nextQrSgyF", 0) __.__ +// CHECK: public typealias Iterator = VariadicOpaqueResultTypes.Variadic.I3 +// CHECK: } +public struct S3: Sequence { + public func makeIterator() -> Variadic.I3 { + return .init() + } +} + +// CHECK: public struct S4 : Swift.Sequence { +// CHECK: public func makeIterator() -> VariadicOpaqueResultTypes.Variadic.Middle.Inner.I4 +// CHECK: public typealias Element = @_opaqueReturnTypeOf("$s25VariadicOpaqueResultTypes0A0V6MiddleV5InnerV2I4V4nextQrSgyF", 0) __.__ +// CHECK: public typealias Iterator = VariadicOpaqueResultTypes.Variadic.Middle.Inner.I4 +// CHECK: } +public struct S4: Sequence { + public func makeIterator() -> Variadic.Middle.Inner.I4 { + return .init() + } +} + From 10fa09cae6a78e6eec2b1cdeba92d9ab0eacda04 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 11 Sep 2025 22:54:22 -0400 Subject: [PATCH 5/5] IRGen: Remove a bit of duplicated code --- lib/IRGen/GenMeta.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 392e69483f314..8a964165eb4d2 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -581,8 +581,7 @@ namespace { void addGenericParameters() { GenericSignature sig = asImpl().getGenericSignature(); auto metadata = - irgen::addGenericParameters(IGM, B, - asImpl().getGenericSignature(), + irgen::addGenericParameters(IGM, B, sig, /*implicit=*/false); assert(metadata.NumParams == metadata.NumParamsEmitted && "We can't use implicit GenericParamDescriptors here");