From af78807313c85ddc3da7bd60bee8ad0fbd63eaa3 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 11 Sep 2025 18:36:58 -0400 Subject: [PATCH] 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 89ce69ff428..e3707e4e560 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 fffc5dc985a..40a13fca658 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