diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index b12ead478d4e6..bc5ad7bdffbef 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -243,6 +243,10 @@ SWIFT_NAME("BridgedASTContext.getIdentifier(self:_:)") BridgedIdentifier BridgedASTContext_getIdentifier(BridgedASTContext cContext, BridgedStringRef cStr); +SWIFT_NAME("BridgedASTContext.getDollarIdentifier(self:_:)") +BridgedIdentifier +BridgedASTContext_getDollarIdentifier(BridgedASTContext cContext, size_t idx); + SWIFT_NAME("BridgedASTContext.langOptsHasFeature(self:_:)") bool BridgedASTContext_langOptsHasFeature(BridgedASTContext cContext, BridgedFeature feature); @@ -552,6 +556,13 @@ SWIFT_NAME("getter:BridgedDeclContext.isModuleScopeContext(self:)") BRIDGED_INLINE bool BridgedDeclContext_isModuleScopeContext(BridgedDeclContext dc); +SWIFT_NAME("getter:BridgedDeclContext.isClosureExpr(self:)") +BRIDGED_INLINE bool BridgedDeclContext_isClosureExpr(BridgedDeclContext dc); + +SWIFT_NAME("BridgedDeclContext.castToClosureExpr(self:)") +BRIDGED_INLINE BridgedClosureExpr +BridgedDeclContext_castToClosureExpr(BridgedDeclContext dc); + SWIFT_NAME("getter:BridgedDeclContext.astContext(self:)") BRIDGED_INLINE BridgedASTContext BridgedDeclContext_getASTContext(BridgedDeclContext dc); @@ -1173,6 +1184,9 @@ BRIDGED_INLINE void BridgedParamDecl_setSpecifier(BridgedParamDecl cDecl, BridgedParamSpecifier cSpecifier); +SWIFT_NAME("BridgedParamDecl.setImplicit(self:)") +BRIDGED_INLINE void BridgedParamDecl_setImplicit(BridgedParamDecl cDecl); + SWIFT_NAME("BridgedConstructorDecl.setParsedBody(self:_:)") void BridgedConstructorDecl_setParsedBody(BridgedConstructorDecl decl, BridgedBraceStmt body); @@ -1575,10 +1589,17 @@ BridgedClosureExpr BridgedClosureExpr_createParsed( BridgedSourceLoc cArrowLoc, BridgedNullableTypeRepr cExplicitResultType, BridgedSourceLoc cInLoc); +SWIFT_NAME("BridgedClosureExpr.getParameterList(self:)") +BridgedParameterList +BridgedClosureExpr_getParameterList(BridgedClosureExpr cClosure); + SWIFT_NAME("BridgedClosureExpr.setParameterList(self:_:)") void BridgedClosureExpr_setParameterList(BridgedClosureExpr cClosure, BridgedParameterList cParams); +SWIFT_NAME("getter:BridgedClosureExpr.hasAnonymousClosureVars(self:)") +bool BridgedClosureExpr_hasAnonymousClosureVars(BridgedClosureExpr cClosure); + SWIFT_NAME("BridgedClosureExpr.setHasAnonymousClosureVars(self:)") void BridgedClosureExpr_setHasAnonymousClosureVars(BridgedClosureExpr cClosure); @@ -2477,6 +2498,13 @@ BridgedParameterList BridgedParameterList_createParsed( BridgedASTContext cContext, BridgedSourceLoc cLeftParenLoc, BridgedArrayRef cParameters, BridgedSourceLoc cRightParenLoc); +SWIFT_NAME("getter:BridgedParameterList.size(self:)") +size_t BridgedParameterList_size(BridgedParameterList cParameterList); + +SWIFT_NAME("BridgedParameterList.get(self:_:)") +BridgedParamDecl BridgedParameterList_get(BridgedParameterList cParameterList, + size_t i); + struct BridgedASTType { swift::TypeBase * _Nullable type; diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index 337c1be509f97..06a2cd6f8b190 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -134,6 +134,16 @@ bool BridgedDeclContext_isModuleScopeContext(BridgedDeclContext dc) { return dc.unbridged()->isModuleScopeContext(); } +bool BridgedDeclContext_isClosureExpr(BridgedDeclContext dc) { + return llvm::isa_and_present( + llvm::dyn_cast(dc.unbridged())); +} + +BridgedClosureExpr BridgedDeclContext_castToClosureExpr(BridgedDeclContext dc) { + return llvm::cast( + llvm::cast(dc.unbridged())); +} + BridgedASTContext BridgedDeclContext_getASTContext(BridgedDeclContext dc) { return dc.unbridged()->getASTContext(); } @@ -270,6 +280,10 @@ void BridgedParamDecl_setSpecifier(BridgedParamDecl cDecl, cDecl.unbridged()->setSpecifier(unbridge(cSpecifier)); } +void BridgedParamDecl_setImplicit(BridgedParamDecl cDecl) { + cDecl.unbridged()->setImplicit(); +} + //===----------------------------------------------------------------------===// // MARK: BridgedSubscriptDecl //===----------------------------------------------------------------------===// diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index e7496edae63f6..e56279d39b5ea 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -598,6 +598,10 @@ class ASTContext final { /// specified string. Identifier getIdentifier(StringRef Str) const; + /// getDollarIdentifier - Return the uniqued and AST-Context-owned version of + /// anonymous closure parameter (e.g. '$1') name by the index. + Identifier getDollarIdentifier(size_t Idx) const; + /// Convert a given alias map to a map of Identifiers between module aliases and their actual names. /// For example, if '-module-alias Foo=X -module-alias Bar=Y' input is passed in, the aliases Foo and Bar are /// the names of the imported or referenced modules in source files in the main module, and X and Y diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4bf3b59f3d254..16cd23b1c799f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -971,6 +971,12 @@ Identifier ASTContext::getIdentifier(StringRef Str) const { return Identifier(I->getKeyData()); } +Identifier ASTContext::getDollarIdentifier(size_t Idx) const { + SmallVector StrBuf; + StringRef varName = ("$" + Twine(Idx)).toStringRef(StrBuf); + return getIdentifier(varName); +} + void ASTContext::lookupInModule( ModuleDecl *M, StringRef name, diff --git a/lib/AST/Bridging/ASTContextBridging.cpp b/lib/AST/Bridging/ASTContextBridging.cpp index 6cec0b04d73f2..4f511ac010ccd 100644 --- a/lib/AST/Bridging/ASTContextBridging.cpp +++ b/lib/AST/Bridging/ASTContextBridging.cpp @@ -26,6 +26,11 @@ BridgedIdentifier BridgedASTContext_getIdentifier(BridgedASTContext cContext, return cContext.unbridged().getIdentifier(cStr.unbridged()); } +BridgedIdentifier +BridgedASTContext_getDollarIdentifier(BridgedASTContext cContext, size_t idx) { + return cContext.unbridged().getDollarIdentifier(idx); +} + bool BridgedASTContext_langOptsHasFeature(BridgedASTContext cContext, BridgedFeature feature) { return cContext.unbridged().LangOpts.hasFeature((Feature)feature); diff --git a/lib/AST/Bridging/DeclBridging.cpp b/lib/AST/Bridging/DeclBridging.cpp index 3716c01eeddf3..3bcbbe2b963c3 100644 --- a/lib/AST/Bridging/DeclBridging.cpp +++ b/lib/AST/Bridging/DeclBridging.cpp @@ -728,6 +728,10 @@ bool BridgedNominalTypeDecl_isStructWithUnreferenceableStorage( return false; } +//===----------------------------------------------------------------------===// +// MARK: BridgedParameterList +//===----------------------------------------------------------------------===// + BridgedParameterList BridgedParameterList_createParsed( BridgedASTContext cContext, BridgedSourceLoc cLeftParenLoc, BridgedArrayRef cParameters, BridgedSourceLoc cRightParenLoc) { @@ -736,3 +740,12 @@ BridgedParameterList BridgedParameterList_createParsed( cParameters.unbridged(), cRightParenLoc.unbridged()); } + +size_t BridgedParameterList_size(BridgedParameterList cParameterList) { + return cParameterList.unbridged()->size(); +} + +BridgedParamDecl BridgedParameterList_get(BridgedParameterList cParameterList, + size_t i) { + return cParameterList.unbridged()->get(i); +} diff --git a/lib/AST/Bridging/ExprBridging.cpp b/lib/AST/Bridging/ExprBridging.cpp index 355f0d1ebb543..1cb52f3f0641e 100644 --- a/lib/AST/Bridging/ExprBridging.cpp +++ b/lib/AST/Bridging/ExprBridging.cpp @@ -146,11 +146,20 @@ BridgedClosureExpr BridgedClosureExpr_createParsed( declContext); } +BridgedParameterList +BridgedClosureExpr_getParameterList(BridgedClosureExpr cClosure) { + return cClosure.unbridged()->getParameters(); +} + void BridgedClosureExpr_setParameterList(BridgedClosureExpr cClosure, BridgedParameterList cParams) { cClosure.unbridged()->setParameterList(cParams.unbridged()); } +bool BridgedClosureExpr_hasAnonymousClosureVars(BridgedClosureExpr cClosure) { + return cClosure.unbridged()->hasAnonymousClosureVars(); +} + void BridgedClosureExpr_setHasAnonymousClosureVars( BridgedClosureExpr cClosure) { cClosure.unbridged()->setHasAnonymousClosureVars(); diff --git a/lib/ASTGen/Sources/ASTGen/Exprs.swift b/lib/ASTGen/Sources/ASTGen/Exprs.swift index c3e0bd67ef430..051bda880e03c 100644 --- a/lib/ASTGen/Sources/ASTGen/Exprs.swift +++ b/lib/ASTGen/Sources/ASTGen/Exprs.swift @@ -309,28 +309,47 @@ extension ASTGenVisitor { inLoc: signature.inLoc ) - let body = self.withDeclContext(expr.asDeclContext) { - BridgedBraceStmt.createParsed( - self.ctx, - lBraceLoc: self.generateSourceLoc(node.leftBrace), - elements: self.generate(codeBlockItemList: node.statements), - rBraceLoc: self.generateSourceLoc(node.rightBrace) - ) - } - if signature.params == nil { - // TODO: Handle doller identifiers inside the closure. let loc = self.generateSourceLoc(node.leftBrace) - let params = BridgedParameterList.createParsed( + var params: [BridgedParamDecl] = [] + if let anonymousParamMaxIndex = findMaxAnonymousClosureParamUsage(closureExpr: node) { + for idx in 0...anonymousParamMaxIndex { + let param = BridgedParamDecl.createParsed( + self.ctx, + declContext: expr.asDeclContext, + specifierLoc: nil, + argName: nil, + argNameLoc: nil, + paramName: ctx.getDollarIdentifier(idx), + paramNameLoc: loc, + type: nil, + defaultValue: nil + ) + param.setSpecifier(.default) + param.setImplicit() + params.append(param) + } + } + + let paramList = BridgedParameterList.createParsed( self.ctx, leftParenLoc: loc, - parameters: .init(), + parameters: params.lazy.bridgedArray(in: self), rightParenLoc: loc ) - expr.setParameterList(params) + expr.setParameterList(paramList) expr.setHasAnonymousClosureVars() } + let body = self.withDeclContext(expr.asDeclContext) { + BridgedBraceStmt.createParsed( + self.ctx, + lBraceLoc: self.generateSourceLoc(node.leftBrace), + elements: self.generate(codeBlockItemList: node.statements), + rBraceLoc: self.generateSourceLoc(node.rightBrace) + ) + } + expr.setBody(body) if signature.captureList.count > 0 { @@ -539,13 +558,54 @@ extension ASTGenVisitor { ) } + func generateDollarIdentifierExpr(token: TokenSyntax) -> BridgedExpr { + let loc = self.generateSourceLoc(token) + + guard self.declContext.isClosureExpr else { + // TODO: Diagnose dollar identifier not in ClosureExpr + fatalError("dollar identifier not in ClosureExpr") + } + + guard let idx = parseDollarIdentifierIndex(text: token.rawText) else { + // TODO: Diagnose. + fatalError("(compiler bug) malformed dollar identifier token") + } + + let closure = self.declContext.castToClosureExpr() + let params = closure.getParameterList() + + if !closure.hasAnonymousClosureVars { + // TODO: Diagnose and fix it to the named parameter. + // Fall through, bind to the parameter even though the name doesn't match. + } + + guard idx < params.size else { + // TODO: Diagnose out of range (but should be unreachable) + fatalError("(compiler bug) out-of-bound dollar identifier number") + } + + let param = params.get(idx) + + return BridgedDeclRefExpr.create( + self.ctx, + decl: param.asDecl, + loc: .createParsed(loc), + isImplicit: false + ).asExpr + } + func generate(declReferenceExpr node: DeclReferenceExprSyntax) -> BridgedExpr { if node.baseName.isEditorPlaceholder { + if node.argumentNames != nil { + // TODO: Diagnose. + } return generateEditorPlaceholderExpr(token: node.baseName).asExpr } if node.baseName.rawTokenKind == .dollarIdentifier { - // TODO: Handle dollar identifier in closure decl context. - // It's done in C++ Parser because it needs to handle inactive #if regions. + if node.argumentNames != nil { + // TODO: Diagnose. + } + return generateDollarIdentifierExpr(token: node.baseName) } let nameAndLoc = generateDeclNameRef(declReferenceExpr: node) return BridgedUnresolvedDeclRefExpr.createParsed( @@ -1168,3 +1228,128 @@ extension ASTGenVisitor { ); } } + +private func parseDollarIdentifierIndex(text: SyntaxText) -> Int? { + precondition(text.first == UInt8(ascii: "$")) + var result = 0 + for c in text.dropFirst() { + switch c { + case UInt8(ascii: "0")...UInt8(ascii: "9"): + let digit = Int(c &- UInt8(ascii: "0")) + result = result * 10 + digit + default: + return nil + } + } + return result +} + +func findMaxAnonymousClosureParamUsage(closureExpr node: ClosureExprSyntax) -> Int? { + let visitor = ClosureAnonymousParameterFinder() + visitor.walk(node.statements) + return visitor.maxIndex + + class ClosureAnonymousParameterFinder: SyntaxVisitor { + var maxIndex: Int? = nil + + init() { + super.init(viewMode: .sourceAccurate) + } + + override func visit(_ node: DeclReferenceExprSyntax) -> SyntaxVisitorContinueKind { + if + node.baseName.rawTokenKind == .dollarIdentifier, + let idx = parseDollarIdentifierIndex(text: node.baseName.rawText) + { + self.maxIndex = max(self.maxIndex ?? 0, idx) + } + return .skipChildren + } + + // Nodes that change the decl context. + + override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: SubscriptDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: DeinitializerDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: AccessorBlockSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: MacroDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: EnumCaseDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + + // Nodes that never contain expressions, but can have many descendant nodes. + + override func visit(_ node: ArrayTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: AttributedTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: CompositionTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: DictionaryTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: FunctionTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: MemberTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: TupleTypeSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: AssociatedTypeDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: PrecedenceGroupDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + override func visit(_ node: PoundSourceLocationSyntax) -> SyntaxVisitorContinueKind { + return .skipChildren + } + } + +} + diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 332114e53e010..47b5533ac6d55 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3106,9 +3106,7 @@ Expr *Parser::parseExprAnonClosureArg() { auto &decls = AnonClosureVars.back().Item; while (ArgNo >= decls.size()) { unsigned nextIdx = decls.size(); - SmallVector StrBuf; - StringRef varName = ("$" + Twine(nextIdx)).toStringRef(StrBuf); - Identifier ident = Context.getIdentifier(varName); + Identifier ident = Context.getDollarIdentifier(nextIdx); SourceLoc varLoc = leftBraceLoc; auto *var = new (Context) ParamDecl(SourceLoc(), SourceLoc(), diff --git a/test/ASTGen/exprs.swift b/test/ASTGen/exprs.swift index c99263141af07..2c777e1f8c323 100644 --- a/test/ASTGen/exprs.swift +++ b/test/ASTGen/exprs.swift @@ -97,6 +97,7 @@ func acceptClosures(x: () -> Void) {} func acceptClosures(x: () -> Void, y: () -> Int) {} func acceptClosures(x: () -> Void, y: () -> Int, _ z: () -> Void) {} func acceptClosures(x: (Int, String) -> Void) {} +func acceptClosures(x: (Int, String, inout String) -> Void) {} func testTrailingClsure() { acceptClosures {} acceptClosures() {} @@ -108,6 +109,13 @@ func testTrailingClsure() { acceptClosures { (x, y: String) -> Void in } acceptClosures { x, y in } acceptClosures { @Sendable x, y in } + + acceptClosures { $1.count == $0.bitWidth } + acceptClosures { $1.count } + acceptClosures { + _ = $1 + acceptClosures { $2 = $1 } + } } func testInOut() {