mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Allow instance properties and methods to be referenced from within a lazy property initializer, with or without explicit 'self.' qualification. The old behavior in Swift 3 was an incredible combination of odd quirks: - If the lazy property had an explicitly-written type, it was possible to reference instance members from the initializer expression by explicitly prefixing 'self.'. - However, if the lazy property type is inferred, it would first be type checked in the initializer context, which has no 'self' available. - Unqualified references to instance members did not work at all, because name lookup thought the "location" of the lookup was outside of the body of the getter. - Unqualified references to static properties worked, however unqualified references to static methods did not, and produced a bogus diagnostic, because one part of the name lookup code thought that initializers were "instance context" and another thought they were "static context". This patch improves on the old behavior with the following fixes: - Give PatternBindingInitializers associated with lazy properties an implicit 'self' declaration for use by name lookup. - In order to allow "re-parenting" the initializer after it has been type checked into the body of the getter, "steal" the initializer's 'self' when buiding the getter. - Fix up name lookup and make it aware of the implicit 'self' decl of a PatternBindingInitializer. This improves upon an earlier fix for this issue by Doug Gregor which only worked with ASTScope enabled; the new fix is more general and shares logic between the two name lookup implementations. Fixes <rdar://problem/16888679>, <https://bugs.swift.org/browse/SR-48>, <https://bugs.swift.org/browse/SR-2203>, <https://bugs.swift.org/browse/SR-4663>, and the countless other dupes of this issue.
213 lines
6.6 KiB
C++
213 lines
6.6 KiB
C++
//===--- Initializer.h - Initializer DeclContext ----------------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the Initializer class, which is a kind of
|
|
// DeclContext used for expressions that are not part of a normal
|
|
// code-evaluation context, such as a global initializer or a default
|
|
// argument.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_INITIALIZER_H
|
|
#define SWIFT_INITIALIZER_H
|
|
|
|
#include "swift/AST/DeclContext.h"
|
|
#include "swift/AST/Decl.h"
|
|
|
|
namespace swift {
|
|
class PatternBindingDecl;
|
|
|
|
enum class InitializerKind : uint8_t {
|
|
/// The initializer expression of a PatternBindingDecl that declares
|
|
/// a global variable or type member.
|
|
PatternBinding,
|
|
|
|
/// A function's default argument expression.
|
|
DefaultArgument,
|
|
};
|
|
|
|
/// An Initializer is a kind of DeclContext used for expressions that
|
|
/// aren't potentially evaluated as part of some function.
|
|
///
|
|
/// Generally, Initializers are created lazily, as most initializers
|
|
/// don't really require DeclContexts.
|
|
class Initializer : public DeclContext {
|
|
unsigned Kind : 1;
|
|
protected:
|
|
unsigned SpareBits : 31;
|
|
|
|
Initializer(InitializerKind kind, DeclContext *parent)
|
|
: DeclContext(DeclContextKind::Initializer, parent),
|
|
Kind(unsigned(kind)) {
|
|
}
|
|
|
|
// Expose this to subclasses.
|
|
using DeclContext::setParent;
|
|
|
|
public:
|
|
/// Returns the kind of initializer this is.
|
|
InitializerKind getInitializerKind() const {
|
|
return InitializerKind(Kind);
|
|
}
|
|
|
|
static bool classof(const DeclContext *DC) {
|
|
return DC->getContextKind() == DeclContextKind::Initializer;
|
|
}
|
|
static bool classof(const Initializer *I) { return true; }
|
|
};
|
|
|
|
/// The initializer expression of a non-local pattern binding
|
|
/// declaration, such as a field or global variable.
|
|
class PatternBindingInitializer : public Initializer {
|
|
PatternBindingDecl *Binding;
|
|
|
|
// created lazily for 'self' lookup from lazy property initializer
|
|
ParamDecl *SelfParam;
|
|
|
|
friend class ASTContext; // calls reset on unused contexts
|
|
|
|
void reset(DeclContext *parent) {
|
|
setParent(parent);
|
|
Binding = nullptr;
|
|
SelfParam = nullptr;
|
|
}
|
|
|
|
public:
|
|
explicit PatternBindingInitializer(DeclContext *parent)
|
|
: Initializer(InitializerKind::PatternBinding, parent),
|
|
Binding(nullptr), SelfParam(nullptr) {
|
|
SpareBits = 0;
|
|
}
|
|
|
|
|
|
void setBinding(PatternBindingDecl *binding, unsigned bindingIndex) {
|
|
setParent(binding->getDeclContext());
|
|
Binding = binding;
|
|
SpareBits = bindingIndex;
|
|
}
|
|
|
|
PatternBindingDecl *getBinding() const { return Binding; }
|
|
|
|
unsigned getBindingIndex() const { return SpareBits; }
|
|
|
|
ParamDecl *getImplicitSelfDecl();
|
|
|
|
static bool classof(const DeclContext *DC) {
|
|
if (auto init = dyn_cast<Initializer>(DC))
|
|
return classof(init);
|
|
return false;
|
|
}
|
|
static bool classof(const Initializer *I) {
|
|
return I->getInitializerKind() == InitializerKind::PatternBinding;
|
|
}
|
|
};
|
|
|
|
/// SerializedPatternBindingInitializer - This represents what was originally a
|
|
/// PatternBindingInitializer during serialization. It is preserved as a special
|
|
/// class only to maintain the correct AST structure and remangling after
|
|
/// deserialization.
|
|
class SerializedPatternBindingInitializer : public SerializedLocalDeclContext {
|
|
PatternBindingDecl *Binding;
|
|
|
|
public:
|
|
SerializedPatternBindingInitializer(PatternBindingDecl *Binding,
|
|
unsigned bindingIndex)
|
|
: SerializedLocalDeclContext(LocalDeclContextKind::PatternBindingInitializer,
|
|
Binding->getDeclContext()),
|
|
Binding(Binding) {
|
|
SpareBits = bindingIndex;
|
|
}
|
|
|
|
PatternBindingDecl *getBinding() const {
|
|
return Binding;
|
|
}
|
|
|
|
unsigned getBindingIndex() const { return SpareBits; }
|
|
|
|
|
|
static bool classof(const DeclContext *DC) {
|
|
if (auto LDC = dyn_cast<SerializedLocalDeclContext>(DC))
|
|
return LDC->getLocalDeclContextKind() ==
|
|
LocalDeclContextKind::PatternBindingInitializer;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// A default argument expression. The parent context is the function
|
|
/// (possibly a closure) for which this is a default argument.
|
|
class DefaultArgumentInitializer : public Initializer {
|
|
friend class ASTContext; // calls reset on unused contexts
|
|
void reset(DeclContext *parent, unsigned index) {
|
|
setParent(parent);
|
|
SpareBits = index;
|
|
}
|
|
|
|
public:
|
|
explicit DefaultArgumentInitializer(DeclContext *parent, unsigned index)
|
|
: Initializer(InitializerKind::DefaultArgument, parent) {
|
|
SpareBits = (unsigned(ResilienceExpansion::Maximal) | index << 1);
|
|
}
|
|
|
|
unsigned getIndex() const { return SpareBits >> 1; }
|
|
|
|
ResilienceExpansion getResilienceExpansion() const {
|
|
return ResilienceExpansion(SpareBits & 1);
|
|
}
|
|
|
|
/// Change the parent of this context. This is necessary because
|
|
/// the function signature is parsed before the function
|
|
/// declaration/expression itself is built.
|
|
void changeFunction(AbstractFunctionDecl *parent);
|
|
|
|
/// Change the resilience expansion of this context, necessary
|
|
/// for the same reason as above.
|
|
void changeResilienceExpansion(ResilienceExpansion expansion) {
|
|
SpareBits = (SpareBits & ~1) | unsigned(expansion);
|
|
}
|
|
|
|
static bool classof(const DeclContext *DC) {
|
|
if (auto init = dyn_cast<Initializer>(DC))
|
|
return classof(init);
|
|
return false;
|
|
}
|
|
static bool classof(const Initializer *I) {
|
|
return I->getInitializerKind() == InitializerKind::DefaultArgument;
|
|
}
|
|
};
|
|
|
|
/// SerializedDefaultArgumentInitializer - This represents what was originally a
|
|
/// DefaultArgumentInitializer during serialization. It is preserved only to
|
|
/// maintain the correct AST structure and remangling after deserialization.
|
|
class SerializedDefaultArgumentInitializer : public SerializedLocalDeclContext {
|
|
const unsigned Index;
|
|
public:
|
|
SerializedDefaultArgumentInitializer(unsigned Index, DeclContext *Parent)
|
|
: SerializedLocalDeclContext(LocalDeclContextKind::DefaultArgumentInitializer,
|
|
Parent),
|
|
Index(Index) {}
|
|
|
|
unsigned getIndex() const {
|
|
return Index;
|
|
}
|
|
|
|
static bool classof(const DeclContext *DC) {
|
|
if (auto LDC = dyn_cast<SerializedLocalDeclContext>(DC))
|
|
return LDC->getLocalDeclContextKind() ==
|
|
LocalDeclContextKind::DefaultArgumentInitializer;
|
|
return false;
|
|
}
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|