Skip to content

Commit d798d3f

Browse files
authored
Merge pull request swiftlang#39190 from xedin/diagnostics-for-weak-patterns
[ResultBuilders] Properly diagnose `weak` declarations with non-optional type
2 parents 33b5868 + 3bbde55 commit d798d3f

File tree

7 files changed

+131
-25
lines changed

7 files changed

+131
-25
lines changed

include/swift/Sema/CSFix.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,10 @@ enum class FixKind : uint8_t {
331331

332332
/// Specify a type for an explicitly written placeholder that could not be
333333
/// resolved.
334-
SpecifyTypeForPlaceholder
334+
SpecifyTypeForPlaceholder,
335+
336+
/// Allow `weak` declarations to be bound to a non-optional type.
337+
AllowNonOptionalWeak,
335338
};
336339

337340
class ConstraintFix {
@@ -2424,6 +2427,21 @@ class AllowInvalidStaticMemberRefOnProtocolMetatype final
24242427
create(ConstraintSystem &cs, ConstraintLocator *locator);
24252428
};
24262429

2430+
class AllowNonOptionalWeak final : public ConstraintFix {
2431+
AllowNonOptionalWeak(ConstraintSystem &cs, ConstraintLocator *locator)
2432+
: ConstraintFix(cs, FixKind::AllowNonOptionalWeak, locator) {}
2433+
2434+
public:
2435+
std::string getName() const override {
2436+
return "allow `weak` with non-optional type";
2437+
}
2438+
2439+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2440+
2441+
static AllowNonOptionalWeak *create(ConstraintSystem &cs,
2442+
ConstraintLocator *locator);
2443+
};
2444+
24272445
} // end namespace constraints
24282446
} // end namespace swift
24292447

lib/Sema/CSDiagnostics.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7776,3 +7776,27 @@ bool UnsupportedRuntimeCheckedCastFailure::diagnoseAsError() {
77767776
.fixItReplace(getCastRange(), "as");
77777777
return true;
77787778
}
7779+
7780+
bool InvalidWeakAttributeUse::diagnoseAsError() {
7781+
auto *pattern =
7782+
dyn_cast_or_null<NamedPattern>(getAnchor().dyn_cast<Pattern *>());
7783+
if (!pattern)
7784+
return false;
7785+
7786+
auto *var = pattern->getDecl();
7787+
auto varType = OptionalType::get(getType(var));
7788+
7789+
auto diagnostic =
7790+
emitDiagnosticAt(var, diag::invalid_ownership_not_optional,
7791+
ReferenceOwnership::Weak, varType);
7792+
7793+
auto typeRange = var->getTypeSourceRangeForDiagnostics();
7794+
if (varType->hasSimpleTypeRepr()) {
7795+
diagnostic.fixItInsertAfter(typeRange.End, "?");
7796+
} else {
7797+
diagnostic.fixItInsert(typeRange.Start, "(")
7798+
.fixItInsertAfter(typeRange.End, ")?");
7799+
}
7800+
7801+
return true;
7802+
}

lib/Sema/CSDiagnostics.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,6 +2589,25 @@ class InvalidMemberRefOnProtocolMetatype final : public FailureDiagnostic {
25892589
bool diagnoseAsError() override;
25902590
};
25912591

2592+
/// Diagnose situations where `weak` attribute is used with a non-optional type
2593+
/// in declaration e.g. `weak var x: <Type>`:
2594+
///
2595+
/// \code
2596+
/// class X {
2597+
/// }
2598+
///
2599+
/// weak var x: X = ...
2600+
/// \endcode
2601+
///
2602+
/// `weak` declaration is required to use an optional type e.g. `X?`.
2603+
class InvalidWeakAttributeUse final : public FailureDiagnostic {
2604+
public:
2605+
InvalidWeakAttributeUse(const Solution &solution, ConstraintLocator *locator)
2606+
: FailureDiagnostic(solution, locator) {}
2607+
2608+
bool diagnoseAsError() override;
2609+
};
2610+
25922611
} // end namespace constraints
25932612
} // end namespace swift
25942613

lib/Sema/CSFix.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,3 +1989,14 @@ AllowInvalidStaticMemberRefOnProtocolMetatype::create(
19891989
return new (cs.getAllocator())
19901990
AllowInvalidStaticMemberRefOnProtocolMetatype(cs, locator);
19911991
}
1992+
1993+
bool AllowNonOptionalWeak::diagnose(const Solution &solution,
1994+
bool asNote) const {
1995+
InvalidWeakAttributeUse failure(solution, getLocator());
1996+
return failure.diagnose(asNote);
1997+
}
1998+
1999+
AllowNonOptionalWeak *AllowNonOptionalWeak::create(ConstraintSystem &cs,
2000+
ConstraintLocator *locator) {
2001+
return new (cs.getAllocator()) AllowNonOptionalWeak(cs, locator);
2002+
}

lib/Sema/CSGen.cpp

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,31 +2210,19 @@ namespace {
22102210
oneWayVarType = CS.createTypeVariable(
22112211
CS.getConstraintLocator(locator), TVO_CanBindToNoEscape);
22122212

2213-
// If there is an externally-imposed pattern type and the
2214-
// binding/capture is marked as `weak`, let's make sure
2215-
// that the imposed type is optional.
2213+
// If there is externally-imposed type, and the variable
2214+
// is marked as `weak`, let's fallthrough and allow the
2215+
// `one-way` constraint to be fixed in diagnostic mode.
22162216
//
2217-
// Note that there is no need to check `varType` since
2218-
// it's only "externally" bound if this pattern isn't marked
2219-
// as `weak`.
2220-
if (externalPatternType &&
2221-
optionality == ReferenceOwnershipOptionality::Required) {
2222-
// If the type is not yet known, let's add a constraint
2223-
// to make sure that it can only be bound to an optional type.
2224-
if (externalPatternType->isTypeVariableOrMember()) {
2225-
auto objectTy = CS.createTypeVariable(
2226-
CS.getConstraintLocator(locator.withPathElement(
2227-
ConstraintLocator::OptionalPayload)),
2228-
TVO_CanBindToLValue | TVO_CanBindToNoEscape);
2229-
2230-
CS.addConstraint(ConstraintKind::OptionalObject,
2231-
externalPatternType, objectTy, locator);
2232-
} else if (!externalPatternType->getOptionalObjectType()) {
2233-
// TODO(diagnostics): A tailored fix to indiciate that `weak`
2234-
// should have an optional type.
2235-
return Type();
2236-
}
2237-
}
2217+
// That would make sure that type of this variable is
2218+
// recorded in the constraint system, which would then
2219+
// be used instead of `getVarType` upon discovering a
2220+
// reference to this variable in subsequent expression(s).
2221+
//
2222+
// If we let constraint generation fail here, it would trigger
2223+
// interface type request via `var->getType()` that would
2224+
// attempt to validate `weak` attribute, and produce a
2225+
// diagnostic in the middle of the solver path.
22382226

22392227
CS.addConstraint(ConstraintKind::OneWayEqual, oneWayVarType,
22402228
externalPatternType ? externalPatternType : varType,

lib/Sema/CSSimplify.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4956,6 +4956,25 @@ bool ConstraintSystem::repairFailures(
49564956
*this, lhs, rhs, getConstraintLocator(locator)));
49574957
}
49584958

4959+
// `weak` declaration with an explicit non-optional type e.g.
4960+
// `weak x: X = ...` where `X` is a class.
4961+
if (auto *TP = dyn_cast<TypedPattern>(pattern)) {
4962+
if (auto *NP = dyn_cast<NamedPattern>(TP->getSubPattern())) {
4963+
auto *var = NP->getDecl();
4964+
4965+
auto ROK = ReferenceOwnership::Strong;
4966+
if (auto *OA = var->getAttrs().getAttribute<ReferenceOwnershipAttr>())
4967+
ROK = OA->get();
4968+
4969+
if (!rhs->getOptionalObjectType() &&
4970+
optionalityOf(ROK) == ReferenceOwnershipOptionality::Required) {
4971+
conversionsOrFixes.push_back(
4972+
AllowNonOptionalWeak::create(*this, getConstraintLocator(NP)));
4973+
break;
4974+
}
4975+
}
4976+
}
4977+
49594978
break;
49604979
}
49614980

@@ -11709,6 +11728,17 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1170911728
return SolutionKind::Solved;
1171011729
}
1171111730

11731+
case FixKind::AllowNonOptionalWeak: {
11732+
if (recordFix(fix))
11733+
return SolutionKind::Error;
11734+
11735+
(void)matchTypes(type1, OptionalType::get(type2),
11736+
ConstraintKind::Conversion,
11737+
TypeMatchFlags::TMF_ApplyingFix, locator);
11738+
11739+
return SolutionKind::Solved;
11740+
}
11741+
1171211742
case FixKind::UseSubscriptOperator:
1171311743
case FixKind::ExplicitlyEscaping:
1171411744
case FixKind::MarkGlobalActorFunction:

test/Constraints/result_builder_diags.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,3 +781,19 @@ func test_rdar65667992() {
781781
}
782782
}
783783
}
784+
785+
func test_weak_with_nonoptional_type() {
786+
class X {
787+
func test() -> Int { 0 }
788+
}
789+
790+
tuplify(true) { c in
791+
weak var x: X = X() // expected-error {{'weak' variable should have optional type 'X?'}}
792+
793+
if let x = x {
794+
x.test()
795+
}
796+
797+
42
798+
}
799+
}

0 commit comments

Comments
 (0)