Skip to content

Sema: Do not diagnose set accessor availability for InOutExprs inside of LoadExprs #72369

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
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
1 change: 0 additions & 1 deletion lib/Parse/ParseIfConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,6 @@ Result Parser::parseIfConfigRaw(
bool isActive, IfConfigElementsRole role)>
parseElements,
llvm::function_ref<Result(SourceLoc endLoc, bool hadMissingEnd)> finish) {
auto startLoc = Tok.getLoc();
assert(Tok.is(tok::pound_if));

Parser::StructureMarkerRAII ParsingDecl(
Expand Down
21 changes: 20 additions & 1 deletion lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3496,6 +3496,20 @@ class ExprAvailabilityWalker : public ASTWalker {
return call;
}

/// Walks up to the first enclosing LoadExpr and returns it.
const LoadExpr *getEnclosingLoadExpr() const {
assert(!ExprStack.empty() && "must be called while visiting an expression");
ArrayRef<const Expr *> stack = ExprStack;
stack = stack.drop_back();

for (auto expr : llvm::reverse(stack)) {
if (auto loadExpr = dyn_cast<LoadExpr>(expr))
return loadExpr;
}

return nullptr;
}

/// Walk an assignment expression, checking for availability.
void walkAssignExpr(AssignExpr *E) {
// We take over recursive walking of assignment expressions in order to
Expand Down Expand Up @@ -3575,7 +3589,12 @@ class ExprAvailabilityWalker : public ASTWalker {

/// Walk an inout expression, checking for availability.
void walkInOutExpr(InOutExpr *E) {
walkInContext(E, E->getSubExpr(), MemberAccessContext::InOut);
// If there is a LoadExpr in the stack, then this InOutExpr is not actually
// indicative of any mutation so the access context should just be Getter.
auto accessContext = getEnclosingLoadExpr() ? MemberAccessContext::Getter
: MemberAccessContext::InOut;

walkInContext(E, E->getSubExpr(), accessContext);
}

bool shouldWalkIntoClosure(AbstractClosureExpr *closure) const {
Expand Down
3 changes: 1 addition & 2 deletions stdlib/public/core/StringCreate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ internal func _allASCII(_ input: UnsafeBufferPointer<UInt8>) -> Bool {
//
let count = input.count
var ptr = UnsafeRawPointer(input.baseAddress._unsafelyUnwrappedUnchecked)
var i = 0

let asciiMask64 = 0x8080_8080_8080_8080 as UInt64
let asciiMask32 = UInt32(truncatingIfNeeded: asciiMask64)
Expand Down Expand Up @@ -63,7 +62,7 @@ internal func _allASCII(_ input: UnsafeBufferPointer<UInt8>) -> Bool {
}

if ptr < end {
let value = ptr.loadUnaligned(fromByteOffset: i, as: UInt8.self)
let value = ptr.loadUnaligned(fromByteOffset: 0, as: UInt8.self)
guard value & asciiMask8 == 0 else { return false }
}
_internalInvariant(ptr == end || ptr + 1 == end)
Expand Down
253 changes: 253 additions & 0 deletions test/Sema/availability_accessors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
// RUN: %target-typecheck-verify-swift -parse-as-library -module-name MyModule

struct Value {
static let defaultValue = Value(a: Nested(b: 1))

struct Nested {
var b: Int

@discardableResult
mutating func setToZero() -> Int {
let prev = b
b = 0
return prev
}
}

var a: Nested

subscript(_ i: Int) -> Nested {
get { a }
set { a = newValue }
}

@discardableResult
mutating func setToZero() -> Int {
let prev = a.b
a.setToZero()
return prev
}
}

struct BaseStruct {
var available: Value = .defaultValue

var unavailableGetter: Value {
@available(*, unavailable)
get { fatalError() } // expected-note 24 {{getter for 'unavailableGetter' has been explicitly marked unavailable here}}
set {}
}

var unavailableSetter: Value {
get { .defaultValue }
@available(*, unavailable)
set { fatalError() } // expected-note 12 {{setter for 'unavailableSetter' has been explicitly marked unavailable here}}
}

var unavailableGetterAndSetter: Value {
@available(*, unavailable)
get { fatalError() } // expected-note 24 {{getter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
@available(*, unavailable)
set { fatalError() } // expected-note 12 {{setter for 'unavailableGetterAndSetter' has been explicitly marked unavailable here}}
}
}

func takesIntInOut(_ i: inout Int) -> Int {
return 0
}

let someValue = Value.defaultValue

func testRValueLoads() {
var x = BaseStruct() // expected-warning {{variable 'x' was never mutated; consider changing to 'let' constant}}

_ = x.available
_ = x.available.a
_ = x.available[0]
_ = x.available[0].b

_ = x.unavailableGetter // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = x.unavailableGetter.a // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = x.unavailableGetter[0] // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = x.unavailableGetter[0].b // expected-error {{getter for 'unavailableGetter' is unavailable}}

_ = x.unavailableSetter
_ = x.unavailableSetter.a
_ = x.unavailableSetter[0]
_ = x.unavailableSetter[0].b

_ = x.unavailableGetterAndSetter // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
_ = x.unavailableGetterAndSetter.a // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
_ = x.unavailableGetterAndSetter[0] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
_ = x.unavailableGetterAndSetter[0].b // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
}

func testLValueAssignments() {
var x = BaseStruct()

x.available = someValue
x.available.a = someValue.a
x.available[0] = someValue.a
x.available[0].b = 1

x.unavailableGetter = someValue
x.unavailableGetter.a = someValue.a // expected-error {{getter for 'unavailableGetter' is unavailable}}
x.unavailableGetter[0] = someValue.a // expected-error {{getter for 'unavailableGetter' is unavailable}}
x.unavailableGetter[0].b = 1 // expected-error {{getter for 'unavailableGetter' is unavailable}}

x.unavailableSetter = someValue // expected-error {{setter for 'unavailableSetter' is unavailable}}
x.unavailableSetter.a = someValue.a // FIXME: missing diagnostic for setter
x.unavailableSetter[0] = someValue.a // expected-error {{setter for 'unavailableSetter' is unavailable}}
x.unavailableSetter[0].b = 1 // expected-error {{setter for 'unavailableSetter' is unavailable}}

x.unavailableGetterAndSetter = someValue // expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
x.unavailableGetterAndSetter.a = someValue.a // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} FIXME: missing diagnostic for setter
x.unavailableGetterAndSetter[0] = someValue.a // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
x.unavailableGetterAndSetter[0].b = 1 // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
}

func testKeyPathLoads() {
let a = [0]
var x = BaseStruct()

_ = x[keyPath: \.available]
_ = x[keyPath: \.available.a]
_ = x[keyPath: \.available[0]]
_ = x[keyPath: \.available[0].b]
_ = a[keyPath: \.[takesIntInOut(&x.available.a.b)]]
_ = a[keyPath: \.[takesIntInOut(&x.available[0].b)]]

_ = x[keyPath: \.unavailableGetter] // FIXME: missing diagnostic for getter
_ = x[keyPath: \.unavailableGetter.a] // FIXME: missing diagnostic for getter
_ = x[keyPath: \.unavailableGetter[0]] // FIXME: missing diagnostic for getter
_ = x[keyPath: \.unavailableGetter[0].b] // FIXME: missing diagnostic for getter
_ = a[keyPath: \.[takesIntInOut(&x.unavailableGetter.a.b)]] // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = a[keyPath: \.[takesIntInOut(&x.unavailableGetter[0].b)]] // expected-error {{getter for 'unavailableGetter' is unavailable}}

_ = x[keyPath: \.unavailableSetter]
_ = x[keyPath: \.unavailableSetter.a]
_ = x[keyPath: \.unavailableSetter[0]]
_ = x[keyPath: \.unavailableSetter[0].b]
_ = a[keyPath: \.[takesIntInOut(&x.unavailableSetter.a.b)]] // FIXME: missing diagnostic for setter
_ = a[keyPath: \.[takesIntInOut(&x.unavailableSetter[0].b)]] // expected-error {{setter for 'unavailableSetter' is unavailable}}

_ = x[keyPath: \.unavailableGetterAndSetter] // FIXME: missing diagnostic for getter
_ = x[keyPath: \.unavailableGetterAndSetter.a] // FIXME: missing diagnostic for getter
_ = x[keyPath: \.unavailableGetterAndSetter[0]] // FIXME: missing diagnostic for getter
_ = x[keyPath: \.unavailableGetterAndSetter[0].b] // FIXME: missing diagnostic for getter
_ = a[keyPath: \.[takesIntInOut(&x.unavailableGetterAndSetter.a.b)]] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} FIXME: missing diagnostic for setter
_ = a[keyPath: \.[takesIntInOut(&x.unavailableGetterAndSetter[0].b)]] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
}

func testKeyPathAssignments() {
var a = [0]
var x = BaseStruct()

x[keyPath: \.available] = someValue
x[keyPath: \.available.a] = someValue.a
x[keyPath: \.available[0]] = someValue.a
x[keyPath: \.available[0].b] = 1
x[keyPath: \.available] = someValue
a[keyPath: \.[takesIntInOut(&x.available.a.b)]] = 0
a[keyPath: \.[takesIntInOut(&x.available[0].b)]] = 0

x[keyPath: \.unavailableGetter] = someValue
x[keyPath: \.unavailableGetter.a] = someValue.a // FIXME: missing diagnostic for getter
x[keyPath: \.unavailableGetter[0]] = someValue.a // FIXME: missing diagnostic for getter
x[keyPath: \.unavailableGetter[0].b] = 1 // FIXME: missing diagnostic for getter
a[keyPath: \.[takesIntInOut(&x.unavailableGetter.a.b)]] = 0 // expected-error {{getter for 'unavailableGetter' is unavailable}}
a[keyPath: \.[takesIntInOut(&x.unavailableGetter[0].b)]] = 0 // expected-error {{getter for 'unavailableGetter' is unavailable}}

x[keyPath: \.unavailableSetter] = someValue
x[keyPath: \.unavailableSetter.a] = someValue.a // FIXME: missing diagnostic for setter
x[keyPath: \.unavailableSetter[0]] = someValue.a // FIXME: missing diagnostic for setter
x[keyPath: \.unavailableSetter[0].b] = 1 // FIXME: missing diagnostic for setter
a[keyPath: \.[takesIntInOut(&x.unavailableSetter.a.b)]] = 0 // FIXME: missing diagnostic for setter
a[keyPath: \.[takesIntInOut(&x.unavailableSetter[0].b)]] = 0 // expected-error {{setter for 'unavailableSetter' is unavailable}}

x[keyPath: \.unavailableGetterAndSetter] = someValue
x[keyPath: \.unavailableGetterAndSetter.a] = someValue.a // FIXME: missing diagnostics for getter and setter
x[keyPath: \.unavailableGetterAndSetter[0]] = someValue.a // FIXME: missing diagnostics for getter and setter
x[keyPath: \.unavailableGetterAndSetter[0].b] = 1 // FIXME: missing diagnostics for getter and setter
a[keyPath: \.[takesIntInOut(&x.unavailableGetterAndSetter.a.b)]] = 0 // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} FIXME: missing diagnostic for setter
a[keyPath: \.[takesIntInOut(&x.unavailableGetterAndSetter[0].b)]] = 0 // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
}

func testMutatingMember() {
var x = BaseStruct()
let a = [0]

x.available.setToZero()
x.available[0].setToZero()
_ = a[x.available.setToZero()]
_ = a[x.available.a.setToZero()]
_ = a[x.available[0].setToZero()]

x.unavailableGetter.setToZero() // expected-error {{getter for 'unavailableGetter' is unavailable}}
x.unavailableGetter[0].setToZero() // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = a[x.unavailableGetter.setToZero()] // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = a[x.unavailableGetter.a.setToZero()] // expected-error {{getter for 'unavailableGetter' is unavailable}}
_ = a[x.unavailableGetter[0].setToZero()] // expected-error {{getter for 'unavailableGetter' is unavailable}}

x.unavailableSetter.setToZero() // expected-error {{setter for 'unavailableSetter' is unavailable}}
x.unavailableSetter[0].setToZero() // expected-error {{setter for 'unavailableSetter' is unavailable}}
_ = a[x.unavailableSetter.setToZero()] // expected-error {{setter for 'unavailableSetter' is unavailable}}
_ = a[x.unavailableSetter.a.setToZero()] // FIXME: missing diagnostic for setter
_ = a[x.unavailableSetter[0].setToZero()] // expected-error {{setter for 'unavailableSetter' is unavailable}}

x.unavailableGetterAndSetter.setToZero() // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
x.unavailableGetterAndSetter[0].setToZero() // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
_ = a[x.unavailableGetterAndSetter.setToZero()] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
_ = a[x.unavailableGetterAndSetter.a.setToZero()] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} FIXME: should diagnose setter
_ = a[x.unavailableGetterAndSetter[0].setToZero()] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
}

func testPassAsInOutParameter() {
func takesInOut<T>(_ t: inout T) {}

var x = BaseStruct()

takesInOut(&x.available)
takesInOut(&x.available.a)
takesInOut(&x.available[0])
takesInOut(&x.available[0].b)

takesInOut(&x.unavailableGetter) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesInOut(&x.unavailableGetter.a) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesInOut(&x.unavailableGetter[0]) // expected-error {{getter for 'unavailableGetter' is unavailable}}
takesInOut(&x.unavailableGetter[0].b) // expected-error {{getter for 'unavailableGetter' is unavailable}}

takesInOut(&x.unavailableSetter) // expected-error {{setter for 'unavailableSetter' is unavailable}}
takesInOut(&x.unavailableSetter.a) //
takesInOut(&x.unavailableSetter[0]) // expected-error {{setter for 'unavailableSetter' is unavailable}}
takesInOut(&x.unavailableSetter[0].b) // expected-error {{setter for 'unavailableSetter' is unavailable}}

takesInOut(&x.unavailableGetterAndSetter) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
takesInOut(&x.unavailableGetterAndSetter.a) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} FIXME: missing diagnostic for setter
takesInOut(&x.unavailableGetterAndSetter[0]) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
takesInOut(&x.unavailableGetterAndSetter[0].b) // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}} expected-error {{setter for 'unavailableGetterAndSetter' is unavailable}}
}

var global = BaseStruct()

struct TestPatternBindingInitExprs {
var available = global.available
var available_a = global.available.a
var available_0 = global.available[0]
var available_0_b = global.available[0].b

var unavailableGetter = global.unavailableGetter // expected-error {{getter for 'unavailableGetter' is unavailable}}
var unavailableGetter_a = global.unavailableGetter.a // expected-error {{getter for 'unavailableGetter' is unavailable}}
var unavailableGetter_0 = global.unavailableGetter[0] // expected-error {{getter for 'unavailableGetter' is unavailable}}
var unavailableGetter_0_b = global.unavailableGetter[0].b // expected-error {{getter for 'unavailableGetter' is unavailable}}

var unavailableSetter = global.unavailableSetter
var unavailableSetter_a = global.unavailableSetter.a
var unavailableSetter_0 = global.unavailableSetter[0]
var unavailableSetter_0_b = global.unavailableSetter[0].b

var unavailableGetterAndSetter = global.unavailableGetterAndSetter // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
var unavailableGetterAndSetter_a = global.unavailableGetterAndSetter.a // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
var unavailableGetterAndSetter_0 = global.unavailableGetterAndSetter[0] // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
var unavailableGetterAndSetter_0_b = global.unavailableGetterAndSetter[0].b // expected-error {{getter for 'unavailableGetterAndSetter' is unavailable}}
}