Skip to content
Draft
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
64 changes: 63 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,69 @@
> [!NOTE]
> This is in reverse chronological order, so newer entries are added to the top.

## Swift (next)
## Swift 6.3

* The checking for illegal forward references to local variables is now consistent regardless of
whether the reference appears in a closure. Previously the type-checker could incorrectly permit
forward references within a closure that it would reject outside of the closure, however this
is not something that can be supported in general in the type-checker. In most cases this should
have no impact since such invalid references would have already been rejected by later diagnostic
passes in the compiler. However there are a couple of cases that were previously legal that are
now illegal.

These include:

- `lazy` local variables with initializers that forward reference a local variable in a closure,
or local variables with attached macros that relocate the initializer into an accessor, e.g:

```swift
func foo() {
lazy var x = { y } // error: use of local variable 'y' before its declaration
let y = 0
}
```

```swift
func foo() {
@LazyLikeMacro var x = { y } // error: use of local variable 'y' before its declaration
let y = 0
}
```

- Forward references to local computed variables from a closure, e.g:

```swift
func foo() {
var x = { y } // error: use of local variable 'y' before its declaration
var y: Int { 0 }
}
```

Both cases were already invalid if there was no closure involved. These are now consistently
rejected.

This then allows for consistent shadowing behavior inside and outside of closures, allowing the
following previously illegal case to become legal:

```swift
struct S {
var x: Int
func foo() {
// Already legal, both refer to `self.x`
let y = x
let x = x

let z = x // Refers to the local var.
}
func bar() {
// Both previously illegal, now both refer to `self.x`.
let y = { x }()
let x: Int = { x }()

let z = x // Still refers to the local var.
}
}
```

* [SE-0491][]:
You can now use a module selector to specify which module Swift should look inside to find a named declaration. A
Expand Down
14 changes: 8 additions & 6 deletions include/swift/AST/ASTScope.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class ASTScopeImpl : public ASTAllocated<ASTScopeImpl> {
friend class GenericTypeOrExtensionWherePortion;
friend class GenericTypeOrExtensionWherePortion;
friend class IterableTypeBodyPortion;
friend class TopLevelCodeScope;
friend class ScopeCreator;
friend class ASTSourceFileScope;
friend class ABIAttributeScope;
Expand Down Expand Up @@ -245,7 +246,7 @@ class ASTScopeImpl : public ASTAllocated<ASTScopeImpl> {
#pragma mark - debugging and printing

public:
const SourceFile *getSourceFile() const;
SourceFile *getSourceFile() const;
std::string getClassName() const;

/// Print out this scope for debugging/reporting purposes.
Expand Down Expand Up @@ -1212,18 +1213,19 @@ class ClosureParametersScope final : public ASTScopeImpl {
class TopLevelCodeScope final : public ASTScopeImpl {
public:
TopLevelCodeDecl *const decl;
SourceLoc endLoc;
ASTScopeImpl *insertionPoint = nullptr;

TopLevelCodeScope(TopLevelCodeDecl *e, SourceLoc endLoc)
: ASTScopeImpl(ScopeKind::TopLevelCode), decl(e), endLoc(endLoc) {}
TopLevelCodeScope(TopLevelCodeDecl *e)
: ASTScopeImpl(ScopeKind::TopLevelCode), decl(e) {}
virtual ~TopLevelCodeScope() {}

protected:
ASTScopeImpl *expandSpecifically(ScopeCreator &scopeCreator) override;

NullablePtr<const ASTScopeImpl> getLookupParent() const override;

private:
AnnotatedInsertionPoint
expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &);
void expandAScopeThatDoesNotCreateANewInsertionPoint(ScopeCreator &);

public:
SourceRange
Expand Down
26 changes: 18 additions & 8 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,14 +520,15 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl>, public Swi
/// Whether this is the backing storage for a property wrapper.
IsPropertyWrapperBackingProperty : 1,

/// Whether this is a lazily top-level global variable from the main file.
IsTopLevelGlobal : 1,

/// Whether this variable has no attached property wrappers.
NoAttachedPropertyWrappers : 1,

/// Whether this variable has no property wrapper auxiliary variables.
NoPropertyWrapperAuxiliaryVariables : 1
NoPropertyWrapperAuxiliaryVariables : 1,

/// Whether this variable is a placeholder that is introducing during
/// type-checking that has its type inferred from its use.
IsPlaceholderVar : 1
);

SWIFT_INLINE_BITFIELD(ParamDecl, VarDecl, 1+2+NumDefaultArgumentKindBits,
Expand Down Expand Up @@ -2875,6 +2876,8 @@ class PatternBindingDecl final : public Decl,
/// global variables.
class TopLevelCodeDecl : public DeclContext, public Decl {
BraceStmt *Body;
std::optional<TopLevelCodeDecl *> Previous;

SourceLoc getLocFromSource() const { return getStartLoc(); }
friend class Decl;
public:
Expand All @@ -2886,6 +2889,8 @@ class TopLevelCodeDecl : public DeclContext, public Decl {
BraceStmt *getBody() const { return Body; }
void setBody(BraceStmt *b) { Body = b; }

TopLevelCodeDecl *getPrevious() const;

SourceLoc getStartLoc() const;
SourceRange getSourceRange() const;

Expand Down Expand Up @@ -6800,13 +6805,18 @@ class VarDecl : public AbstractStorageDecl {
/// \c nullptr.
VarDecl *getOriginalVarForBackingStorage() const;

/// Whether this variable is a placeholder that is introducing during
/// type-checking that has its type inferred from its use.
bool isPlaceholderVar() const {
return Bits.VarDecl.IsPlaceholderVar;
}
void setIsPlaceholderVar() {
Bits.VarDecl.IsPlaceholderVar = true;
}

/// Retrieve the backing storage property for a lazy property.
VarDecl *getLazyStorageProperty() const;

/// True if this is a top-level global variable from the main source file.
bool isTopLevelGlobal() const { return Bits.VarDecl.IsTopLevelGlobal; }
void setTopLevelGlobal(bool b) { Bits.VarDecl.IsTopLevelGlobal = b; }

/// True if this is any storage of static duration (global scope or static).
bool isGlobalStorage() const;

Expand Down
10 changes: 0 additions & 10 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -6203,8 +6203,6 @@ ERROR(global_actor_arg,none,
(Type))
ERROR(global_actor_non_final_class,none,
"non-final class %0 cannot be a global actor", (DeclName))
ERROR(global_actor_top_level_var,none,
"top-level code variables cannot have a global actor", ())
ERROR(global_actor_access,none,
"%select{private|fileprivate|internal|package|public|open}0 %kind1 "
"cannot have %select{private|fileprivate|internal|package|%error|%error}2 "
Expand Down Expand Up @@ -7216,14 +7214,6 @@ ERROR(availability_decl_no_unavailable, none,
"%kindonly0 cannot be marked unavailable with '@available'",
(const Decl *))

ERROR(availability_global_script_no_potential,
none, "global variable cannot be marked potentially "
"unavailable with '@available' in script mode", ())

ERROR(availability_global_script_no_unavailable,
none, "global variable cannot be marked unavailable "
"with '@available' in script mode", ())

ERROR(availability_stored_property_no_potential,
none, "stored properties cannot be marked potentially unavailable with "
"'@available'", ())
Expand Down
3 changes: 1 addition & 2 deletions include/swift/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1299,8 +1299,7 @@ class Parser {
StaticSpellingKind StaticSpelling,
SourceLoc VarLoc,
bool hasInitializer,
const DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls);
const DeclAttributes &Attributes);
ParserStatus parseGetEffectSpecifier(ParsedAccessors &accessors,
SourceLoc &asyncLoc,
SourceLoc &throwsLoc,
Expand Down
1 change: 0 additions & 1 deletion lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2620,7 +2620,6 @@ namespace {
printFlag(VD->isDebuggerVar(), "debugger_var", DeclModifierColor);
printFlag(VD->isLazyStorageProperty(), "lazy_storage_property",
DeclModifierColor);
printFlag(VD->isTopLevelGlobal(), "top_level_global", DeclModifierColor);
printFlag(VD->isLazyStorageProperty(), "lazy_storage_property",
DeclModifierColor);

Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ASTScope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ ASTContext &ASTScopeImpl::getASTContext() const {

#pragma mark getSourceFile

const SourceFile *ASTScopeImpl::getSourceFile() const {
SourceFile *ASTScopeImpl::getSourceFile() const {
if (auto sourceFileScope = dyn_cast<ASTSourceFileScope>(this))
return sourceFileScope->SF;

Expand Down
25 changes: 8 additions & 17 deletions lib/AST/ASTScopeCreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,7 @@ class NodeAdder
ASTScopeImpl *visitTopLevelCodeDecl(TopLevelCodeDecl *d,
ASTScopeImpl *p,
ScopeCreator &scopeCreator) {
ASTScopeAssert(endLoc.has_value(), "TopLevelCodeDecl in wrong place?");
return scopeCreator.constructExpandAndInsert<TopLevelCodeScope>(
p, d, *endLoc);
return scopeCreator.constructExpandAndInsert<TopLevelCodeScope>(p, d);
}

#pragma mark special-case creation
Expand Down Expand Up @@ -800,10 +798,10 @@ CREATES_NEW_INSERTION_POINT(GuardStmtScope)
CREATES_NEW_INSERTION_POINT(PatternEntryDeclScope)
CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope)
CREATES_NEW_INSERTION_POINT(BraceStmtScope)
CREATES_NEW_INSERTION_POINT(TopLevelCodeScope)
CREATES_NEW_INSERTION_POINT(ConditionalClausePatternUseScope)
CREATES_NEW_INSERTION_POINT(ABIAttributeScope)

NO_NEW_INSERTION_POINT(TopLevelCodeScope)
NO_NEW_INSERTION_POINT(FunctionBodyScope)
NO_NEW_INSERTION_POINT(AbstractFunctionDeclScope)
NO_NEW_INSERTION_POINT(CustomAttributeScope)
Expand Down Expand Up @@ -1017,19 +1015,6 @@ BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint(
"For top-level code decls, need the scope under, say a guard statement."};
}

AnnotatedInsertionPoint
TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &
scopeCreator) {

auto *body =
scopeCreator
.addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this, endLoc);

return {body, "So next top level code scope and put its decls in its body "
"under a guard statement scope (etc) from the last top level "
"code scope"};
}

AnnotatedInsertionPoint
ABIAttributeScope::expandAScopeThatCreatesANewInsertionPoint(
ScopeCreator &scopeCreator) {
Expand Down Expand Up @@ -1294,6 +1279,12 @@ void CustomAttributeScope::
}
}

void TopLevelCodeScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
ScopeCreator &scopeCreator) {
insertionPoint = scopeCreator.addToScopeTreeAndReturnInsertionPoint(decl->getBody(), this,
std::nullopt);
}

#pragma mark expandScope

ASTScopeImpl *GenericTypeOrExtensionWholePortion::expandScope(
Expand Down
14 changes: 14 additions & 0 deletions lib/AST/ASTScopeLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,20 @@ ConditionalClauseInitializerScope::getLookupParent() const {
return parent->getLookupParent();
}

NullablePtr<const ASTScopeImpl> TopLevelCodeScope::getLookupParent() const {
auto parent = getParent().get();
if (auto *prev = decl->getPrevious()) {
auto *fileScope = cast<ASTSourceFileScope>(parent);
auto prevScope = fileScope->findChildContaining(prev->getEndLoc(), getSourceManager());
if (auto *prevTopLevel = dyn_cast<TopLevelCodeScope>(prevScope.getPtrOrNull())) {
if (!prevTopLevel->getWasExpanded())
prevTopLevel->expandAndBeCurrent(const_cast<TopLevelCodeScope *>(this)->getScopeCreator());
return prevTopLevel->insertionPoint;
}
}
return parent;
}

#pragma mark looking in locals or members - locals

bool GenericParamScope::lookupLocalsOrMembers(DeclConsumer consumer) const {
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ASTScopeSourceRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ SourceRange FunctionBodyScope::getSourceRangeOfThisASTNode(

SourceRange TopLevelCodeScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return SourceRange(decl->getStartLoc(), endLoc);
return decl->getSourceRange();
}

SourceRange SubscriptDeclScope::getSourceRangeOfThisASTNode(
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2642,10 +2642,14 @@ class Verifier : public ASTWalker {
}

void verifyChecked(PatternBindingDecl *binding) {
auto isTopLevel = isa<TopLevelCodeDecl>(binding->getDeclContext());
// Look at all of the VarDecls being bound.
for (auto idx : range(binding->getNumPatternEntries()))
if (auto *P = binding->getPattern(idx))
P->forEachVariable([&](VarDecl *VD) {
auto varIsTopLevel = isa<TopLevelCodeDecl>(VD->getDeclContext());
ASSERT(isTopLevel == varIsTopLevel &&
"Must have consistent top-level context");
// ParamDecls never get PBD's.
assert(!isa<ParamDecl>(VD) && "ParamDecl has a PatternBindingDecl?");
});
Expand Down
1 change: 0 additions & 1 deletion lib/AST/Bridging/DeclBridging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ BridgedPatternBindingDecl BridgedPatternBindingDecl_createParsed(
VD->attachParsedAttrs(cAttrs.unbridged());
VD->setStatic(staticLoc.isValid());
VD->setIntroducer(introducer);
VD->setTopLevelGlobal(isa<TopLevelCodeDecl>(declContext));
});

entries.emplace_back(pattern, entry.equalLoc, entry.init.unbridged(),
Expand Down
Loading