|
@@ -31,12 +31,12 @@ export type M2Type =
|
|
|
| { kind: 'stringLit', length: number }
|
|
| { kind: 'stringLit', length: number }
|
|
|
| { kind: 'bool' }
|
|
| { kind: 'bool' }
|
|
|
| { kind: 'bitset' }
|
|
| { kind: 'bitset' }
|
|
|
- | { kind: 'setNamed', name: string }
|
|
|
|
|
|
|
+ | { kind: 'setNamed', name: string, decl?: string }
|
|
|
| { kind: 'setLit' }
|
|
| { kind: 'setLit' }
|
|
|
- | { kind: 'array', element: M2Type, size: number | null, name: string | null }
|
|
|
|
|
- | { kind: 'record', name: string | null, textKey: string }
|
|
|
|
|
- | { kind: 'pointer', target: M2Type, name: string | null }
|
|
|
|
|
- | { kind: 'enum', name: string }
|
|
|
|
|
|
|
+ | { kind: 'array', element: M2Type, size: number | null, name: string | null, decl?: string }
|
|
|
|
|
+ | { kind: 'record', name: string | null, textKey: string, decl?: string }
|
|
|
|
|
+ | { kind: 'pointer', target: M2Type, name: string | null, decl?: string }
|
|
|
|
|
+ | { kind: 'enum', name: string, decl?: string }
|
|
|
| { kind: 'subrange', base: M2Type }
|
|
| { kind: 'subrange', base: M2Type }
|
|
|
| { kind: 'proc', params?: M2Type[], result?: M2Type | null }
|
|
| { kind: 'proc', params?: M2Type[], result?: M2Type | null }
|
|
|
| { kind: 'nil' };
|
|
| { kind: 'nil' };
|
|
@@ -333,24 +333,33 @@ function resolveT(t: M2Type, unit: M2Unit, dir: string, line: number, ch: number
|
|
|
// NIL assignment, same-type comparison and mismatch detection work.
|
|
// NIL assignment, same-type comparison and mismatch detection work.
|
|
|
// (Approximation: an opaque type need not be pointer-implemented, but
|
|
// (Approximation: an opaque type need not be pointer-implemented, but
|
|
|
// treating it as unknown would silence all checking on it.)
|
|
// treating it as unknown would silence all checking on it.)
|
|
|
- return { kind: 'pointer', target: UNKNOWN, name: t.qualifier ? `${t.qualifier}.${t.name}` : t.name };
|
|
|
|
|
|
|
+ const r = resolved.sym.nameRange;
|
|
|
|
|
+ return {
|
|
|
|
|
+ kind: 'pointer', target: UNKNOWN,
|
|
|
|
|
+ name: t.qualifier ? `${t.qualifier}.${t.name}` : t.name,
|
|
|
|
|
+ decl: `${resolved.filePath}:${r.startLine}:${r.startCh}`,
|
|
|
|
|
+ };
|
|
|
}
|
|
}
|
|
|
seen.add(key);
|
|
seen.add(key);
|
|
|
const inner = resolveT(
|
|
const inner = resolveT(
|
|
|
parseTypeText(detail.slice(eq + 1)), host, path.dirname(resolved.filePath),
|
|
parseTypeText(detail.slice(eq + 1)), host, path.dirname(resolved.filePath),
|
|
|
resolved.sym.nameRange.startLine, resolved.sym.nameRange.startCh, seen,
|
|
resolved.sym.nameRange.startLine, resolved.sym.nameRange.startCh, seen,
|
|
|
);
|
|
);
|
|
|
- return attachName(inner, t.qualifier ? `${t.qualifier}.${t.name}` : t.name);
|
|
|
|
|
|
|
+ const r = resolved.sym.nameRange;
|
|
|
|
|
+ const decl = `${resolved.filePath}:${r.startLine}:${r.startCh}`;
|
|
|
|
|
+ return attachName(inner, t.qualifier ? `${t.qualifier}.${t.name}` : t.name, decl);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** Attach the ultimate type name so nominal comparison works through aliases. */
|
|
|
|
|
-function attachName(t: M2Type, name: string): M2Type {
|
|
|
|
|
|
|
+/** Attach the ultimate type name (and declaration identity) so nominal
|
|
|
|
|
+ * comparison works through aliases and qualified spellings
|
|
|
|
|
+ * (`RiseSetResult` and `Sunrise.RiseSetResult` share one declaration). */
|
|
|
|
|
+function attachName(t: M2Type, name: string, decl: string | undefined): M2Type {
|
|
|
switch (t.kind) {
|
|
switch (t.kind) {
|
|
|
- case 'array': return { ...t, name };
|
|
|
|
|
- case 'record': return { ...t, name };
|
|
|
|
|
- case 'pointer': return { ...t, name };
|
|
|
|
|
- case 'enum': return { ...t, name: t.name || name };
|
|
|
|
|
- case 'setNamed': return { ...t, name: t.name || name };
|
|
|
|
|
|
|
+ case 'array': return { ...t, name, decl: decl ?? t.decl };
|
|
|
|
|
+ case 'record': return { ...t, name, decl: decl ?? t.decl };
|
|
|
|
|
+ case 'pointer': return { ...t, name, decl: decl ?? t.decl };
|
|
|
|
|
+ case 'enum': return { ...t, name: t.name || name, decl: decl ?? t.decl };
|
|
|
|
|
+ case 'setNamed': return { ...t, name: t.name || name, decl: decl ?? t.decl };
|
|
|
default: return t;
|
|
default: return t;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -388,10 +397,12 @@ export function isAssignable(to: M2Type, from: M2Type): boolean {
|
|
|
return t.size === null || f.length <= t.size;
|
|
return t.size === null || f.length <= t.size;
|
|
|
}
|
|
}
|
|
|
if (t.kind === 'array' && f.kind === 'array') {
|
|
if (t.kind === 'array' && f.kind === 'array') {
|
|
|
|
|
+ if (t.decl && f.decl) return t.decl === f.decl;
|
|
|
if (t.name || f.name) return sameNominal(t.name ?? null, f.name ?? null);
|
|
if (t.name || f.name) return sameNominal(t.name ?? null, f.name ?? null);
|
|
|
return normKey(t) === normKey(f);
|
|
return normKey(t) === normKey(f);
|
|
|
}
|
|
}
|
|
|
if (t.kind === 'record' && f.kind === 'record') {
|
|
if (t.kind === 'record' && f.kind === 'record') {
|
|
|
|
|
+ if (t.decl && f.decl) return t.decl === f.decl;
|
|
|
if (t.name || f.name) return sameNominal(t.name ?? null, f.name ?? null);
|
|
if (t.name || f.name) return sameNominal(t.name ?? null, f.name ?? null);
|
|
|
return normKey(t) === normKey(f);
|
|
return normKey(t) === normKey(f);
|
|
|
}
|
|
}
|
|
@@ -399,11 +410,17 @@ export function isAssignable(to: M2Type, from: M2Type): boolean {
|
|
|
if (t.kind === 'pointer' && f.kind === 'pointer') return typeKey(t.target) === typeKey(f.target);
|
|
if (t.kind === 'pointer' && f.kind === 'pointer') return typeKey(t.target) === typeKey(f.target);
|
|
|
if (t.kind === 'proc' && f.kind === 'nil') return true;
|
|
if (t.kind === 'proc' && f.kind === 'nil') return true;
|
|
|
if (t.kind === 'proc' && f.kind === 'proc') return true;
|
|
if (t.kind === 'proc' && f.kind === 'proc') return true;
|
|
|
- if (t.kind === 'enum' && f.kind === 'enum') return t.name === f.name;
|
|
|
|
|
|
|
+ if (t.kind === 'enum' && f.kind === 'enum') {
|
|
|
|
|
+ if (t.decl && f.decl) return t.decl === f.decl;
|
|
|
|
|
+ return t.name === f.name;
|
|
|
|
|
+ }
|
|
|
if (t.kind === 'bool' && f.kind === 'bool') return true;
|
|
if (t.kind === 'bool' && f.kind === 'bool') return true;
|
|
|
if (t.kind === 'bitset' && (f.kind === 'bitset' || f.kind === 'setLit')) return true;
|
|
if (t.kind === 'bitset' && (f.kind === 'bitset' || f.kind === 'setLit')) return true;
|
|
|
if (t.kind === 'setNamed' && (f.kind === 'setLit' || f.kind === 'bitset')) return true;
|
|
if (t.kind === 'setNamed' && (f.kind === 'setLit' || f.kind === 'bitset')) return true;
|
|
|
- if (t.kind === 'setNamed' && f.kind === 'setNamed') return t.name === f.name;
|
|
|
|
|
|
|
+ if (t.kind === 'setNamed' && f.kind === 'setNamed') {
|
|
|
|
|
+ if (t.decl && f.decl) return t.decl === f.decl;
|
|
|
|
|
+ return t.name === f.name;
|
|
|
|
|
+ }
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -439,7 +456,10 @@ export function isComparable(a: M2Type, b: M2Type, ordering: boolean): boolean {
|
|
|
if (ta.kind === 'stringLit' && tb.kind === 'array') return tb.element.kind === 'char' || tb.element.kind === 'unknown';
|
|
if (ta.kind === 'stringLit' && tb.kind === 'array') return tb.element.kind === 'char' || tb.element.kind === 'unknown';
|
|
|
if (tb.kind === 'stringLit' && ta.kind === 'array') return ta.element.kind === 'char' || ta.element.kind === 'unknown';
|
|
if (tb.kind === 'stringLit' && ta.kind === 'array') return ta.element.kind === 'char' || ta.element.kind === 'unknown';
|
|
|
if (ta.kind === 'bool' && tb.kind === 'bool') return !ordering;
|
|
if (ta.kind === 'bool' && tb.kind === 'bool') return !ordering;
|
|
|
- if (ta.kind === 'enum' && tb.kind === 'enum') return ta.name === tb.name;
|
|
|
|
|
|
|
+ if (ta.kind === 'enum' && tb.kind === 'enum') {
|
|
|
|
|
+ if (ta.decl && tb.decl) return ta.decl === tb.decl;
|
|
|
|
|
+ return ta.name === tb.name;
|
|
|
|
|
+ }
|
|
|
if (isSetish(ta) && isSetish(tb)) return !ordering;
|
|
if (isSetish(ta) && isSetish(tb)) return !ordering;
|
|
|
if (ta.kind === 'pointer' && tb.kind === 'pointer') {
|
|
if (ta.kind === 'pointer' && tb.kind === 'pointer') {
|
|
|
if (ordering) return false;
|
|
if (ordering) return false;
|
|
@@ -550,10 +570,10 @@ function typeKey(t: M2Type): string {
|
|
|
switch (b.kind) {
|
|
switch (b.kind) {
|
|
|
case 'whole': return `whole:${b.literal ? '*' : b.id}`;
|
|
case 'whole': return `whole:${b.literal ? '*' : b.id}`;
|
|
|
case 'real': return `real:${b.literal ? '*' : b.id}`;
|
|
case 'real': return `real:${b.literal ? '*' : b.id}`;
|
|
|
- case 'array': return `array:${b.name ?? normKey(b.element)}`;
|
|
|
|
|
- case 'record': return `record:${b.name ?? normKey(b)}`;
|
|
|
|
|
|
|
+ case 'array': return `array:${b.decl ?? b.name ?? normKey(b.element)}`;
|
|
|
|
|
+ case 'record': return `record:${b.decl ?? b.name ?? normKey(b)}`;
|
|
|
case 'pointer': return `pointer:${b.name ?? typeKey(b.target)}`;
|
|
case 'pointer': return `pointer:${b.name ?? typeKey(b.target)}`;
|
|
|
- case 'enum': return `enum:${b.name}`;
|
|
|
|
|
|
|
+ case 'enum': return `enum:${b.decl ?? b.name}`;
|
|
|
case 'unknown': return 'unknown';
|
|
case 'unknown': return 'unknown';
|
|
|
default: return b.kind;
|
|
default: return b.kind;
|
|
|
}
|
|
}
|