Skip to content

Fix nested types in closure context and non-static operator crash #9377

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
May 7, 2017
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
3 changes: 3 additions & 0 deletions include/swift/AST/DeclContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
return ParentAndKind.getPointer();
}

/// Returns the semantic parent for purposes of name lookup.
DeclContext *getParentForLookup() const;

/// Return true if this is a child of the specified other decl context.
bool isChildContextOf(const DeclContext *Other) const {
if (this == Other) return false;
Expand Down
10 changes: 10 additions & 0 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,16 @@ Decl *DeclContext::getInnermostDeclarationDeclContext() {
return nullptr;
}

DeclContext *DeclContext::getParentForLookup() const {
if (isa<ProtocolDecl>(this) || isa<ExtensionDecl>(this)) {
// If we are inside a protocol or an extension, skip directly
// to the module scope context, without looking at any (invalid)
// outer types.
return getModuleScopeContext();
}
return getParent();
}

ModuleDecl *DeclContext::getParentModule() const {
const DeclContext *DC = this;
while (!DC->isModuleContext())
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
}
}

DC = DC->getParent();
DC = DC->getParentForLookup();
}

if (!isCascadingUse.hasValue())
Expand Down
37 changes: 13 additions & 24 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4733,35 +4733,24 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
// Check for static/final/class when we're in a type.
auto dc = FD->getDeclContext();
if (dc->isTypeContext()) {
// Within a class, operator functions must be 'static' or 'final'.
if (auto classDecl = dc->getAsClassOrClassExtensionContext()) {
// For a class, we also need the function or class to be 'final'.
if (!classDecl->isFinal() && !FD->isFinal() &&
FD->getStaticSpelling() != StaticSpellingKind::KeywordStatic) {
if (!FD->isStatic()) {
TC.diagnose(FD->getLoc(), diag::nonstatic_operator_in_type,
operatorName,
dc->getDeclaredInterfaceType())
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
"static ");

FD->setStatic();
} else {
TC.diagnose(FD->getLoc(), diag::nonfinal_operator_in_class,
operatorName, dc->getDeclaredInterfaceType())
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
"final ");
FD->getAttrs().add(new (TC.Context) FinalAttr(/*IsImplicit=*/true));
}
}
} else if (!FD->isStatic()) {
// Operator functions must be static.
TC.diagnose(FD, diag::nonstatic_operator_in_type,
if (!FD->isStatic()) {
TC.diagnose(FD->getLoc(), diag::nonstatic_operator_in_type,
operatorName,
dc->getDeclaredInterfaceType())
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
"static ");

FD->setStatic();
} else if (auto classDecl = dc->getAsClassOrClassExtensionContext()) {
// For a class, we also need the function or class to be 'final'.
if (!classDecl->isFinal() && !FD->isFinal() &&
FD->getStaticSpelling() != StaticSpellingKind::KeywordStatic) {
TC.diagnose(FD->getLoc(), diag::nonfinal_operator_in_class,
operatorName, dc->getDeclaredInterfaceType())
.fixItInsert(FD->getAttributeInsertionLoc(/*forModifier=*/true),
"final ");
FD->getAttrs().add(new (TC.Context) FinalAttr(/*IsImplicit=*/true));
}
}
} else if (!dc->isModuleScopeContext()) {
TC.diagnose(FD, diag::operator_in_local_scope);
Expand Down
43 changes: 7 additions & 36 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,33 +259,22 @@ findDeclContextForType(TypeChecker &TC,

auto ownerDC = typeDecl->getDeclContext();

// If the type is declared at the top level, there's nothing we can learn from
// walking our parent contexts.
if (ownerDC->isModuleScopeContext())
// If the type is not nested in another type, there's nothing we can
// learn from walking parent contexts.
if (!ownerDC->isTypeContext() ||
isa<GenericTypeParamDecl>(typeDecl))
return std::make_tuple(Type(), true);

// Workaround for issue where generic typealias generic parameters are
// looked up with the wrong 'fromDC'.
if (isa<TypeAliasDecl>(ownerDC)) {
assert(isa<GenericTypeParamDecl>(typeDecl));
return std::make_tuple(Type(), true);
}

bool needsBaseType = (ownerDC->isTypeContext() &&
!isa<GenericTypeParamDecl>(typeDecl));
NominalTypeDecl *ownerNominal =
ownerDC->getAsNominalTypeOrNominalTypeExtensionContext();

// We might have an invalid extension that didn't resolve.
//
// FIXME: How did UnqualifiedLookup find the decl then?
if (needsBaseType && ownerNominal == nullptr)
if (ownerNominal == nullptr)
return std::make_tuple(Type(), false);

auto getSelfType = [&](DeclContext *DC) -> Type {
if (!needsBaseType)
return Type();

// When looking up a nominal type declaration inside of a
// protocol extension, always use the nominal type and
// not the protocol 'Self' type.
Expand Down Expand Up @@ -330,18 +319,6 @@ findDeclContextForType(TypeChecker &TC,
}

// We're going to check the next parent context.

// FIXME: Horrible hack. Don't allow us to reference a generic parameter
// from a context outside a ProtocolDecl.
if (isa<ProtocolDecl>(parentDC) && isa<GenericTypeParamDecl>(typeDecl))
return std::make_tuple(Type(), false);
}

// If we didn't find the member in an immediate parent context and
// there is no base type, something went wrong.
if (!needsBaseType) {
assert(false && "Should have found non-type context by now");
return std::make_tuple(Type(), false);
}

// Now, search the supertypes or refined protocols of each parent
Expand Down Expand Up @@ -447,11 +424,6 @@ findDeclContextForType(TypeChecker &TC,
// If not, walk into the refined protocols, if any.
pushRefined(protoDecl);
}

// FIXME: Horrible hack. Don't allow us to reference a generic parameter
// or associated type from a context outside a ProtocolDecl.
if (isa<ProtocolDecl>(parentDC) && isa<AbstractTypeParamDecl>(typeDecl))
return std::make_tuple(Type(), false);
}

assert(false && "Should have found context by now");
Expand Down Expand Up @@ -943,7 +915,7 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc,
// Try ignoring access control.
DeclContext *lookupDC = dc;
if (options.contains(TR_GenericSignature))
lookupDC = dc->getParent();
lookupDC = dc->getParentForLookup();

NameLookupOptions relookupOptions = lookupOptions;
relookupOptions |= NameLookupFlags::KnownPrivate;
Expand Down Expand Up @@ -1232,8 +1204,7 @@ resolveTopLevelIdentTypeComponent(TypeChecker &TC, DeclContext *DC,
if (!DC->isCascadingContextForLookup(/*excludeFunctions*/false))
options |= TR_KnownNonCascadingDependency;

// The remaining lookups will be in the parent context.
lookupDC = DC->getParent();
lookupDC = DC->getParentForLookup();
}

// We need to be able to perform unqualified lookup into the given
Expand Down
19 changes: 19 additions & 0 deletions test/Constraints/tuple.swift
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,22 @@ var y = 0
let _ = (x, (y, 0))
takesRValue((x, (y, 0)))
takesAny((x, (y, 0)))

// SR-2600 - Closure cannot infer tuple parameter names
typealias Closure<A, B> = ((a: A, b: B)) -> String

func invoke<A, B>(a: A, b: B, _ closure: Closure<A,B>) {
print(closure((a, b)))
}

invoke(a: 1, b: "B") { $0.b }

invoke(a: 1, b: "B") { $0.1 }

invoke(a: 1, b: "B") { (c: (a: Int, b: String)) in
return c.b
}

invoke(a: 1, b: "B") { c in
return c.b
}
2 changes: 1 addition & 1 deletion test/decl/func/operator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class C0 {
}

class C1 {
final func %%%(lhs: C1, rhs: C1) -> C1 { return lhs }
final func %%%(lhs: C1, rhs: C1) -> C1 { return lhs } // expected-error{{operator '%%%' declared in type 'C1' must be 'static'}}{{3-3=static }}
}

final class C2 {
Expand Down
6 changes: 3 additions & 3 deletions test/decl/nested/protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ struct OuterGeneric<D> {
protocol InnerProtocol { // expected-error{{protocol 'InnerProtocol' cannot be nested inside another declaration}}
associatedtype Rooster
func flip(_ r: Rooster)
func flop(_ t: D)
func flop(_ t: D) // expected-error{{use of undeclared type 'D'}}
}
}

class OuterGenericClass<T> {
protocol InnerProtocol { // expected-error{{protocol 'InnerProtocol' cannot be nested inside another declaration}}
associatedtype Rooster
func flip(_ r: Rooster)
func flop(_ t: T)
func flop(_ t: T) // expected-error{{use of undeclared type 'T'}}
}
}

Expand All @@ -25,7 +25,7 @@ protocol OuterProtocol {
// expected-note@-1 {{did you mean 'InnerProtocol'?}}
associatedtype Rooster
func flip(_ r: Rooster)
func flop(_ h: Hen)
func flop(_ h: Hen) // expected-error{{use of undeclared type 'Hen'}}
}
}

Expand Down
17 changes: 17 additions & 0 deletions test/decl/nested/type_in_function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@
// Generic class locally defined in non-generic function (rdar://problem/20116710)
func f3() {
class B<T> {}

class C : B<Int> {}

_ = B<Int>()
_ = C()
}

// Type defined inside a closure (rdar://problem/31803589)
func hasAClosure() {
_ = {
enum E<T> { case a(T) }

let _ = E.a("hi")
let _ = E<String>.a("hi")
let _: E = .a("hi")
let _: E<String> = .a("hi")
}
}

protocol Racoon {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s

extension Integer {
#^A^#
extension {
var : Self
52 changes: 52 additions & 0 deletions validation-test/compiler_crashers_2_fixed/0094-rdar30689883.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RUN: not %target-swift-frontend %s -emit-ir

public struct Trie<Key: Hashable, Value> {

internal var root: TrieNode<Key, Value>

public init() {
self.root = TrieNode<Key, Value>()
}

public mutating func removeValue<C: Collection>(forCollection collection: C) -> Value? where C.Iterator.Element == Key {
return self.root.removeValue(forIterator: collection.makeIterator())
}

public mutating func updateValue<C: Collection>(_ value: Value, forCollection collection: C) -> Value? where C.Iterator.Element == Key {
return self.root.updateValue(value, forIterator: collection.makeIterator())
}

public func value<C: Collection>(forCollection collection: C) -> Value? where C.Iterator.Element == Key {
return self.root.value(forIterator: collection.makeIterator())
}
}

internal struct TrieNode<Key: Hashable, Value> {

internal let children: Dictionary<Key, TrieNode<Key, Value>>

internal let value: Value?

internal init(value: Value?=nil) {
self.children = Dictionary<Key, TrieNode<Key, Value>>()
self.value = value
}

internal mutating func removeValue<I: IteratorProtocol>(forIterator iterator: inout I) -> Value? where I.Element == Key {
return nil
}

internal mutating func updateValue<I: IteratorProtocol>(_ value: Value, forIterator iterator: inout I) -> Value? where I.Element == Key {
return nil
}

internal func value<I: IteratorProtocol>(forIterator iterator: inout I) -> Value? where I.Element == Key {
guard let key = iterator.next() else {
return value
}

return self.children[key]?.value(forIterator: iterator)
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
protocol P{class a}class C:P{protocol A:a
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
struct B{let f=a}protocol P{}extension P{extension{func&(U=
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
protocol P}extension P{lazy var f={extension{var f=((self
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
protocol P{
protocol
P{typealias a{}struct B{extension{protocol P{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
protocol P{typealias a
struct A{{}func a:a
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
protocol A{protocol A:A{class a{let c=a}typealias a
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

// RUN: not --crash %target-swift-frontend %s -emit-ir
// RUN: not %target-swift-frontend %s -emit-ir
protocol A{{}struct A{typealias a:Self
protocol P{extension{lazy var f=A.a