Skip to content

[Sema] Some more cleanup now that we migrated all of code completion to solver-based #68363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 4 additions & 12 deletions include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ class SyntacticElementTarget;
// so they could be made friends of ConstraintSystem.
namespace TypeChecker {

llvm::Optional<BraceStmt *> applyResultBuilderBodyTransform(
FuncDecl *func, Type builderType,
bool ClosuresInResultBuilderDontParticipateInInference);
llvm::Optional<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,
Type builderType);

llvm::Optional<constraints::SyntacticElementTarget>
typeCheckExpression(constraints::SyntacticElementTarget &target,
Expand Down Expand Up @@ -1815,12 +1814,6 @@ enum class ConstraintSystemFlags {

/// Disable macro expansions.
DisableMacroExpansions = 0x100,

/// Non solver-based code completion expects that closures inside result
/// builders don't participate in inference.
/// Once all code completion kinds are migrated to solver-based we should be
/// able to remove this flag.
ClosuresInResultBuildersDontParticipateInInference = 0x200,
};

/// Options that affect the constraint system as a whole.
Expand Down Expand Up @@ -2877,9 +2870,8 @@ class ConstraintSystem {

// FIXME: Perhaps these belong on ConstraintSystem itself.
friend llvm::Optional<BraceStmt *>
swift::TypeChecker::applyResultBuilderBodyTransform(
FuncDecl *func, Type builderType,
bool ClosuresInResultBuilderDontParticipateInInference);
swift::TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func,
Type builderType);

friend llvm::Optional<SyntacticElementTarget>
swift::TypeChecker::typeCheckExpression(
Expand Down
9 changes: 2 additions & 7 deletions lib/Sema/BuilderTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -912,9 +912,8 @@ class ResultBuilderTransform

} // end anonymous namespace

llvm::Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
FuncDecl *func, Type builderType,
bool ClosuresInResultBuilderDontParticipateInInference) {
llvm::Optional<BraceStmt *>
TypeChecker::applyResultBuilderBodyTransform(FuncDecl *func, Type builderType) {
// Pre-check the body: pre-check any expressions in it and look
// for return statements.
//
Expand Down Expand Up @@ -970,10 +969,6 @@ llvm::Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
}

ConstraintSystemOptions options = ConstraintSystemFlags::AllowFixes;
if (ClosuresInResultBuilderDontParticipateInInference) {
options |= ConstraintSystemFlags::
ClosuresInResultBuildersDontParticipateInInference;
}
auto resultInterfaceTy = func->getResultInterfaceType();
auto resultContextType = func->mapTypeIntoContext(resultInterfaceTy);

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSRanking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
// problems with restating requirements in protocols.
identical = false;

if (cs.Context.CompletionCallback) {
if (cs.isForCodeCompletion()) {
// Don't rank based on overload choices of function calls that contain the
// code completion token.
ASTNode anchor = simplifyLocatorToAnchor(overload.locator);
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSStep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ StepResult ConjunctionStep::resume(bool prevFailed) {

if (Solutions.size() == 1) {
auto score = Solutions.front().getFixedScore();
if (score.Data[SK_Fix] > 0 && !CS.getASTContext().CompletionCallback)
if (score.Data[SK_Fix] > 0 && !CS.isForCodeCompletion())
Producer.markExhausted();
}
} else if (Solutions.size() != 1) {
Expand Down
13 changes: 1 addition & 12 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7311,24 +7311,13 @@ bool ConstraintSystem::participatesInInference(ClosureExpr *closure) const {
if (getAppliedResultBuilderTransform(closure))
return true;

if (closure->hasSingleExpressionBody())
return true;

if (Options.contains(ConstraintSystemFlags::LeaveClosureBodyUnchecked))
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think if we're keeping the LeaveClosureBodyUnchecked flag, then we'll probably want to restore the hasSingleExpressionBody check since I assume LeaveClosureBodyUnchecked is only meant to apply to multi-statement closures?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be right. But things are looking good right now even without the check. Let’s see what the stress tester has to say

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appears to be fine even with closure->hasSingleExpressionBody removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think hasSingleExpressionBody is necessary and you can remove isInResultBuilderContext method too since this was the last use of it. The only question I have is why do we have this setup in a way that getAppliedResultBuilderTransform doesn't respect LeaveClosureBodyUnchecked...

I think this method should look as follows:

return !(Options.contains(ConstraintSystemFlags::LeaveClosureBodyUnchecked) || closure->hasEmptyBody());

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't getAppliedResultBuilderTransform needed because we still generate a result builder body that needs type-checking even if the parsed body is empty?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I think we should add an method to get a body for a closure and use the result to check whether it's empty or not, seems like that would minimize the risk of making mistakes like that...


if (closure->hasEmptyBody())
return false;

// If body is nested in a parent that has a function builder applied,
// let's prevent inference until result builders.
if (Options.contains(
ConstraintSystemFlags::
ClosuresInResultBuildersDontParticipateInInference)) {
return !isInResultBuilderContext(closure);
} else {
return true;
}
return true;
}

TypeVarBindingProducer::TypeVarBindingProducer(BindingSet &bindings)
Expand Down
45 changes: 0 additions & 45 deletions lib/Sema/TypeCheckCodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,51 +57,6 @@
using namespace swift;
using namespace constraints;

/// Find the declaration directly referenced by this expression.
static std::pair<ValueDecl *, FunctionRefKind>
findReferencedDecl(Expr *expr, DeclNameLoc &loc) {
do {
expr = expr->getSemanticsProvidingExpr();

if (auto ice = dyn_cast<ImplicitConversionExpr>(expr)) {
expr = ice->getSubExpr();
continue;
}

if (auto dre = dyn_cast<DeclRefExpr>(expr)) {
loc = dre->getNameLoc();
return { dre->getDecl(), dre->getFunctionRefKind() };
}

return { nullptr, FunctionRefKind::Unapplied };
} while (true);
}

// Check if \p E is a call expression to curried thunk of "KeyPath as function".
// i.e. '{ `$kp$` in { $0[keyPath: $kp$] } }(keypath)'
static bool isKeyPathCurriedThunkCallExpr(Expr *E) {
auto CE = dyn_cast<CallExpr>(E);
if (!CE)
return false;
auto thunk = dyn_cast<AutoClosureExpr>(CE->getFn());
if (!thunk)
return false;
if (thunk->getParameters()->size() != 1 ||
thunk->getParameters()->get(0)->getParameterName().str() != "$kp$")
return false;

auto *unaryArg = CE->getArgs()->getUnlabeledUnaryExpr();
if (!unaryArg)
return false;
return isa<KeyPathExpr>(unaryArg);
}

// Extract the keypath expression from the curried thunk expression.
static Expr *extractKeyPathFromCurryThunkCall(Expr *E) {
assert(isKeyPathCurriedThunkCallExpr(E));
return cast<CallExpr>(E)->getArgs()->getUnlabeledUnaryExpr();
}

static Type
getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
ConcreteDeclRef &referencedDecl,
Expand Down
30 changes: 5 additions & 25 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2605,11 +2605,8 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
if (auto *func = dyn_cast<FuncDecl>(DC)) {
if (Type builderType = getResultBuilderType(func)) {
if (func->getBody()) {
auto optBody = TypeChecker::applyResultBuilderBodyTransform(
func, builderType,
/*ClosuresInResultBuilderDontParticipateInInference=*/
ctx.CompletionCallback == nullptr &&
ctx.SolutionCallback == nullptr);
auto optBody =
TypeChecker::applyResultBuilderBodyTransform(func, builderType);
if ((ctx.CompletionCallback && ctx.CompletionCallback->gotCallback()) ||
(ctx.SolutionCallback && ctx.SolutionCallback->gotCallback())) {
// We already informed the completion callback of solutions found by
Expand All @@ -2632,31 +2629,14 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(
}
}

// The enclosing closure might be a single expression closure or a function
// builder closure. In such cases, the body elements are type checked with
// the closure itself. So we need to try type checking the enclosing closure
// signature first unless it has already been type checked.
// If the context is a closure, type check the entire surrounding closure.
// Conjunction constraints ensure that statements unrelated to the one that
// contains the code completion token are not type checked.
if (auto CE = dyn_cast<ClosureExpr>(DC)) {
if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) {
swift::typeCheckASTNodeAtLoc(
TypeCheckASTNodeAtLocContext::declContext(CE->getParent()),
CE->getLoc());
// We need the actor isolation of the closure to be set so that we can
// annotate results that are on the same global actor.
// Since we are evaluating TypeCheckASTNodeAtLocRequest for every closure
// from outermost to innermost, we don't want to call checkActorIsolation,
// because that would cause actor isolation to be checked multiple times
// for nested closures. Instead, call determineClosureActorIsolation
// directly and set the closure's actor isolation manually. We can
// guarantee of that the actor isolation of enclosing closures have their
// isolation checked before nested ones are being checked by the way
// TypeCheckASTNodeAtLocRequest is called multiple times, as described
// above.
auto ActorIsolation = determineClosureActorIsolation(
CE, __Expr_getType, __AbstractClosureExpr_getActorIsolation);
CE->setActorIsolation(ActorIsolation);
// Type checking the parent closure also type checked this node.
// Nothing to do anymore.
return false;
}
}
Expand Down
5 changes: 2 additions & 3 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,8 @@ void typeCheckASTNode(ASTNode &node, DeclContext *DC,
/// e.g., because of a \c return statement. Otherwise, returns either the
/// fully type-checked body of the function (on success) or a \c nullptr
/// value if an error occurred while type checking the transformed body.
llvm::Optional<BraceStmt *> applyResultBuilderBodyTransform(
FuncDecl *func, Type builderType,
bool ClosuresInResultBuilderDontParticipateInInference = false);
llvm::Optional<BraceStmt *> applyResultBuilderBodyTransform(FuncDecl *func,
Type builderType);

/// Find the return statements within the body of the given function.
std::vector<ReturnStmt *> findReturnStatements(AnyFunctionRef fn);
Expand Down