Skip to content

Fix SR-11159, better checking for enum elements with the same head and different labels #26741

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

Closed
wants to merge 1 commit into from

Conversation

AndrewLitteken
Copy link
Contributor

This is a fix to SR-11159 which was a bug where having two enum elements with different named arguments could cause the compiler to crash in a switch statement. I do my best to follow the following swift evolution proposal . There are still a few errors surrounding error messages that I would help from if possible.

Resolves SR-11159.

@AndrewLitteken AndrewLitteken changed the title SR-11159 Fix SR-11159, better checking for enum elements with the same head and different labels Aug 20, 2019
@AndrewLitteken
Copy link
Contributor Author

@swift-ci Please smoke test

Copy link
Collaborator

@theblixguy theblixguy left a comment

Choose a reason for hiding this comment

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

I only had a very quick look, but here are some initial comments.

(btw, welcome to the project! 🎉)

ERROR(enum_element_pattern_assoc_values_using_labels,none,
"using label to match to enum, enum element pattern cannot match against mixed labels and variable patterns.",())
ERROR(enum_element_pattern_assoc_values_using_var_patterns,none,
"using variable patterns to match against enum, enum element pattern cannot match against mixed labels and variable patterns.",())
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, what's a "variable pattern"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the proposal when doing an case statement you can't mix labels and variables. So in (a: _, let y) the variable patterns is let y. There's also something added in the Identifier to flag that it came from a variable pattern. I don't think that this method is the best position though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Still, I don't think we call them "variable patterns" in user-facing diagnostics.

@@ -1442,12 +1443,14 @@ bool DeclContext::lookupQualified(Type type,
assert(decls.empty() && "additive lookup not supported");

// Handle AnyObject lookup.
if (type->isAnyObject())
if (type->isAnyObject()){
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you remove these changes? There are a lot of them and are unrelated to your fix and makes the PR really long.

I would also suggest running git-clang-format to format your changes.

continue;
}
bool addCandidate = true;
if(queryParams.size() == 1){
Copy link
Collaborator

Choose a reason for hiding this comment

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

This condition would always be true, right? Since we continue above if the size is not equal to 1.

Copy link
Contributor Author

@AndrewLitteken AndrewLitteken Aug 20, 2019

Choose a reason for hiding this comment

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

It isn't always true because it will continue only if the sizes are not equal and the size is not equal to 1. If both the case and the enum element are the same it should not continue. It should be ((enumElementParams.size() != queryParams.size()) && (queryParams.size() != 1)) for clarity.

bool enumHasLabels = false;
for(size_t i = 0;i<enumElementParams.size();++i)
enumHasLabels = enumHasLabels || !enumElementParams[i].empty();
if(foundElements.size() == 1 && !enumHasLabels){
Copy link
Collaborator

@theblixguy theblixguy Aug 20, 2019

Choose a reason for hiding this comment

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

Hmm, if you only care about a single result, you can do foundElements .getSingleTypeResult() (will return nullptr if size() != 1). Perhaps you can lift this check outside the for loop in that case?

@CodaFi
Copy link
Contributor

CodaFi commented Aug 20, 2019

I think this patch is trying to do a few separate things. I'd like to review the DeclName patch to the space engine in a separate PR, if you could split it off.

@AndrewLitteken
Copy link
Contributor Author

The SpaceEngine changes are to take care of exhaustivness/redundancy errors that pop up when the changes is added to the Enum typechecking. Should I try to resolve those issues just within the Enum checking?

@CodaFi
Copy link
Contributor

CodaFi commented Aug 20, 2019

Yes. That change is easiest to take at this stage.

@AndrewLitteken
Copy link
Contributor Author

SpaceEngine patch is now in #26754

@AndrewLitteken
Copy link
Contributor Author

I've made a few changes to this after my last pull request to try to clean this up a bit and minimize changes. It currently passes both the standard and validation tests.

@CodaFi
Copy link
Contributor

CodaFi commented Oct 17, 2019

The general approach looks good to me so far. I'm going to spend a little more time staring at the matching algorithm (it was the hardest part of this when I came along and tried to do it a while ago). In the mean time, could you please write a bunch of tests!

@CodaFi
Copy link
Contributor

CodaFi commented Oct 21, 2019

Here's a good one to add as a test https://bugs.swift.org/browse/SR-11159

@AndrewLitteken
Copy link
Contributor Author

Added a few tests. I haven't added anything concerning warnings and exhaustiveness. There are some issues checking for this when the labels are created from the variable declaration in the constructor. It seems like that could be broken into its own issue.

@AndrewLitteken
Copy link
Contributor Author

I was wondering what the current status of this pull request is?

@CodaFi
Copy link
Contributor

CodaFi commented Jan 24, 2020

Give it a rebase and let’s see about getting it merged.

… value, adding fix for if enum element has no var/let pattern but has associated values

adding additional file

removing print statement

addition to identifier to identify from let or var, used to match against enum cases to avoid ambiguities

removing optimization for exhaustiveness checking to work properly

adding new functionality, removing debug statements

removing stray iostream includes, enforcing 80 column width

formatting corrections

Update TypeCheckSwitchStmt.cpp

committing column widths for TypeCheckPattern.cpp

removing some sublime place brackets and extra lines

Update TypeCheckStmt.cpp

adding range based for loop strategies

removing SpaceEngine changes to be put in different pull request

fixing DeclName and Identifier problems

fixing error where a label with no let/var pattern would not show error with wrong label, and so finding enum element if only one member with same head found

Update TypeCheckStmt.cpp

cleaning up code

fixing full enum description being included and correct warnings

clang format changes

cleaning up code, Optional Bools, and removing unused code

fix small bug, add fixmes and tests

correcting changes from rebase

clang format

fixing tests
@AndrewLitteken
Copy link
Contributor Author

Rebase should be complete, and passes the basic build script tests

@theblixguy
Copy link
Collaborator

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 5415e94

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 5415e94

@theblixguy
Copy link
Collaborator

Hmm, maybe something broke:

/Users/buildnode/jenkins/workspace/swift-PR-osx/branch-master/swift/stdlib/public/core/StringObject.swift:95:16: error: cannot convert value of type 'StringObject.Variant' to expected argument type 'Optional<>'
08:18:33 if case .immortal = self { return true }


// Do not crash
enum Foo1
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Please match stdlib style in here.

@@ -557,6 +559,8 @@ namespace {
PAIRCASE (SpaceKind::Constructor, SpaceKind::Constructor): {
// Optimization: If the heads of the constructors don't match then
// the two are disjoint and their difference is the first space.

// FIXME: Exhuastiveness checking for labelled Enums
Copy link
Contributor

Choose a reason for hiding this comment

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

Hold on: is this still not implemented properly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has to do with extraneous warnings when there are labelled enums with the same base name. There wasn't a good solution developed in the last pull request, and it was sort of out of the scope of the initial bug report, I thought it could be fixed in a different pull request.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, OK. That's usually called "redundancy checking". Had a small heart attack at the thought we had regressed the exhaustiveness checker.

.diagnose(EEP->getLoc(),
diag::enum_element_pattern_assoc_values_remove)
.fixItRemove(sub->getSourceRange());
;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: stray semicolon

@@ -1286,22 +1432,20 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern,
// isn't a static VarDecl, so the existing mechanics in
// extractEnumElement won't work.
if (type->getAnyNominal() == Context.getOptionalDecl()) {
if (EEP->getName().isSimpleName("None") ||
EEP->getName().isSimpleName("Some")) {
if (EEP->getName().getBaseIdentifier().str() == "None" ||
Copy link
Contributor

Choose a reason for hiding this comment

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

You shouldn't have to touch this code. The guard above checks if the nominal type we're dealing with is precisely Swift.Optional<T>

Copy link
Contributor

Choose a reason for hiding this comment

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

If anything we oughta just get rid of it. These are Swift 2-3 migration guides.

return new (Context) EnumElementPattern(
ume->getDotLoc(), ume->getNameLoc(),
DeclNameRef(
DeclName(Context, ume->getName().getBaseName(), namedParams)),
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: DeclName::withArgumentLabels is a touch cleaner here.

ParentType(ParentType), DotLoc(DotLoc), NameLoc(NameLoc), Name(Name),
ElementDeclOrUnresolvedOriginalExpr(Element),
SubPattern(SubPattern) {
: Pattern(PatternKind::EnumElement), ParentType(ParentType),
Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing seems to have changed in this file except whitespace. Can you revert this out?

params.push_back(Identifier());
else
// Set None if not using labels, throw error if contradicts
if (usingLabels == None)
Copy link
Contributor

Choose a reason for hiding this comment

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

This if-else tree and the one below it absolutely positively need explicit braces. I can't track anything here.

@@ -4731,6 +4731,10 @@ ERROR(empty_switch_stmt,none,
"'switch' statement body must have at least one 'case' or 'default' "
"block; do you want to add a default case?",())
ERROR(non_exhaustive_switch,none, "switch must be exhaustive", ())
ERROR(enum_element_pattern_assoc_values_using_labels,none,
"using label to match to enum, enum element pattern cannot match against mixed labels and variable patterns.",())
Copy link
Contributor

Choose a reason for hiding this comment

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

How about:

pattern matching case '%0' must specify all labels if it specifies any labels

and a nice note that rewrites things?

ERROR(enum_element_pattern_assoc_values_using_labels,none,
"using label to match to enum, enum element pattern cannot match against mixed labels and variable patterns.",())
ERROR(enum_element_pattern_assoc_values_using_var_patterns,none,
"using variable patterns to match against enum, enum element pattern cannot match against mixed labels and variable patterns.",())
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing here as above.

Copy link
Contributor

@CodaFi CodaFi left a comment

Choose a reason for hiding this comment

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

Left behind a bunch of nits. I have a high-level comment as well which is:
If the general idea is to rewrite patterns to have the appropriate label set, the only correct way to do that is to query the Type itself for that information and rewrite.

This patch also needs to make affordances for other language modes and provide compatibility warnings and notes to help fix these errors.

@AndrewLitteken
Copy link
Contributor Author

AndrewLitteken commented Jan 31, 2020

I think I may need to take a step back and try to go about this a different way. The fix is pretty different from the initial suggestion in SR-11159 and needs to solve a bigger problem than I thought it was initially (essentially adding Proposal 0155 in).

@shahmishal
Copy link
Member

Please update the base branch to main by Oct 5th otherwise the pull request will be closed automatically.

  • How to change the base branch: (Link)
  • More detail about the branch update: (Link)

@shahmishal shahmishal closed this Oct 5, 2020
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