Skip to content

[Diagnostics] Adding assignments directly to CS and diagnosing from there. #18950

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 3 commits into from
Aug 26, 2018

Conversation

gregomni
Copy link
Contributor

@gregomni gregomni commented Aug 24, 2018

Always CSGen assignment statements by making a typeVar, binding the dest to @lvalue-typeVar and binding the source to convertible-to-typeVar, and then let the CS figure things out, rather than the previous implementation, which was trying to eagerly figure out types. This makes most assignment failures now TreatRValueAsLValue FixConstraints, which can be figured out via the new diagnostics system and in some cases produce better, more specific errors.

WIP:

  • Assignments via key path diagnoses are more specific, but are right now misleadingly so in some cases: need to add a specific non-writable keyPath diagnostic.
  • Recomputing assignment dest type in CSApply should be unnecessary now, need to rip that out.
  • Bunch of assignment-related code in old CSDiag is now dead, but not all of it, need to figure out how much I can rip out.

@gregomni
Copy link
Contributor Author

@xedin This is the stuff I'm working on mentioned in my comment in #18948

Perhaps especially of interest, see previouslyDiagnosed added to CSFix here: in several cases LValue fixes happen because of some other fix in the assignment source or destination, and at least for this sort of fix, if a subexpr has already diagnosed something, further diagnosis up at the assignment expr level is unhelpful.

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni
Copy link
Contributor Author

Squashed them merged. I had been working from a branch still passing solution to ConstraintFix diagnose() so lots of conflicts, but simple ones.

@swift-ci Please smoke test.


// Compute the type to which the source must be converted to allow
// assignment to the destination.
auto destTy = CS.computeAssignDestType(expr->getDest(), expr->getLoc());
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens to computeAssignDestType()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After I kill the other caller in CSApply, which shouldn't need it any more because any conversion restrictions should already be in the CS, then it gets deleted.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sweet

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

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

Overall this makes sense to me, thank you!

But previousDiagnosed needs some more thought, I'd very much like to keep diagnostic interface free from flags like that. I think logic like that should live in constraint system - recordFixes should identify situations where paling up fixes is not ideal and not record them just like you mentioned.

@gregomni
Copy link
Contributor Author

@xedin Well, I still want the fix to happen, because I want the solution to be reached rather than the system be unresolved. It's just the diagnosis that should be omitted. Another possible alternative for my use would be some sort of bool hasFixInExpr(Expr *expr) that walks the subexpr tree under a given expression to see if other fixes have been done. I could go in that direction instead.

@xedin
Copy link
Contributor

xedin commented Aug 24, 2018

Solver can go on even without recording a fix if it's redundant (like we do right now if the locators are the same), it's just a matter of what recordFixes returns.

@xedin
Copy link
Contributor

xedin commented Aug 24, 2018

I think it makes sense to either add that logic to recordFixes or applySolutionFixes as you mentioned.

@gregomni
Copy link
Contributor Author

Ah, that's true. Can you think of a good name for a function like bool shouldRecordFixIfAlreadyExistingFixInSubExpr(FixKind kind) that would be callable from recordFixes?

@xedin
Copy link
Contributor

xedin commented Aug 24, 2018

Maybe something like bool supersedesExistingFixes(ConstraintFix *fix) and record only if it does?

@gregomni
Copy link
Contributor Author

That's a much better name, but supersedes implies being able to discard the existing one(s). augmentsExistingFixes?

@xedin
Copy link
Contributor

xedin commented Aug 24, 2018

yeah, probably augments or complements or similar is better :) Sorry, I'm not great at languages...

@xedin
Copy link
Contributor

xedin commented Aug 24, 2018

Or maybe even as simple as bool isUsefulFix(ConstraintFix *) because how it determines usefulness is implementation detail...

More specific diagnoses for assigning to read-only keypaths.
'computeAssignDestType' is dead code now.
ConstraintFix shouldRecordFix()
@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test

@gregomni gregomni changed the title WIP [Diagnostics] Adding assignments directly to CS and diagnosing from there. [Diagnostics] Adding assignments directly to CS and diagnosing from there. Aug 25, 2018
@gregomni
Copy link
Contributor Author

gregomni commented Aug 25, 2018

Did the bits in "WIP" in the first comment above, and decided to go with shouldRecordFix() for the name of the FixConstraint method. Think this is good to go.

Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

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

I think once we get the story straight with shouldRecordFix, it's indeed going to be good to go!

@@ -44,6 +44,18 @@ void ConstraintFix::print(llvm::raw_ostream &Out) const {

void ConstraintFix::dump() const {print(llvm::errs()); }

bool ConstraintFix::constraintSystemContainsFixInExpr(Expr *expr) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can just fold this into new TreatRValueAsLValue::shouldRecordFix overload you've added, since it's only used there anyway, it could be extracted later if needed...

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually on the second thought there is a kind attached to each fix, I think this could be moved to recordFix because it's kind of weird that fix itself is trying to determine if it should be recorded or not. I think it shouldn't even worry about it, that's what constraint system is for...

@@ -689,6 +689,17 @@ resolveImmutableBase(Expr *expr, ConstraintSystem &CS) {
return { expr, member };
}

if (auto tupleExpr = dyn_cast<TupleExpr>(SE->getIndex())) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there no better way to check this, @jckarter ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Have SubscriptDecls been sufficiently parameter-list-ified that we can look at the argument labels and parameter list instead? cc @slavapestov

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, but here we're looking at the arguments to the call, not the subscript declaration.

return TupleType::get(destTupleTypes, CS.getASTContext());
} else {
Type destTy = CS.createTypeVariable(CS.getConstraintLocator(expr));
CS.addConstraint(ConstraintKind::Bind, CS.getType(expr), LValueType::get(destTy),
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit - I think this should be reversed - LValueType::get(destType) bind CS.getType(expr) because we'd rather always bind to something we are going to return (which also happens to be a type variable always).


if (type2->is<LValueType>() && !isTypeVarOrMember1) {
if (attemptFixes && type2->is<LValueType>() && !isTypeVarOrMember1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice catch!

@gregomni
Copy link
Contributor Author

@xedin Is this more like what you were thinking for recordFix?

Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

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

Yes! I think this makes more sense.

@@ -4986,6 +4987,15 @@ ConstraintSystem::simplifyRestrictedConstraint(
llvm_unreachable("Unhandled SolutionKind in switch.");
}

static bool isAugmentingFix(ConstraintFix *fix) {
switch (fix->getKind()) {
case swift::constraints::FixKind::TreatRValueAsLValue:
Copy link
Contributor

Choose a reason for hiding this comment

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

Does it really need to be qualified completely like that here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope. I didn't notice the overly helpful autocomplete.

@xedin
Copy link
Contributor

xedin commented Aug 25, 2018

@swift-ci please test source compatibility

@xedin
Copy link
Contributor

xedin commented Aug 25, 2018

@swift-ci please test

@xedin
Copy link
Contributor

xedin commented Aug 25, 2018

If the tests are green, this should be good to go.

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 17f3c0d0e7d7be1bd9d8c34c902fb8cfaf48154c

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 17f3c0d0e7d7be1bd9d8c34c902fb8cfaf48154c

@gregomni
Copy link
Contributor Author

@swift-ci Please smoke test.

@gregomni gregomni merged commit c95cfc6 into swiftlang:master Aug 26, 2018
@@ -689,6 +689,17 @@ resolveImmutableBase(Expr *expr, ConstraintSystem &CS) {
return { expr, member };
}

if (auto tupleExpr = dyn_cast<TupleExpr>(SE->getIndex())) {
if (tupleExpr->getNumElements() == 1 && tupleExpr->getElementName(0).str() == "keyPath") {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would at least compare Identifiers and not strings, so tupleExpr->getElementName(0) == ctx.getIdentifier("keyPath").

However I imagine this check ("is this a subscript that's really a keypath application") must appear elsewhere in the compiler. Can we extract it into a common method?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants