Skip to content

Commit d4f1894

Browse files
author
Nathan Hawes
committed
Simplify diagnostics for inherited default argument values in module interfaces
Also: - additionally require the containing and overridden initializers are designated, as that's the only case in which we should produce the '= super' syntax in module interfaces - Add notes to point out the locations of the overriden initializer when it's not designated, and the corresponding parameter in that initializer when it doesn't have a default argument to inherit.
1 parent 1e10252 commit d4f1894

File tree

5 files changed

+94
-66
lines changed

5 files changed

+94
-66
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,15 +1281,18 @@ WARNING(expr_dynamic_lookup_swift3_objc_inference,none,
12811281
"deprecated in Swift 4",
12821282
(DescriptiveDeclKind, DeclName, Identifier))
12831283

1284-
ERROR(inherited_default_value_not_on_constructor_param,none,
1285-
"default value inheritance via 'super' is only valid for initializer "
1286-
"parameters", ())
1284+
ERROR(inherited_default_value_not_in_designated_constructor,none,
1285+
"default value inheritance via 'super' is only valid on the parameters of "
1286+
"designated initializers", ())
1287+
ERROR(inherited_default_value_used_in_non_overriding_constructor,none,
1288+
"default value inheritance via 'super' can only be used when "
1289+
"overriding a designated initializer", ())
12871290
ERROR(corresponding_param_not_defaulted,none,
12881291
"default value inheritance via 'super' requires that the corresponding "
1289-
"parameter of the overridden initializer has a default value", ())
1290-
ERROR(inherited_default_value_used_in_non_overriding_constructor,none,
1291-
"default value inheritance via 'super' can only be used on the "
1292-
"parameters of overriding initializers", ())
1292+
"parameter of the overridden designated initializer has a default value",
1293+
())
1294+
NOTE(inherited_default_param_here,none,
1295+
"corresponding parameter declared here", ())
12931296

12941297
// Alignment attribute
12951298
ERROR(alignment_not_power_of_two,none,

lib/Parse/ParsePattern.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,16 @@ static ParserStatus parseDefaultArgument(
7171
SyntaxParsingContext DefaultArgContext(P.SyntaxContext,
7272
SyntaxKind::InitializerClause);
7373
SourceLoc equalLoc = P.consumeToken(tok::equal);
74-
// Handle the special '= super' syntax for inherited default values in module
75-
// interface files
74+
7675
if (P.SF.Kind == SourceFileKind::Interface) {
76+
// Swift module interfaces don't synthesize inherited intializers and
77+
// instead include them explicitly in subclasses. Since the
78+
// \c DefaultArgumentKind of these initializers is \c Inherited, this is
79+
// represented textually as `= super` in the interface.
80+
81+
// If we're in a module interface and the default argument is exactly
82+
// `super` (i.e. the token after that is `,` or `)` which end a parameter)
83+
// report an inherited default argument to the caller and return.
7784
if (P.Tok.is(tok::kw_super) && P.peekToken().isAny(tok::comma, tok::r_paren)) {
7885
hasInheritedDefaultArg = true;
7986
P.consumeToken(tok::kw_super);

lib/Sema/TypeCheckStmt.cpp

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,51 +1753,37 @@ checkInheritedDefaultValueRestrictions(TypeChecker &TC, ParamDecl *PD) {
17531753

17541754
auto *DC = PD->getInnermostDeclContext();
17551755
const SourceFile *SF = DC->getParentSourceFile();
1756-
if (!SF || SF->Kind != SourceFileKind::Interface)
1756+
assert((SF && SF->Kind == SourceFileKind::Interface || PD->isImplicit()) &&
1757+
"explicit inherited default argument outside of a module interface?");
1758+
1759+
// The containing decl should be a designated initializer.
1760+
auto ctor = dyn_cast<ConstructorDecl>(DC);
1761+
if (!ctor || ctor->isConvenienceInit()) {
1762+
TC.diagnose(
1763+
PD->getLoc(),
1764+
diag::inherited_default_value_not_in_designated_constructor);
17571765
return;
1766+
}
17581767

1759-
if (!isa<ConstructorDecl>(DC)) {
1760-
TC.diagnose(PD->getLoc(),
1761-
diag::inherited_default_value_not_on_constructor_param);
1768+
// The decl it overrides should also be a designated initializer.
1769+
auto overridden = ctor->getOverriddenDecl();
1770+
if (!overridden || overridden->isConvenienceInit()) {
1771+
TC.diagnose(
1772+
PD->getLoc(),
1773+
diag::inherited_default_value_used_in_non_overriding_constructor);
1774+
if (overridden)
1775+
TC.diagnose(overridden->getLoc(), diag::overridden_here);
17621776
return;
17631777
}
17641778

1765-
// Check the matching parameter of an overriden decl has a default value
1766-
auto ctor = cast<ConstructorDecl>(DC);
1779+
// The corresponding parameter should have a default value.
17671780
Optional<unsigned> idx = getParamIndex(ctor->getParameters(), PD);
1768-
bool foundOverriden = false;
17691781
assert(idx && "containing decl does not contain param?");
1770-
for (auto overridden = ctor->getOverriddenDecl();
1771-
overridden != nullptr;
1772-
overridden = overridden->getOverriddenDecl()) {
1773-
foundOverriden = true;
1774-
auto equivalentParam = overridden->getParameters()->get(*idx);
1775-
assert(equivalentParam && "inherited decl mismatched arguments?");
1776-
1777-
// If the corresponding overridden param is also inherited, keep looking
1778-
if (equivalentParam->getDefaultArgumentKind() ==
1779-
DefaultArgumentKind::Inherited)
1780-
continue;
1781-
1782-
// If it doesn't have a default value, diagnose it
1783-
if (equivalentParam->getDefaultArgumentKind() == DefaultArgumentKind::None) {
1784-
TC.diagnose(PD->getLoc(),
1785-
diag::corresponding_param_not_defaulted);
1786-
}
1787-
// We found a match: it's valid
1788-
return;
1782+
ParamDecl *equivalentParam = overridden->getParameters()->get(*idx);
1783+
if (equivalentParam->getDefaultArgumentKind() == DefaultArgumentKind::None) {
1784+
TC.diagnose(PD->getLoc(), diag::corresponding_param_not_defaulted);
1785+
TC.diagnose(equivalentParam->getLoc(), diag::inherited_default_param_here);
17891786
}
1790-
1791-
// Used with a param of a non-overriding initializer, or one where the
1792-
// corresponding param in the overridden decl was also inherited but *it*
1793-
// didn't override anything. Diagnose the first case and ignore the second.
1794-
// The second will be diagnosed when checking the default args of the
1795-
// overridden declaration.
1796-
if (foundOverriden)
1797-
return;
1798-
1799-
TC.diagnose(PD->getLoc(),
1800-
diag::inherited_default_value_used_in_non_overriding_constructor);
18011787
}
18021788

18031789
/// Check the default arguments that occur within this pattern.

test/ParseableInterface/default-args.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,24 @@
1212
// CHECK: class Base {
1313
public class Base {
1414
// CHECK: init(x: Int = 3)
15-
public init(x: Int = 3) {}
16-
// CHECK: foo(y: Int = 42)
17-
public func foo(y: Int = 42) {}
15+
public init(x: Int = 3) {}
16+
public convenience init(convInit: Int) {
17+
self.init(x: convInit)
18+
}
19+
// CHECK: foo(y: Int = 42)
20+
public func foo(y: Int = 42) {}
1821
}
1922

2023
// CHECK: class Derived : Base {
2124
public class Derived: Base {
22-
// CHECK: init(y: Int)
23-
public convenience init(y: Int) {
24-
self.init()
25-
}
26-
27-
// CHECK: override {{(public )?}}init(x: Int = super)
25+
// CHECK: init(y: Int)
26+
public convenience init(y: Int) {
27+
self.init()
28+
}
29+
30+
// CHECK-NOT: init(convInit: Int = super)
31+
// CHECK: override {{(public )?}}init(x: Int = super)
32+
// CHECK-NOT: init(convInit: Int = super)
2833
}
2934

3035
public enum Enum {

test/ParseableInterface/inherited-defaults-checks.swiftinterface

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,63 @@
33

44
// RUN: %empty-directory(%t)
55
// RUN: %target-typecheck-verify-swift
6-
// BUN: %target-swift-frontend -typecheck -verify -module-cache-path %t/MCP -o %t/InheritedDefaults.swiftmodule -module-name InheritedDefaults %s
76

87
import Swift
98

109
public class Bar {
11-
public init(x: Int = 24, y: Int)
10+
// note associated with the expected error in (F) below
11+
public init(x: Int = 24, y: Int, z: Int = 42) // expected-note {{corresponding parameter declared here}}
12+
13+
public init(a: Int, b: Int = 99)
14+
public convenience init(convInit: Int = 45) {}
15+
16+
// note associated with the expected error in (D) below
17+
public convenience init(first: Int, second: Int = 88, third: Int, fourth: Int) // expected-note {{overridden declaration is here}}
1218
}
1319

1420
public class Foo: Bar {
15-
public override init(x: Int = super, y: Int)
16-
public subscript(k: Int = super) -> Int { get } // expected-error {{default value inheritance via 'super' is only valid for initializer parameters}}
17-
public func foo(z: Int = super) // expected-error {{default value inheritance via 'super' is only valid for initializer parameters}}
21+
22+
// A) designated overriding designated - valid
23+
public override init(x: Int = super, y: Int, z: Int = super)
24+
25+
// B) convenience shadowing convenience
26+
public convenience init(convInit: Int = super) // expected-error {{default value inheritance via 'super' is only valid on the parameters of designated initializers}}
27+
28+
// C) convenience overriding designated
29+
public override convenience init(a: Int, b: Int = super) // expected-error {{default value inheritance via 'super' is only valid on the parameters of designated initializers}}
30+
31+
// D) designated shadowing convenience
32+
public init(first: Int, second: Int = super, third: Int, fourth: Int) // expected-error {{default value inheritance via 'super' can only be used when overriding a designated initializer}}
33+
34+
// E) not in initializer
35+
public subscript(k: Int = super) -> Int { get } // expected-error {{default value inheritance via 'super' is only valid on the parameters of designated initializers}}
36+
public func foo(z: Int = super) // expected-error {{default value inheritance via 'super' is only valid on the parameters of designated initializers}}
1837
}
1938

2039
public class Baz: Bar {
21-
public override init(x: Int = super, y: Int = super) // expected-error {{default value inheritance via 'super' requires that the corresponding parameter of the overridden initializer has a default value}}
40+
41+
// F) Matching param not defaulted
42+
public override init(x: Int = super, y: Int = super, z: Int = super) // expected-error {{default value inheritance via 'super' requires that the corresponding parameter of the overridden designated initializer has a default value}}
2243
}
2344

2445
public class Direct: Bar {
25-
public override init(x: Int = super, y: Int)
26-
public override init(other: Int = super, value: Int) // expected-error {{argument labels for initializer 'init(other:value:)' do not match those of overridden initializer 'init(x:y:)'}}
27-
// expected-error@-1 {{default value inheritance via 'super' can only be used on the parameters of overriding initializers}}
46+
public override init(x: Int = super, y: Int, z: Int = super)
47+
48+
// G) Doesn't override anything
49+
public override init(other: Int = super, value: Int) // expected-error {{argument labels for initializer 'init(other:value:)' do not match those of overridden initializer 'init(a:b:)'}}
50+
// expected-error@-1 {{default value inheritance via 'super' can only be used when overriding a designated initializer}}
2851
}
2952

3053
public class Indirect: Direct {
31-
public override init(x: Int = super, y: Int)
54+
55+
// H) Chain of inherited defaults - valid all the way down
56+
public override init(x: Int = super, y: Int, z: Int = super)
57+
58+
// I) Chain of inherited defaults - invalid further down (and diagnosed there)
3259
public override init(other: Int = super, value: Int)
3360
}
3461

3562
public enum Bob {
3663
case bar(p: Int)
37-
public init(foo: String = super /*comment*/) // expected-error {{default value inheritance via 'super' can only be used on the parameters of overriding initializers}}
64+
public init(foo: String = super /*comment*/) // expected-error {{default value inheritance via 'super' can only be used when overriding a designated initializer}}
3865
}

0 commit comments

Comments
 (0)