Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 | 3265x 3265x 6919x 6919x 6919x 6919x 6919x 3265x 3265x 6919x 6373x 546x 451x 451x 46x 3265x 3226x 6865x 6919x 6922x 6919x 2171x 2171x 4748x 3219x 3219x 3219x 692x 692x 692x 39x 39x 3219x 3168x 51x 1529x 509x 509x 509x 840x 840x 840x 840x 509x 1020x 10325x 131x 131x 10194x 10194x 6865x 4705x 3207x 1498x 827x 995x 535x 460x 3652x 7428x 7428x 7428x 131x | /**
* 1.4 Resolve: settle the type names 1.3 Declare could not.
*
* Declare owns per-file facts, so for a bare `T` written inside a scope it can
* only answer when this file declares the scope type. Otherwise the name is
* either a global type or a scope type from an included file, and telling those
* apart needs the scope types the file's include closure declares -- a
* cross-file fact, which is 1.4's to own.
*
* Declare records those sites as `TDeferredType`, carrying the identifier as
* WRITTEN and the scope it was written in. That pair is the input ADR-057
* needs, and recording it is what keeps the decision at the parse tree:
* qualifying a resolved name later cannot work, because by then `global.Mode`
* and a bare `Mode` are the same string.
*
* This is a REBUILD everywhere it can be. Every type-bearing field on a symbol
* is `readonly`, so settling one means constructing a new symbol.
*
* `IScopeSymbol.functions` is the one exception, and it is a real one rather
* than an oversight: it IS type-bearing, and `SymbolRegistry` caches the scope
* object by path, so a rebuilt scope would leave the registry holding the
* unsettled functions. That array is written in place, from the same memo the
* rebuild uses, so both readers end up on one object.
*
* #1722: the settle also stamps each function parameter whose type is opaque
* (`IParameterInfo.isOpaqueHandle`). That too is a fact Declare cannot know --
* whether a typedef ever received a body is decided over every header -- and
* stamping here, where the one settled object is made, is what lets the scope's
* copy carry it: a separate pass would rebuild the function a second time and
* leave `scope.functions` on the unstamped one.
*/
import type TSymbol from "../../types/symbols/TSymbol";
import type TType from "../../types/TType";
import type IParameterInfo from "../../types/symbols/IParameterInfo";
import type IStructFieldSymbol from "../../types/symbols/IStructFieldSymbol";
import type IFunctionSymbol from "../../types/symbols/IFunctionSymbol";
import ScopeUtils from "../../utils/ScopeUtils";
import TypeResolver from "../../utils/TypeResolver";
class DeferredTypes {
/**
* Settle every deferred type in `symbols` against what their file can see.
*
* @param symbols one file's declared symbols, as Declare emitted them
* @param isScopeType whether a QUALIFIED name is a scope type visible from
* the file these symbols belong to: declared there or anywhere in its
* include closure. Not Declare's own set, which cannot see an include, and
* not the whole program's, which also holds siblings the file never
* includes (#1724).
* @param isOpaqueType whether a settled type name is an opaque C typedef, so
* a parameter of it holds a handle (ADR-030, #1722)
*/
static settle(
symbols: ReadonlyArray<TSymbol>,
isScopeType: (qualifiedName: string) => boolean,
isOpaqueType: (typeName: string) => boolean,
): TSymbol[] {
// Memoized on the ORIGINAL object, so a symbol reached twice yields one
// settled object rather than two equal ones. A scope's member function is
// reached twice by construction: `ScopeCollector` pushes it into the file's
// member symbols AND `SymbolRegistry.registerFunction` pushes the same
// object onto `scope.functions`.
const settled = new Map<TSymbol, TSymbol>();
const settleOnce = (symbol: TSymbol): TSymbol => {
const existing = settled.get(symbol);
Iif (existing) {
return existing;
}
const result = DeferredTypes.settleSymbol(
symbol,
isScopeType,
isOpaqueType,
);
settled.set(symbol, result);
return result;
};
const resolved = symbols.map(settleOnce);
// `IScopeSymbol.functions` is type-bearing, and unlike every other container
// here it cannot be REBUILT into agreement: `SymbolRegistry.getOrCreateScope`
// caches scopes by path, so the registry holds this very object and a fresh
// copy would leave it pointing at the unsettled one. The array is written in
// place instead -- the single mutation in a pass that is otherwise a rebuild,
// and the justification is the alias, not convenience.
//
// Only entries THIS file declared are replaced, which is what consulting the
// memo (rather than settling afresh) buys: a scope spanned across files
// (#1333) holds its siblings' functions too, and settling those here would
// overwrite the object their own file already settled -- reintroducing the
// divergence one file over.
for (const symbol of resolved) {
if (symbol.kind !== "scope") {
continue;
}
symbol.functions.forEach((func, index) => {
const already = settled.get(func);
if (already && already !== func) {
symbol.functions[index] = already as IFunctionSymbol;
}
});
}
return resolved;
}
/**
* Does any symbol here still carry a deferred type?
*
* The settlement's own negative control. `getTypeName` throws on a deferred
* type, so an unsettled one would surface as an internal error somewhere in
* codegen with no indication of which pass dropped it; asking here names the
* pass instead.
*/
static hasUnsettled(symbols: ReadonlyArray<TSymbol>): boolean {
return symbols.some((symbol) =>
DeferredTypes.typesOf(symbol).some(DeferredTypes.containsDeferred),
);
}
private static settleSymbol(
symbol: TSymbol,
isScopeType: (qualifiedName: string) => boolean,
isOpaqueType: (typeName: string) => boolean,
): TSymbol {
const settle = (type: TType): TType =>
DeferredTypes.settleType(type, isScopeType);
// Identity is preserved wherever nothing was deferred, which is the common
// case by a wide margin: only a bare name inside a scope can defer, so most
// symbols have nothing to settle. Rebuilding them regardless would allocate
// a copy of the whole symbol table on every run and quietly break any
// consumer comparing by reference.
if (symbol.kind === "variable") {
const type = settle(symbol.type);
return type === symbol.type ? symbol : { ...symbol, type };
}
if (symbol.kind === "function") {
const returnType = settle(symbol.returnType);
let parametersChanged = false;
const parameters = symbol.parameters.map((parameter): IParameterInfo => {
const type = settle(parameter.type);
// #1722: decided here, once, on the settled type -- a bare name that
// settles to a scope type is C-Next's own and never opaque.
const isOpaqueHandle = isOpaqueType(TypeResolver.getTypeName(type));
if (type === parameter.type && !isOpaqueHandle) return parameter;
parametersChanged = true;
return isOpaqueHandle
? { ...parameter, type, isOpaqueHandle }
: { ...parameter, type };
});
if (returnType === symbol.returnType && !parametersChanged) {
return symbol;
}
return { ...symbol, returnType, parameters };
}
if (symbol.kind === "struct") {
let fieldsChanged = false;
const fields = new Map<string, IStructFieldSymbol>();
for (const [name, field] of symbol.fields) {
const type = settle(field.type);
Eif (type === field.type) {
fields.set(name, field);
continue;
}
fieldsChanged = true;
fields.set(name, { ...field, type });
}
return fieldsChanged ? { ...symbol, fields } : symbol;
}
// enum, bitmap and register carry no TType: an enum member holds a value, a
// bitmap's backing type is a constant, and a register member's C type is a
// plain string resolved at declaration. Returned unchanged rather than
// rebuilt, so identity is preserved for everything with nothing to do.
//
// `scope` is NOT in that list, though it was until 37 fixtures proved
// otherwise: `IScopeSymbol.functions` holds `IFunctionSymbol`s, each with a
// `returnType` and parameter types. Its members are settled by `settle`
// above, which is the only place that can see both the scope and the memo
// of what this file already settled.
return symbol;
}
/**
* Settle one type, recursing into an array's element.
*
* ADR-057 is applied here exactly as Declare would have applied it, from the
* WRITTEN identifier and the scope it appeared in -- never from the resolved
* name, which no longer distinguishes a bare reference from a `global.` one.
*/
static settleType(
type: TType,
isScopeType: (qualifiedName: string) => boolean,
): TType {
if (type.kind === "deferred") {
const name = ScopeUtils.qualifyScopeType(
type.name,
type.scopePath,
isScopeType,
);
// Re-classified from the settled name, because which ARM a name belongs
// to -- struct, enum, bitmap -- is decided by the name, and Declare could
// not decide it while the name was still unknown.
return DeferredTypes.classify(name);
}
Iif (type.kind === "array") {
const elementType = DeferredTypes.settleType(
type.elementType,
isScopeType,
);
return elementType === type.elementType ? type : { ...type, elementType };
}
return type;
}
/** Every TType reachable from a symbol, for `hasUnsettled`. */
private static typesOf(symbol: TSymbol): TType[] {
if (symbol.kind === "variable") return [symbol.type];
if (symbol.kind === "function") {
return DeferredTypes.functionTypes(symbol);
}
if (symbol.kind === "struct") {
return [...symbol.fields.values()].map((field) => field.type);
}
// A scope reaches TType only through its member functions -- and that is
// exactly where an unsettled type survived, because the registry aliases
// this array. A control that cannot look here cannot fail on the case it
// exists to catch.
if (symbol.kind === "scope") {
return symbol.functions.flatMap(DeferredTypes.functionTypes);
}
return [];
}
/** A function's own types: its return type and each parameter's. */
private static functionTypes(func: IFunctionSymbol): TType[] {
return [func.returnType, ...func.parameters.map((p) => p.type)];
}
private static containsDeferred(type: TType): boolean {
Iif (type.kind === "deferred") return true;
Iif (type.kind === "array") {
return DeferredTypes.containsDeferred(type.elementType);
}
return false;
}
/**
* Turn a settled name back into a `TType` arm.
*
* Delegated to the classifier Declare uses for every other name rather than
* re-derived here: which arm a name belongs to is one decision, and a second
* copy of that heuristic would drift from it without anything noticing.
*/
private static classify(name: string): TType {
return TypeResolver.resolve(name);
}
}
export default DeferredTypes;
|