Skip to content

Conversation

@rintaro
Copy link
Member

@rintaro rintaro commented Oct 31, 2019

The first commit is purely bug fixes. They are revealed if the second commit is enabled. However they can be reproduced if the declaration is in the other files in the current module. (Added test/IDE/complete_multifile.swift regression test case).

  • Default memberwise initializer for struct was not suggested
  • Dynamic member lookup didn't work
  • init(rawValue:) was not suggested for RawRepresentable types (rdar://problem/56391233)
  • '$name' was not suggested for @propertyWrappered value

The second commit actually stops eager typechecking decls in the primary file.

  • Use performParseAndResolveImportsOnly() to invoke the frontend
  • Do bindExtensions() in ide::typeCheckContextUntil()
  • Typecheck preceding TopLevelCodeDecls only if the compleiton is in
    a TopLevelCodeDecl
  • Other related tweaks

rdar://problem/56636747

@rintaro rintaro force-pushed the ide-completion-rdar56636747 branch from 4ffb312 to 883cbd4 Compare October 31, 2019 00:30
Comment on lines 4102 to 4174
Copy link
Member Author

@rintaro rintaro Oct 31, 2019

Choose a reason for hiding this comment

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

Previously B.foo() is overwriteAccess(AccessLevel::Public)ed from diagnoseWitnessFixAccessLevel(). Since the typechecking is now in DiagnosticSuppression, diagnoseWitnessFixAccessLevel() is not called so we need to manually check it.

Copy link
Member Author

Choose a reason for hiding this comment

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

HasInitialValueAttr is injected in the typechecking.

Copy link
Member Author

@rintaro rintaro Oct 31, 2019

Choose a reason for hiding this comment

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

Moved after typeCheckContextUntil() because addKeywords() may call DeclContext::getSelfProtocolDecl() which can only be called after bindExtensions().

@rintaro
Copy link
Member Author

rintaro commented Oct 31, 2019

@swift-ci Please smoke test

@rintaro rintaro force-pushed the ide-completion-rdar56636747 branch 2 times, most recently from f96a0cd to 9f9298e Compare October 31, 2019 06:14
@rintaro
Copy link
Member Author

rintaro commented Oct 31, 2019

@swift-ci Please smoke test

@rintaro
Copy link
Member Author

rintaro commented Oct 31, 2019

@swift-ci Please test Linux

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 883cbd4005925c9adad35506e888c20bc6dc4ec3

@rintaro
Copy link
Member Author

rintaro commented Oct 31, 2019

@swift-ci Please test Linux

@rintaro rintaro force-pushed the ide-completion-rdar56636747 branch from 9f9298e to 0d6e0c8 Compare October 31, 2019 21:30
@rintaro
Copy link
Member Author

rintaro commented Oct 31, 2019

@swift-ci Please smoke test

Copy link
Contributor

Choose a reason for hiding this comment

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

How is this triggered during normal type-checking? This seems kind of brittle to live in lookup-visible-decls, although admittedly this whole file is basically like that...

Copy link
Member Author

@rintaro rintaro Nov 1, 2019

Choose a reason for hiding this comment

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

If the type is declared in the primary files, they are synthesized in TypeChecker::typeCheckDecl()(DeclChecker) or in typeCheckFunctionsAndExternalDecls().

But if it's in the non-primary files, AFAIU, they are lazily synthesized on demand in DeclContext::lookupQualified()
https://github.com/apple/swift/blob/bd971507bae5a12a2b40365e795799037ec59fcc/lib/AST/NameLookup.cpp#L1573-L1579
https://github.com/apple/swift/blob/bd971507bae5a12a2b40365e795799037ec59fcc/lib/Sema/TypeChecker.h#L1018-L1024

I can move this synthesizeMemberDeclsForLookup() function into TypeChecker::, but I don't think it's worth...

@rintaro
Copy link
Member Author

rintaro commented Nov 5, 2019

Resolved conflicts.
@swift-ci Please smoke test

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand. Can you give an example? Normally stored properties can be overridden with computed properties as long as they're not final.

Copy link
Member Author

@rintaro rintaro Nov 5, 2019

Choose a reason for hiding this comment

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

I believe this meant something like:

class C {
  class var foo = 1
}

But that's compile error, so we don't care much how they are treated in override completion. So maybe this check is not needed.
But here, I'm just changing D->getAttrs().hasAttribute<HasInitialValueAttr>() to isa<VarDecl>(D) && cast<VarDecl>(D)->hasInitialValue()

Copy link
Contributor

Choose a reason for hiding this comment

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

My point was that the presence of an initial value expression does not have any bearing on whether the declaration can be overridden or not. The comment above is incorrect.

I think the only relevant check here is whether the property is final (static is equivalent to class final).

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, I just removed this check.

Copy link
Contributor

Choose a reason for hiding this comment

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

The access of the requirement is the same as the access of the protocol, so I believe this entire loop and the search for the witness of 'VD' can go away.

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 mean like this?

    if (Access < AccessLevel::Public &&
        !isa<ProtocolDecl>(VD->getDeclContext())) {
      for (auto Conformance : CurrentNominal->getAllConformances()) {
        auto Proto = Conformance->getProtocol();
        Access = std::max(Access, Proto->getFormalAccess());
      }
    }

This may cause unnecessary public when the type conforms to two or more protocols. Like

public protocol P1 { }
protocol P2 {
  func bar()
}
public class B {
  func bar() {}
}
public class C: B, P1, P2 {
  <complete>
}

In this case when overriding bar(), C.bar() is not needed to be public.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe the getStdlibModule() and bindExtensions() calls should be done as part of performParseAndNameBindingOnly()?

Copy link
Member Author

Choose a reason for hiding this comment

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

Made bindExtensions() a part of performParseAndNameBindingOnly().

It turned out that getStdlibModule() is meaningless. Since ASTContext::getStdlibModule(bool loadIfAbsent = false) is called with default loadIfAbsent = false, it doesn't do anything. So I just removed it from performTypechecking()

Copy link
Contributor

Choose a reason for hiding this comment

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

You can replace this loop with calls to Conformance->forEachTypeWitness() and forEachValueWitness(), passing in useResolver=true.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can check if the IDC itself is a member of a source file outside the loop.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, done!

Copy link
Contributor

Choose a reason for hiding this comment

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

It's possible that an interface type has been set, but implicit constructors were not added (and vice versa). You can unconditinally call addImplicitConstructors() here; it's idempotent if called multiple times.

Copy link
Member Author

Choose a reason for hiding this comment

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

IIRC, that causes circular dependency error. I will try that again.

Copy link
Member Author

Choose a reason for hiding this comment

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

Here's the cyclic dependency error when enabling typo correction. How can we fix this?

$ cat test.swift
struct Broken {
  var b = True
}

$ swift -frontend -typecheck -typo-correction-limit 10 -debug-cycles test.swift
test.swift:1:8: error: circular reference
struct Broken {
       ^
test.swift:2:3: note: through reference here
  var b = True
  ^
test.swift:2:7: note: through reference here
  var b = True
      ^
test.swift:2:7: note: through reference here
  var b = True
      ^
===CYCLE DETECTED===
 `--SynthesizeMemberwiseInitRequest(test.(file)[email protected]:1:8)
     `--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
     `--StorageImplInfoRequest(test.(file)[email protected]:2:7)
     |   `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
     `--StorageImplInfoRequest(test.(file)[email protected]:2:7)
     |   `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
     `--AccessLevelRequest(test.(file)[email protected]:2:7)
     `--AccessLevelRequest(test.(file)[email protected]:2:7)
     `--InterfaceTypeRequest(test.(file)[email protected]:2:7)
         `--NamingPatternRequest(test.(file)[email protected]:2:7)
             `--PatternBindingEntryRequest((unknown decl), 0)
                 `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
 ...
                 `--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
                 `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 `--GenericSignatureRequest(test.(file)[email protected]:1:8)
                 |   `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 `--InterfaceTypeRequest(test.(file)[email protected]:1:8)
                 |   `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 |   `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 `--HasMemberwiseInitRequest(test.(file)[email protected]:1:8) -> 1
                 |   `--InterfaceTypeRequest(test.(file)[email protected]:1:8)
                 |   |   `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 |   |   `--GenericParamListRequest(test.(file)[email protected]:1:8)
                 |   `--HasUserDefinedDesignatedInitRequest(test.(file)[email protected]:1:8) -> 0
                 |   |   `--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
                 |   `--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
                 |   `--StorageImplInfoRequest(test.(file)[email protected]:2:7)
                 |   |   `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
                 |   `--StorageImplInfoRequest(test.(file)[email protected]:2:7)
                 |   |   `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
                 |   `--AccessLevelRequest(test.(file)[email protected]:2:7)
                 `--SynthesizeMemberwiseInitRequest(test.(file)[email protected]:1:8) (cyclic dependency)

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. It might be better then to check to explicitly check if an SynthesizeMemberwiseInitRequest is active, instead of using hasInterfaceType(), ie,

VD->getASTContext().evaluator.hasActiveRequest(
            SynthesizeMemberwiseInitRequest{...})

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do this all this inside lookupDeclsFromProtocolsBeingConformedTo() instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so because this has to be done before lookupTypeMembers() which is called before lookupDeclsFromProtocolsBeingConformedTo().

Copy link
Contributor

Choose a reason for hiding this comment

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

I might be cleaner to visit the extension's members in doGlobalExtensionLookup()

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@rintaro rintaro force-pushed the ide-completion-rdar56636747 branch 2 times, most recently from 024b1f6 to 113ea63 Compare November 6, 2019 21:03
@rintaro
Copy link
Member Author

rintaro commented Nov 6, 2019

@swift-ci Please smoke test

@rintaro rintaro requested a review from slavapestov November 6, 2019 22:30
Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

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

I don't have much to add to Slava and Ben's feedback, but I love the form of this. It's great to see us taking advantage of more laziness in the type checker.

Copy link
Contributor

Choose a reason for hiding this comment

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

I still think it's better to write Conformance->getProtocol()->getFormalAccess() here.

Copy link
Contributor

Choose a reason for hiding this comment

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

My point was that the presence of an initial value expression does not have any bearing on whether the declaration can be overridden or not. The comment above is incorrect.

I think the only relevant check here is whether the property is final (static is equivalent to class final).

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. It might be better then to check to explicitly check if an SynthesizeMemberwiseInitRequest is active, instead of using hasInterfaceType(), ie,

VD->getASTContext().evaluator.hasActiveRequest(
            SynthesizeMemberwiseInitRequest{...})

- Default memberwise initializer for struct was not suggested
- Dynamic member lookup didn't work
- `init(rawValue:)` was not suggested for `RawRepresentable` types
- '$name' was not suggested for `@propertyWrapper`ed value

rdar://problem/56391233
… file

- Use `performParseAndResolveImportsOnly()` to invoke the frontend
- Do `bindExtensions()` in `ide::typeCheckContextUntil()`
- Typecheck preceding `TopLevelCodeDecl`s only if the compleiton is in
  a `TopLevelCodeDecl`
- Other related tweaks

rdar://problem/56636747
@rintaro rintaro force-pushed the ide-completion-rdar56636747 branch from 113ea63 to 5c2b6a5 Compare November 12, 2019 07:38
@rintaro
Copy link
Member Author

rintaro commented Nov 12, 2019

@swift-ci Please smoke test

In override completion, we didn't use to emit 'class var' with the initial
expression. However, having initial expression does not have any bearing
on whether the declaration is overridable or not.
@rintaro rintaro force-pushed the ide-completion-rdar56636747 branch from 5c2b6a5 to 97b16ce Compare November 12, 2019 08:40
@rintaro
Copy link
Member Author

rintaro commented Nov 12, 2019

@swift-ci Please smoke test

@rintaro rintaro merged commit 71c382e into swiftlang:master Nov 12, 2019
@rintaro rintaro deleted the ide-completion-rdar56636747 branch November 12, 2019 12:43
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.

6 participants