diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 888c11fff559d..cf65ce8111e18 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -3868,6 +3868,30 @@ class PreCheckReturnStmtRequest bool isCached() const { return true; } }; +/// Performs some pre-checking of a function body, including inserting and +/// removing implict returns where needed. This request is currently +/// side-effectful, as it will mutate the body in-place. +/// +/// Note this request is currently uncached as: +/// - The result will ultimately be cached by TypeCheckFunctionBodyRequest. +/// - When re-parsing function bodies in SourceKit, we can set a previously +/// type-checked function body back to being parsed, so caching the result of +/// this request would result in incorrectly attempting to restore the +/// previous body. We either need to eliminate the mutation of the AST, or +/// implement request cache invalidation through request dependencies. +class PreCheckFunctionBodyRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + BraceStmt *evaluate(Evaluator &evaluator, AbstractFunctionDecl *AFD) const; +}; + /// The result of the query for whether a statement can produce a single value. class IsSingleValueStmtResult { public: diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 243da8acd2076..e26034a3df926 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -325,6 +325,9 @@ SWIFT_REQUEST(TypeChecker, SynthesizeAccessorRequest, SeparatelyCached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, TangentStoredPropertyRequest, llvm::Expected(VarDecl *, CanType), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, PreCheckFunctionBodyRequest, + BraceStmt *(AbstractFunctionDecl *), + Uncached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyRequest, BraceStmt *(AbstractFunctionDecl *), SeparatelyCached, NoLocationInfo) diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index 909a09265f4b1..29c3fdeaa9438 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -310,6 +310,10 @@ class ConstraintLocator : public llvm::FoldingSetNode { /// SingleValueStmtExpr. bool isForSingleValueStmtConjunction() const; + /// Whether this locator identifies a conjunction for the branches of a + /// SingleValueStmtExpr, or a conjunction for one of the BraceStmts itself. + bool isForSingleValueStmtConjunctionOrBrace() const; + /// Whether this locator identifies a conversion for a SingleValueStmtExpr /// branch, and if so, the kind of branch. llvm::Optional isForSingleValueStmtBranch() const; diff --git a/lib/IDE/TypeCheckCompletionCallback.cpp b/lib/IDE/TypeCheckCompletionCallback.cpp index 1fb4987940010..f740b35d09e09 100644 --- a/lib/IDE/TypeCheckCompletionCallback.cpp +++ b/lib/IDE/TypeCheckCompletionCallback.cpp @@ -52,8 +52,6 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S, return nullptr; } - auto &CS = S.getConstraintSystem(); - Type Result; if (isExpr(Node)) { @@ -62,9 +60,9 @@ Type swift::ide::getTypeForCompletion(const constraints::Solution &S, Result = S.getResolvedType(Node); } - if (!Result || Result->is()) { - Result = CS.getContextualType(Node, /*forConstraint=*/false); - } + if (!Result || Result->is()) + Result = S.getContextualType(Node); + if (Result && Result->is()) { Result = Type(); } diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 080b4585b2cad..78d777227713f 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -883,11 +883,15 @@ bool ConjunctionStep::attempt(const ConjunctionElement &element) { assert(!ModifiedOptions.has_value() && "Previously modified options should have been restored in resume"); if (CS.isForCodeCompletion() && - !element.mightContainCodeCompletionToken(CS)) { + !element.mightContainCodeCompletionToken(CS) && + !getLocator()->isForSingleValueStmtConjunctionOrBrace()) { ModifiedOptions.emplace(CS.Options); // If we know that this conjunction element doesn't contain the code // completion token, type check it in normal mode without any special // behavior that is intended for the code completion token. + // Avoid doing this for SingleValueStmtExprs, because we can more eagerly + // prune branches in that case, which requires us to detect the code + // completion option while solving the conjunction. CS.Options -= ConstraintSystemFlags::ForCodeCompletion; } diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 0e3271ae1159f..60dbc3bc74b0f 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -242,7 +242,9 @@ class TypeVariableRefFinder : public ASTWalker { // MARK: Constraint generation /// Check whether it makes sense to convert this element into a constraint. -static bool isViableElement(ASTNode element) { +static bool isViableElement(ASTNode element, + bool isForSingleValueStmtCompletion, + ConstraintSystem &cs) { if (auto *decl = element.dyn_cast()) { // - Ignore variable declarations, they are handled by pattern bindings; // - Ignore #if, the chosen children should appear in the @@ -255,10 +257,21 @@ static bool isViableElement(ASTNode element) { } if (auto *stmt = element.dyn_cast()) { - // Empty brace statements are now viable because they do not require - // inference. if (auto *braceStmt = dyn_cast(stmt)) { - return braceStmt->getNumElements() > 0; + // Empty brace statements are not viable because they do not require + // inference. + if (braceStmt->empty()) + return false; + + // Skip if we're doing completion for a SingleValueStmtExpr, and have a + // brace that doesn't involve a single expression, and doesn't have a + // code completion token, as it won't contribute to the type of the + // SingleValueStmtExpr. + if (isForSingleValueStmtCompletion && + !braceStmt->getSingleActiveExpression() && + !cs.containsIDEInspectionTarget(braceStmt)) { + return false; + } } } @@ -324,13 +337,19 @@ static void createConjunction(ConstraintSystem &cs, VarRefCollector paramCollector(cs); + // Whether we're doing completion, and the conjunction is for a + // SingleValueStmtExpr, or one of its braces. + const auto isForSingleValueStmtCompletion = + cs.isForCodeCompletion() && + locator->isForSingleValueStmtConjunctionOrBrace(); + for (const auto &entry : elements) { ASTNode element = std::get<0>(entry); ContextualTypeInfo context = std::get<1>(entry); bool isDiscarded = std::get<2>(entry); ConstraintLocator *elementLoc = std::get<3>(entry); - if (!isViableElement(element)) + if (!isViableElement(element, isForSingleValueStmtCompletion, cs)) continue; // If this conjunction going to represent a body of a closure, @@ -1122,9 +1141,13 @@ class SyntacticElementConstraintGenerator for (auto element : braceStmt->getElements()) { if (cs.isForCodeCompletion() && - !cs.containsIDEInspectionTarget(element)) { + !cs.containsIDEInspectionTarget(element) && + !(braceStmt->getSingleActiveExpression() && + locator->isForSingleValueStmtConjunctionOrBrace())) { // To improve performance, skip type checking elements that can't - // influence the code completion token. + // influence the code completion token. Note we don't do this for + // single expression SingleValueStmtExpr branches, as they're needed to + // infer the type. if (element.is() && !element.isStmt(StmtKind::Guard) && !element.isStmt(StmtKind::Return)) { // Statements can't influence the expresion that contains the code // completion token. diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 89a72217dbfb4..f789a4f49dae9 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -634,22 +634,52 @@ bool ConstraintLocator::isForMacroExpansion() const { return directlyAt(); } -bool ConstraintLocator::isForSingleValueStmtConjunction() const { - auto *SVE = getAsExpr(getAnchor()); +static bool isForSingleValueStmtConjunction(ASTNode anchor, + ArrayRef path) { + auto *SVE = getAsExpr(anchor); if (!SVE) return false; - // Ignore a trailing SyntacticElement path element for the statement. - auto path = getPath(); - if (auto elt = getLastElementAs()) { - if (elt->getElement() == ASTNode(SVE->getStmt())) - path = path.drop_back(); + if (!path.empty()) { + // Ignore a trailing SyntacticElement path element for the statement. + if (auto elt = path.back().getAs()) { + if (elt->getElement() == ASTNode(SVE->getStmt())) + path = path.drop_back(); + } } // Other than the trailing SyntaticElement, we must be at the anchor. return path.empty(); } +bool ConstraintLocator::isForSingleValueStmtConjunction() const { + return ::isForSingleValueStmtConjunction(getAnchor(), getPath()); +} + +bool ConstraintLocator::isForSingleValueStmtConjunctionOrBrace() const { + if (!isExpr(getAnchor())) + return false; + + auto path = getPath(); + while (!path.empty()) { + // Ignore a trailing TernaryBranch locator, which is used for if statements. + if (path.back().is()) { + path = path.drop_back(); + continue; + } + + // Ignore a SyntaticElement path element for a case statement of a switch. + if (auto elt = path.back().getAs()) { + if (elt->getElement().isStmt(StmtKind::Case)) { + path = path.drop_back(); + continue; + } + } + break; + } + return ::isForSingleValueStmtConjunction(getAnchor(), path); +} + llvm::Optional ConstraintLocator::isForSingleValueStmtBranch() const { // Ignore a trailing ContextualType path element. diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index da586167e839a..116f19c344a45 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2396,6 +2396,13 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( return false; } } + if (auto *AFD = dyn_cast(DC)) { + if (AFD->hasBody() && !AFD->isBodyTypeChecked()) { + // Pre-check the function body if needed. + (void)evaluateOrDefault(evaluator, PreCheckFunctionBodyRequest{AFD}, + nullptr); + } + } } // Find innermost ASTNode at Loc from DC. Results the reference to the found @@ -2519,6 +2526,26 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( if (isa(E)) return Action::SkipChildren(E); + // If the location is within a single-expression branch of a + // SingleValueStmtExpr, walk it directly rather than as part of the brace. + // This ensures we type-check it a part of the whole expression, unless it + // has an inner closure or SVE, in which case we can still pick up a + // better node to type-check. + if (auto *SVE = dyn_cast(E)) { + SmallVector scratch; + for (auto *branch : SVE->getSingleExprBranches(scratch)) { + auto branchCharRange = Lexer::getCharSourceRangeFromSourceRange( + SM, branch->getSourceRange()); + if (branchCharRange.contains(Loc)) { + if (!branch->walk(*this)) + return Action::Stop(); + + return Action::SkipChildren(E); + } + } + return Action::Continue(E); + } + if (auto closure = dyn_cast(E)) { // NOTE: When a client wants to type check a closure signature, it // requests with closure's 'getLoc()' location. @@ -2599,11 +2626,6 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( // thus the transform couldn't be applied. Perform code completion // pretending there was no result builder to recover. } - } else if (func->hasSingleExpressionBody() && - func->getResultInterfaceType()->isVoid()) { - // The function returns void. We don't need an explicit return, no matter - // what the type of the expression is. Take the inserted return back out. - func->getBody()->setLastElement(func->getSingleExpressionBody()); } } @@ -2641,7 +2663,60 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( } BraceStmt * -TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, +PreCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, + AbstractFunctionDecl *AFD) const { + auto &ctx = AFD->getASTContext(); + auto *body = AFD->getBody(); + assert(body && "Expected body"); + assert(!AFD->isBodyTypeChecked() && "Body already type-checked?"); + + if (auto *func = dyn_cast(AFD)) { + // Don't apply this pre-checking to functions with result builders. + if (getResultBuilderType(func)) + return body; + + if (func->hasSingleExpressionBody() && + func->getResultInterfaceType()->isVoid()) { + // The function returns void. We don't need an explicit return, no + // matter what the type of the expression is. Take the inserted return + // back out. + body->setLastElement(func->getSingleExpressionBody()); + } + // If there is a single statement in the body that can be turned into a + // single expression return, do so now. + if (!func->getResultInterfaceType()->isVoid()) { + if (auto *S = body->getSingleActiveStatement()) { + if (S->mayProduceSingleValue(evaluator)) { + auto *SVE = SingleValueStmtExpr::createWithWrappedBranches( + ctx, S, /*DC*/ func, /*mustBeExpr*/ false); + auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE); + body->setLastElement(RS); + func->setHasSingleExpressionBody(); + func->setSingleExpressionBody(SVE); + } + } + } + } + + if (auto *ctor = dyn_cast(AFD)) { + if (body->empty() || !isKnownEndOfConstructor(body->getLastElement())) { + // For constructors, we make sure that the body ends with a "return" + // stmt, which we either implicitly synthesize, or the user can write. + // This simplifies SILGen. + SmallVector Elts(body->getElements().begin(), + body->getElements().end()); + Elts.push_back(new (ctx) ReturnStmt(body->getRBraceLoc(), + /*value*/ nullptr, + /*implicit*/ true)); + body = BraceStmt::create(ctx, body->getLBraceLoc(), Elts, + body->getRBraceLoc(), body->isImplicit()); + } + } + return body; +} + +BraceStmt * +TypeCheckFunctionBodyRequest::evaluate(Evaluator &eval, AbstractFunctionDecl *AFD) const { ASTContext &ctx = AFD->getASTContext(); @@ -2672,6 +2747,12 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, range.End); }; + // First do a pre-check of the body. + body = evaluateOrDefault(eval, PreCheckFunctionBodyRequest{AFD}, nullptr); + assert(body); + + // Then apply a result builder if we have one, which if successful will + // produce a type-checked body. bool alreadyTypeChecked = false; if (auto *func = dyn_cast(AFD)) { if (Type builderType = getResultBuilderType(func)) { @@ -2686,42 +2767,6 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, body->walk(ContextualizeClosuresAndMacros(AFD)); } - } else { - if (func->hasSingleExpressionBody() && - func->getResultInterfaceType()->isVoid()) { - // The function returns void. We don't need an explicit return, no - // matter what the type of the expression is. Take the inserted return - // back out. - body->setLastElement(func->getSingleExpressionBody()); - } - // If there is a single statement in the body that can be turned into a - // single expression return, do so now. - if (!func->getResultInterfaceType()->isVoid()) { - if (auto *S = body->getSingleActiveStatement()) { - if (S->mayProduceSingleValue(evaluator)) { - auto *SVE = SingleValueStmtExpr::createWithWrappedBranches( - ctx, S, /*DC*/ func, /*mustBeExpr*/ false); - auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE); - body->setLastElement(RS); - func->setHasSingleExpressionBody(); - func->setSingleExpressionBody(SVE); - } - } - } - } - } else if (auto *ctor = dyn_cast(AFD)) { - if (body->empty() || - !isKnownEndOfConstructor(body->getLastElement())) { - // For constructors, we make sure that the body ends with a "return" stmt, - // which we either implicitly synthesize, or the user can write. This - // simplifies SILGen. - SmallVector Elts(body->getElements().begin(), - body->getElements().end()); - Elts.push_back(new (ctx) ReturnStmt(body->getRBraceLoc(), - /*value*/nullptr, - /*implicit*/true)); - body = BraceStmt::create(ctx, body->getLBraceLoc(), Elts, - body->getRBraceLoc(), body->isImplicit()); } } diff --git a/test/IDE/complete_if_switch_expr.swift b/test/IDE/complete_if_switch_expr.swift new file mode 100644 index 0000000000000..653740af9949f --- /dev/null +++ b/test/IDE/complete_if_switch_expr.swift @@ -0,0 +1,349 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -debug-forbid-typecheck-prefix FORBIDDEN -filecheck %raw-FileCheck -completion-output-dir %t + +enum E { + case e + case f(Int) +} + +struct SomeError: Error {} + +func ifExprDotReturn() -> E { + if .random() { + .#^DOT1?check=DOT^# + } else { + .e + } +} + +func switchExprDotReturn() -> E { + switch Bool.random() { + case true: + .e + case false: + .#^DOT2?check=DOT^# + } +} + +func ifExprDotClosureReturn() -> E { + let x: E = { + if .random() { + .e + } else { + .#^DOT3?check=DOT^# + } + }() + return x +} + +func switchExprDotClosureReturn() -> E { + let x: E = { + switch Bool.random() { + case true: + .#^DOT4?check=DOT^# + case false: + .e + } + }() + return x +} + +func ifExprBranchInferenceReturn1() -> E { + let fn = { + if .random() { + E.e + } else { + .#^DOT5?check=DOT^# + } + } + return fn() +} + +func switchExprBranchInferenceReturn1() -> E { + let fn = { + switch Bool.random() { + case true: + E.e + case false: + .#^DOT6?check=DOT^# + } + } + return fn() +} + +func ifExprBranchInferenceReturn2() -> E { + let fn = { + if .random() { + .#^DOT7?check=DOT^# + } else { + E.e + } + } + return fn() +} + +func switchExprBranchInferenceReturn2() -> E { + let fn = { + switch Bool.random() { + case true: + .#^DOT8?check=DOT^# + case false: + E.e + } + } + return fn() +} + +func ifExprBinding() -> E { + let x: E = + if .random() { + .e + } else { + .#^DOT9?check=DOT^# + } + return x +} + +func switchExprBinding() -> E { + let x: E = + switch Bool.random() { + case true: + .e + case false: + .#^DOT10?check=DOT^# + } + return x +} + +// Triggering interface type computation of this will cause an assert to fire, +// ensuring we don't attempt to type-check any references to it. +let NOTYPECHECK = FORBIDDEN + +func testSkipTypechecking1() -> E { + // Make sure we don't try to type-check the first branch, as it doesn't + // contribute to the type. + if .random() { + _ = NOTYPECHECK + throw SomeError() + } else { + .#^DOT11?check=DOT^# + } +} + +func testSkipTypechecking2() -> E { + // Make sure we don't try to type-check the second branch, as it doesn't + // contribute to the type. + if .random() { + .#^DOT12?check=DOT^# + } else { + _ = NOTYPECHECK + throw SomeError() + } +} + +func testSkipTypechecking3() -> E { + // Make sure we don't try to type-check the first branch, as it doesn't + // contribute to the type. + switch Bool.random() { + case true: + _ = NOTYPECHECK + throw SomeError() + case false: + .#^DOT13?check=DOT^# + } +} + +func testSkipTypechecking4() -> E { + // Make sure we don't try to type-check the second branch, as it doesn't + // contribute to the type. + switch Bool.random() { + case true: + .#^DOT14?check=DOT^# + case false: + _ = NOTYPECHECK + throw SomeError() + } +} + +func takesE(_ e: E) {} + +func testSkipTypechecking5() throws -> E { + // Make sure we only type-check the first branch. + if .random() { + // We can still skip type-checking this tho. + x = NOTYPECHECK + + takesE(.#^DOT15?check=DOT^#) + throw SomeError() + } else { + NOTYPECHECK + } +} + +func testSkipTypechecking6() throws -> E { + // Make sure we only type-check the first branch. + switch Bool.random() { + case true: + // We can still skip type-checking this tho. + x = NOTYPECHECK + + takesE(.#^DOT16?check=DOT^#) + throw SomeError() + case false: + NOTYPECHECK + } +} + +enum F { + case x +} + +func takesIntOrDouble(_ i: Int) -> E { E.e } +func takesIntOrDouble(_ i: Double) -> F { F.x } + +func testSkipTypechecking7() throws -> E { + let fn = { + switch Bool.random() { + case true: + .#^DOT17?check=DOT^# + case false: + // Make sure we don't end up with an ambiguity here. + takesIntOrDouble(0) + } + } + return fn() +} + +func testSkipTypeChecking8() throws -> E { + let e: E = if Bool.random() { + takesE(.#^DOT18?check=DOT^#) + throw NOTYPECHECK + } else { + NOTYPECHECK + } + return e +} + +func testSkipTypeChecking8() throws -> E { + // Only need to type-check the inner function in this case. + let e: E = if Bool.random() { + func localFunc() { + takesE(.#^DOT19?check=DOT^#) + } + throw NOTYPECHECK + } else { + NOTYPECHECK + } + return e +} + +func takesArgAndClosure(_ x: Int, fn: () -> T) -> T { fatalError() } + +func testSkipTypeChecking9() -> E { + // We need to type-check everything for this. + if Bool.random() { + .e + } else { + takesArgAndClosure(0) { + .#^DOT20?check=DOT^# + } + } +} + +func testSkipTypeChecking10() -> E { + // We only need to type-check the inner-most function for this. + if Bool.random() { + NOTYPECHECK + } else { + takesArgAndClosure(NOTYPECHECK) { + func foo() { + takesE(.#^DOT21?check=DOT^#) + } + return NOTYPECHECK + } + } +} + +func testSkipTypeChecking11() -> E { + // We only need to type-check the inner-most function for this. + if Bool.random() { + NOTYPECHECK + } else { + if NOTYPECHECK { + func foo() { + takesE(.#^DOT22?check=DOT^#) + } + throw NOTYPECHECK + } else { + NOTYPECHECK + } + } +} + +func testSkipTypeChecking12() -> E { + // Only need to type-check the condition here. + if .#^DOT23?check=BOOL^# { + NOTYPECHECK + } else { + NOTYPECHECK + } +} + +// BOOL: Begin completions +// BOOL-DAG: Decl[Constructor]/CurrNominal/IsSystem/TypeRelation[Convertible]: init()[#Bool#]; name=init() +// BOOL-DAG: Decl[StaticMethod]/CurrNominal/Flair[ExprSpecific]/IsSystem/TypeRelation[Convertible]: random()[#Bool#]; name=random() + +func testSkipTypeChecking13(_ e: E) -> E { + // TODO: Currently this requires type-checking the bodies, but we ought to + // be able to skip them. This is also an issue in closures. + switch e { + case .#^DOT24?check=DOT^#: + .e + default: + .e + } +} + +func testSkipTypeChecking14() -> E { + if Bool.random() { + .#^DOT25?check=DOT^# + } else if .random() { + let x = NOTYPECHECK + throw x + } else if .random() { + let x = NOTYPECHECK + throw x + } else { + let x = NOTYPECHECK + throw x + } +} + +func testSkipTypeChecking14() -> E { + switch Bool.random() { + case true: + .#^DOT26?check=DOT^# + case _ where Bool.random(): + let x = NOTYPECHECK + throw x + case _ where Bool.random(): + let x = NOTYPECHECK + throw x + default: + let x = NOTYPECHECK + throw x + } +} + +func testSkipTypechecking15(_ x: inout Int) -> E { + switch Bool.random() { + case true: + .#^DOT27?check=DOT^# + case false: + x = 0 + } +} + +// DOT: Begin completions, 2 items +// DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: e[#E#]; name=e +// DOT-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: f({#Int#})[#E#]; name=f()