-
-
Notifications
You must be signed in to change notification settings - Fork 670
Allow duplicate fields #2158
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
Allow duplicate fields #2158
Changes from all commits
ba106b3
948d235
10272de
61db171
34ecdfd
ea6705a
893e052
0bd2c80
d3ed920
0d27e0e
96fbcf5
d882877
b86912a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3092,18 +3092,26 @@ export class Resolver extends DiagnosticEmitter { | |
let fieldPrototype = <FieldPrototype>member; | ||
let fieldTypeNode = fieldPrototype.typeNode; | ||
let fieldType: Type | null = null; | ||
// TODO: handle duplicate non-private fields specifically? | ||
if (!fieldTypeNode) { | ||
if (base) { | ||
let baseMembers = base.members; | ||
if (baseMembers !== null && baseMembers.has(fieldPrototype.name)) { | ||
let baseField = assert(baseMembers.get(fieldPrototype.name)); | ||
if (!baseField.is(CommonFlags.PRIVATE)) { | ||
assert(baseField.kind == ElementKind.FIELD); | ||
fieldType = (<Field>baseField).type; | ||
} | ||
let existingField: Field | null = null; | ||
if (base) { | ||
let baseMembers = base.members; | ||
if (baseMembers !== null && baseMembers.has(fieldPrototype.name)) { | ||
let baseField = assert(baseMembers.get(fieldPrototype.name)); | ||
if (baseField.kind == ElementKind.FIELD) { | ||
existingField = <Field>baseField; | ||
} else { | ||
this.errorRelated( | ||
DiagnosticCode.Duplicate_identifier_0, | ||
fieldPrototype.identifierNode.range, baseField.identifierNode.range, | ||
fieldPrototype.name | ||
); | ||
} | ||
} | ||
} | ||
if (!fieldTypeNode) { | ||
if (existingField !== null && !existingField.is(CommonFlags.PRIVATE)) { | ||
fieldType = existingField.type; | ||
} | ||
if (!fieldType) { | ||
if (reportMode == ReportMode.REPORT) { | ||
this.error( | ||
|
@@ -3130,12 +3138,83 @@ export class Resolver extends DiagnosticEmitter { | |
} | ||
} | ||
if (!fieldType) break; // did report above | ||
if (existingField !== null) { | ||
// visibility checks | ||
/* | ||
existingField visibility on top | ||
+==================+=========+===========+=========+ | ||
| Visibility Table | Private | Protected | Public | | ||
+==================+=========+===========+=========+ | ||
| Private | error | error | error | | ||
+------------------+---------+-----------+---------+ | ||
| Protected | error | allowed | error | | ||
+------------------+---------+-----------+---------+ | ||
| Public | error | allowed | allowed | | ||
+------------------+---------+-----------+---------+ | ||
*/ | ||
|
||
let baseClass = <Class>base; | ||
|
||
// handle cases row-by-row | ||
if (fieldPrototype.is(CommonFlags.PRIVATE)) { | ||
if (existingField.is(CommonFlags.PRIVATE)) { | ||
this.errorRelated( | ||
DiagnosticCode.Types_have_separate_declarations_of_a_private_property_0, | ||
fieldPrototype.identifierNode.range, existingField.identifierNode.range, | ||
fieldPrototype.name | ||
); | ||
} else { | ||
this.errorRelated( | ||
DiagnosticCode.Property_0_is_private_in_type_1_but_not_in_type_2, | ||
fieldPrototype.identifierNode.range, existingField.identifierNode.range, | ||
fieldPrototype.name, instance.internalName, baseClass.internalName | ||
); | ||
} | ||
} else if (fieldPrototype.is(CommonFlags.PROTECTED)) { | ||
if (existingField.is(CommonFlags.PRIVATE)) { | ||
this.errorRelated( | ||
DiagnosticCode.Property_0_is_private_in_type_1_but_not_in_type_2, | ||
fieldPrototype.identifierNode.range, existingField.identifierNode.range, | ||
fieldPrototype.name, baseClass.internalName, instance.internalName | ||
); | ||
} else if (!existingField.is(CommonFlags.PROTECTED)) { | ||
// may be implicitly public | ||
this.errorRelated( | ||
DiagnosticCode.Property_0_is_protected_in_type_1_but_public_in_type_2, | ||
fieldPrototype.identifierNode.range, existingField.identifierNode.range, | ||
fieldPrototype.name, instance.internalName, baseClass.internalName | ||
); | ||
} | ||
} else { | ||
// fieldPrototype is public here | ||
if (existingField.is(CommonFlags.PRIVATE)) { | ||
this.errorRelated( | ||
DiagnosticCode.Property_0_is_private_in_type_1_but_not_in_type_2, | ||
fieldPrototype.identifierNode.range, existingField.identifierNode.range, | ||
fieldPrototype.name, baseClass.internalName, instance.internalName | ||
); | ||
} | ||
} | ||
|
||
// assignability | ||
if (!fieldType.isStrictlyAssignableTo(existingField.type)) { | ||
this.errorRelated( | ||
DiagnosticCode.Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2, | ||
fieldPrototype.identifierNode.range, existingField.identifierNode.range, | ||
fieldPrototype.name, instance.internalName, baseClass.internalName | ||
); | ||
} | ||
} | ||
romdotdog marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let fieldInstance = new Field(fieldPrototype, instance, fieldType); | ||
assert(isPowerOf2(fieldType.byteSize)); | ||
let mask = fieldType.byteSize - 1; | ||
if (memoryOffset & mask) memoryOffset = (memoryOffset | mask) + 1; | ||
fieldInstance.memoryOffset = memoryOffset; | ||
memoryOffset += fieldType.byteSize; | ||
if (existingField !== null) { | ||
fieldInstance.memoryOffset = existingField.memoryOffset; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a vague suspicion that this can lead to issues, since typically inherited fields share the same flow flags for example, which is not the case anymore when duplicating the field. I don't see a case right now where this would trigger for sure (perhaps field initialization checks in ctors, hmm), though, but leaving a note here in case we ever encounter something along those lines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you like me to insert some sort of comment there? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it's fine without, just wanted to leave the note so you and me are aware in case :) |
||
} else { | ||
let mask = fieldType.byteSize - 1; | ||
if (memoryOffset & mask) memoryOffset = (memoryOffset | mask) + 1; | ||
fieldInstance.memoryOffset = memoryOffset; | ||
memoryOffset += fieldType.byteSize; | ||
} | ||
instance.add(memberName, fieldInstance); // reports | ||
break; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"asc_flags": [ | ||
], | ||
"stderr": [ | ||
"TS2442: Types have separate declarations of a private property 'privPriv'.", | ||
"TS2325: Property 'privProt' is private in type 'duplicate-field-errors/A' but not in type 'duplicate-field-errors/B'.", | ||
"TS2325: Property 'privPub' is private in type 'duplicate-field-errors/A' but not in type 'duplicate-field-errors/B'.", | ||
"TS2325: Property 'protPriv' is private in type 'duplicate-field-errors/B' but not in type 'duplicate-field-errors/A'.", | ||
"TS2325: Property 'pubPriv' is private in type 'duplicate-field-errors/B' but not in type 'duplicate-field-errors/A'.", | ||
"TS2444: Property 'pubProt' is protected in type 'duplicate-field-errors/B' but public in type 'duplicate-field-errors/A'.", | ||
"TS2300: Duplicate identifier 'method'." | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
class A { | ||
private privPriv: i32; | ||
private privProt: i32; | ||
private privPub: i32; | ||
|
||
// -- | ||
|
||
protected protPriv: i32; | ||
public pubPriv: i32; | ||
|
||
// -- | ||
|
||
public pubProt: i32; | ||
|
||
// -- | ||
|
||
method(): void {} | ||
} | ||
|
||
export class B extends A { | ||
private privPriv: i32; | ||
protected privProt: i32; | ||
public privPub: i32; | ||
|
||
// -- | ||
|
||
private protPriv: i32; | ||
private pubPriv: i32; | ||
|
||
// -- | ||
|
||
protected pubProt: i32; | ||
|
||
// -- | ||
|
||
public method: i32; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"asc_flags": [ | ||
] | ||
} |
Uh oh!
There was an error while loading. Please reload this page.