Skip to content

Commit

Permalink
refactor: rename "type parameter type" to "type variable" (#1051)
Browse files Browse the repository at this point in the history
### Summary of Changes

Rename the type "type parameter type" to "type variable". This is
shorter, less cumbersome, and better highlights that this type can be
substituted by another type.
  • Loading branch information
lars-reimann authored Apr 15, 2024
1 parent 46145dd commit f6330a1
Show file tree
Hide file tree
Showing 18 changed files with 81 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { SafeDsDocumentationProvider } from '../documentation/safe-ds-documentat
import { SafeDsAnnotations } from '../builtins/safe-ds-annotations.js';
import { isEmpty } from '../../helpers/collections.js';
import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js';
import { NamedType, Type, TypeParameterType } from '../typing/model.js';
import { NamedType, Type, TypeVariable } from '../typing/model.js';
import path from 'path';
import { addLinePrefix, removeLinePrefix } from '../../helpers/strings.js';
import { expandToStringLF } from 'langium/generate';
Expand Down Expand Up @@ -544,7 +544,7 @@ export class SafeDsMarkdownGenerator {
}

private renderType(type: Type, knownPaths: Set<string>): string {
if (type instanceof NamedType && !(type instanceof TypeParameterType)) {
if (type instanceof NamedType && !(type instanceof TypeVariable)) {
const realPath = AstUtils.getDocument(type.declaration).uri.fsPath;
// When generating documentation for the standard library declarations in the `src` folder, references are
// resolved to the `lib` folder. To still create links, we also check this augmented path.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import {
} from '../helpers/nodeProperties.js';
import { SafeDsNodeMapper } from '../helpers/safe-ds-node-mapper.js';
import { SafeDsServices } from '../safe-ds-module.js';
import { ClassType, EnumVariantType, LiteralType, TypeParameterType } from '../typing/model.js';
import { ClassType, EnumVariantType, LiteralType, TypeVariable } from '../typing/model.js';
import type { SafeDsClassHierarchy } from '../typing/safe-ds-class-hierarchy.js';
import { SafeDsTypeComputer } from '../typing/safe-ds-type-computer.js';
import { SafeDsPackageManager } from '../workspace/safe-ds-package-manager.js';
Expand Down Expand Up @@ -244,7 +244,7 @@ export class SafeDsScopeProvider extends DefaultScopeProvider {
let receiverType = this.typeComputer.computeType(node.receiver);
if (receiverType instanceof LiteralType) {
receiverType = this.typeComputer.computeClassTypeForLiteralType(receiverType);
} else if (receiverType instanceof TypeParameterType) {
} else if (receiverType instanceof TypeVariable) {
receiverType = this.typeComputer.computeUpperBound(receiverType);
}

Expand Down
8 changes: 4 additions & 4 deletions packages/safe-ds-lang/src/language/typing/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ export class EnumVariantType extends NamedType<SdsEnumVariant> {
}
}

export class TypeParameterType extends NamedType<SdsTypeParameter> {
export class TypeVariable extends NamedType<SdsTypeParameter> {
override readonly isFullySubstituted = false;

constructor(
Expand All @@ -545,7 +545,7 @@ export class TypeParameterType extends NamedType<SdsTypeParameter> {
override equals(other: unknown): boolean {
if (other === this) {
return true;
} else if (!(other instanceof TypeParameterType)) {
} else if (!(other instanceof TypeVariable)) {
return false;
}

Expand All @@ -564,12 +564,12 @@ export class TypeParameterType extends NamedType<SdsTypeParameter> {
}
}

override withExplicitNullability(isExplicitlyNullable: boolean): TypeParameterType {
override withExplicitNullability(isExplicitlyNullable: boolean): TypeVariable {
if (this.isExplicitlyNullable === isExplicitlyNullable) {
return this;
}

return new TypeParameterType(this.declaration, isExplicitlyNullable);
return new TypeVariable(this.declaration, isExplicitlyNullable);
}
}

Expand Down
24 changes: 12 additions & 12 deletions packages/safe-ds-lang/src/language/typing/safe-ds-type-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
NamedTupleType,
StaticType,
Type,
TypeParameterType,
TypeVariable,
UnionType,
UnknownType,
} from './model.js';
Expand Down Expand Up @@ -58,13 +58,13 @@ export class SafeDsTypeChecker {
}

// Handle type parameter types
if (other instanceof TypeParameterType) {
if (other instanceof TypeVariable) {
if (type.isExplicitlyNullable && !other.isExplicitlyNullable) {
return false;
}

// `T` can always be assigned to `T` or some type parameter it is bounded by
if (type instanceof TypeParameterType && this.typeParameterIsBoundedByTypeParameter(type, other)) {
if (type instanceof TypeVariable && this.typeVariableIsBoundedByTypeVariable(type, other)) {
return true;
}

Expand Down Expand Up @@ -94,22 +94,22 @@ export class SafeDsTypeChecker {
return this.namedTupleTypeIsSubtypeOf(type, other, options);
} else if (type instanceof StaticType) {
return this.staticTypeIsSubtypeOf(type, other, options);
} else if (type instanceof TypeParameterType) {
return this.typeParameterTypeIsSubtypeOf(type, other, options);
} else if (type instanceof TypeVariable) {
return this.typeVariableIsSubtypeOf(type, other, options);
} /* c8 ignore start */ else {
throw new Error(`Unexpected type: ${type.constructor.name}`);
} /* c8 ignore stop */
};

private typeParameterIsBoundedByTypeParameter(type: TypeParameterType, other: TypeParameterType): boolean {
private typeVariableIsBoundedByTypeVariable(type: TypeVariable, other: TypeVariable): boolean {
let current: Type = type;

while (current instanceof TypeParameterType) {
while (current instanceof TypeVariable) {
if (current.declaration === other.declaration) {
return true;
}

current = this.typeComputer().computeUpperBound(current, { stopAtTypeParameterType: true });
current = this.typeComputer().computeUpperBound(current, { stopAtTypeVariable: true });
}

return false;
Expand Down Expand Up @@ -313,7 +313,7 @@ export class SafeDsTypeChecker {
}
}

private typeParameterTypeIsSubtypeOf(type: TypeParameterType, other: Type, options: TypeCheckOptions): boolean {
private typeVariableIsSubtypeOf(type: TypeVariable, other: Type, options: TypeCheckOptions): boolean {
const upperBound = this.typeComputer().computeUpperBound(type);
return this.isSubtypeOf(upperBound, other, options);
}
Expand Down Expand Up @@ -358,7 +358,7 @@ export class SafeDsTypeChecker {
canBeNull = (type: Type): boolean => {
if (type.isExplicitlyNullable) {
return true;
} else if (type instanceof TypeParameterType) {
} else if (type instanceof TypeVariable) {
const upperBound = this.typeComputer().computeUpperBound(type);
return upperBound.isExplicitlyNullable;
} else {
Expand Down Expand Up @@ -392,7 +392,7 @@ export class SafeDsTypeChecker {
/**
* Checks whether {@link type} is some kind of list (with any element type).
*/
isList(type: Type): type is ClassType | TypeParameterType {
isList(type: Type): type is ClassType | TypeVariable {
const listOrNull = this.coreTypes.List(UnknownType).withExplicitNullability(true);

return (
Expand All @@ -407,7 +407,7 @@ export class SafeDsTypeChecker {
/**
* Checks whether {@link type} is some kind of map (with any key/value types).
*/
isMap(type: Type): type is ClassType | TypeParameterType {
isMap(type: Type): type is ClassType | TypeVariable {
const mapOrNull = this.coreTypes.Map(UnknownType, UnknownType).withExplicitNullability(true);

return (
Expand Down
46 changes: 23 additions & 23 deletions packages/safe-ds-lang/src/language/typing/safe-ds-type-computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ import {
StaticType,
Type,
TypeParameterSubstitutions,
TypeParameterType,
TypeVariable,
UnionType,
UnknownType,
} from './model.js';
Expand Down Expand Up @@ -249,7 +249,7 @@ export class SafeDsTypeComputer {
} else if (isSdsSegment(node)) {
return this.computeTypeOfCallableWithManifestTypes(node, state);
} else if (isSdsTypeParameter(node)) {
return this.factory.createTypeParameterType(node, false);
return this.factory.createTypeVariable(node, false);
} /* c8 ignore start */ else {
return UnknownType;
} /* c8 ignore stop */
Expand Down Expand Up @@ -535,7 +535,7 @@ export class SafeDsTypeComputer {

private computeTypeOfIndexedAccess(node: SdsIndexedAccess, state: ComputeTypeState): Type {
const receiverType = this.computeTypeWithRecursionCheck(node.receiver, state);
if (!(receiverType instanceof ClassType) && !(receiverType instanceof TypeParameterType)) {
if (!(receiverType instanceof ClassType) && !(receiverType instanceof TypeVariable)) {
return UnknownType;
}

Expand Down Expand Up @@ -754,19 +754,19 @@ export class SafeDsTypeComputer {
* invalid upper bounds are specified, but are invalid (e.g. because of an unresolved reference or a cycle),
* `unknown` is returned. The result is simplified as much as possible.
*/
computeUpperBound(nodeOrType: SdsTypeParameter | TypeParameterType, options: ComputeUpperBoundOptions = {}): Type {
let type: TypeParameterType;
if (nodeOrType instanceof TypeParameterType) {
computeUpperBound(nodeOrType: SdsTypeParameter | TypeVariable, options: ComputeUpperBoundOptions = {}): Type {
let type: TypeVariable;
if (nodeOrType instanceof TypeVariable) {
type = nodeOrType;
} else {
type = this.computeType(nodeOrType) as TypeParameterType;
type = this.computeType(nodeOrType) as TypeVariable;
}

const result = this.doComputeUpperBound(type, options);
return result.withExplicitNullability(result.isExplicitlyNullable || type.isExplicitlyNullable);
}

private doComputeUpperBound(type: TypeParameterType, options: ComputeUpperBoundOptions): Type {
private doComputeUpperBound(type: TypeVariable, options: ComputeUpperBoundOptions): Type {
const upperBound = type.declaration.upperBound;
if (!upperBound) {
return this.coreTypes.AnyOrNull;
Expand All @@ -775,7 +775,7 @@ export class SafeDsTypeComputer {
const boundType = this.computeType(upperBound);
if (!(boundType instanceof NamedType)) {
return UnknownType;
} else if (options.stopAtTypeParameterType || !(boundType instanceof TypeParameterType)) {
} else if (options.stopAtTypeVariable || !(boundType instanceof TypeVariable)) {
return boundType;
} else {
return this.doComputeUpperBound(boundType, options);
Expand Down Expand Up @@ -937,7 +937,7 @@ export class SafeDsTypeComputer {

// Clamp to upper bound
const upperBound = this.computeUpperBound(typeParameter, {
stopAtTypeParameterType: true,
stopAtTypeVariable: true,
}).substituteTypeParameters(state.substitutions);

if (!this.typeChecker.isSubtypeOf(newSubstitution, upperBound)) {
Expand All @@ -956,7 +956,7 @@ export class SafeDsTypeComputer {
currentVariance: Variance,
state: ComputeSubstitutionsForParametersState,
) {
if (argumentType instanceof TypeParameterType && state.substitutions.has(argumentType.declaration)) {
if (argumentType instanceof TypeVariable && state.substitutions.has(argumentType.declaration)) {
// Can happen for lambdas without manifest parameter types. We gain no information here.
return;
} else if (parameterType instanceof CallableType && argumentType instanceof CallableType) {
Expand Down Expand Up @@ -1029,7 +1029,7 @@ export class SafeDsTypeComputer {
);
}
}
} else if (parameterType instanceof TypeParameterType) {
} else if (parameterType instanceof TypeVariable) {
const currentSubstitution = state.substitutions.get(parameterType.declaration);
const remainingVariance = state.remainingVariances.get(parameterType.declaration);
if (!currentSubstitution) {
Expand Down Expand Up @@ -1076,8 +1076,8 @@ export class SafeDsTypeComputer {
return simplifiedTypes[0]!;
}

// Replace type parameter types by their upper bound
const replacedTypes = this.replaceTypeParameterTypesWithUpperBound(simplifiedTypes);
// Replace type variables by their upper bound
const replacedTypes = this.replaceVariablesWithUpperBound(simplifiedTypes);

// Partition types by their kind
const partitionedTypes = this.partitionTypesLCS(replacedTypes);
Expand Down Expand Up @@ -1130,9 +1130,9 @@ export class SafeDsTypeComputer {
}
}

private replaceTypeParameterTypesWithUpperBound(simplifiedTypes: Type[]) {
private replaceVariablesWithUpperBound(simplifiedTypes: Type[]) {
return simplifiedTypes.map((it) => {
if (it instanceof TypeParameterType) {
if (it instanceof TypeVariable) {
return this.computeUpperBound(it);
} else {
return it;
Expand Down Expand Up @@ -1454,7 +1454,7 @@ export class SafeDsTypeComputer {
const substitutions: TypeParameterSubstitutions = new Map();

for (const typeParameter of typeParameters) {
substitutions.set(typeParameter, this.factory.createTypeParameterType(typeParameter, false));
substitutions.set(typeParameter, this.factory.createTypeVariable(typeParameter, false));
}

return this.factory.createClassType(declaration, substitutions, type.isExplicitlyNullable);
Expand All @@ -1474,7 +1474,7 @@ export class SafeDsTypeComputer {
// See where the type parameters ended up in the computed super type
const invertedTypeParameterMap = new Map<SdsTypeParameter, SdsTypeParameter>();
for (const [key, value] of superType.substitutions) {
if (value instanceof TypeParameterType) {
if (value instanceof TypeVariable) {
invertedTypeParameterMap.set(value.declaration, key);
}
}
Expand Down Expand Up @@ -1557,7 +1557,7 @@ export class SafeDsTypeComputer {
* parameters on parent types get substituted.
*/
computeMatchingSupertype(
type: ClassType | TypeParameterType | undefined,
type: ClassType | TypeVariable | undefined,
target: SdsClass | undefined,
): ClassType | undefined {
// Handle undefined
Expand All @@ -1566,7 +1566,7 @@ export class SafeDsTypeComputer {
}

// Handle type parameter types
if (type instanceof TypeParameterType) {
if (type instanceof TypeVariable) {
const upperBound = this.computeUpperBound(type);
if (upperBound instanceof ClassType) {
return this.computeMatchingSupertype(upperBound, target);
Expand Down Expand Up @@ -1672,10 +1672,10 @@ export class SafeDsTypeComputer {
*/
interface ComputeUpperBoundOptions {
/**
* If `true`, the computation stops at type parameter types and returns them as is. Otherwise, it finds the bounds
* for the type parameter types recursively.
* If `true`, the computation stops at type variables and returns them as is. Otherwise, it finds the bounds for the
* type variable recursively.
*/
stopAtTypeParameterType?: boolean;
stopAtTypeVariable?: boolean;
}

interface ComputeTypeState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
StaticType,
Type,
TypeParameterSubstitutions,
TypeParameterType,
TypeVariable,
UnionType,
} from './model.js';
import { Constant } from '../partialEvaluation/model.js';
Expand Down Expand Up @@ -66,8 +66,8 @@ export class SafeDsTypeFactory {
return new StaticType(this.services, instanceType);
}

createTypeParameterType(declaration: SdsTypeParameter, isExplicitlyNullable: boolean): TypeParameterType {
return new TypeParameterType(declaration, isExplicitlyNullable);
createTypeVariable(declaration: SdsTypeParameter, isExplicitlyNullable: boolean): TypeVariable {
return new TypeVariable(declaration, isExplicitlyNullable);
}

createUnionType(...types: Type[]): UnionType {
Expand Down
8 changes: 4 additions & 4 deletions packages/safe-ds-lang/src/language/validation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
} from '../generated/ast.js';
import { getArguments, getTypeArguments, getTypeParameters, TypeParameter } from '../helpers/nodeProperties.js';
import { SafeDsServices } from '../safe-ds-module.js';
import { ClassType, NamedTupleType, TypeParameterType, UnknownType } from '../typing/model.js';
import { ClassType, NamedTupleType, TypeVariable, UnknownType } from '../typing/model.js';

export const CODE_TYPE_CALLABLE_RECEIVER = 'type/callable-receiver';
export const CODE_TYPE_MISMATCH = 'type/mismatch';
Expand Down Expand Up @@ -135,7 +135,7 @@ export const indexedAccessIndexMustHaveCorrectType = (services: SafeDsServices)
code: CODE_TYPE_MISMATCH,
});
}
} else if (receiverType instanceof ClassType || receiverType instanceof TypeParameterType) {
} else if (receiverType instanceof ClassType || receiverType instanceof TypeVariable) {
const mapType = typeComputer.computeMatchingSupertype(receiverType, coreClasses.Map);
if (mapType) {
const keyType = mapType.getTypeParameterTypeByIndex(0);
Expand Down Expand Up @@ -293,7 +293,7 @@ export const namedTypeTypeArgumentsMustMatchBounds = (services: SafeDsServices)
}

const upperBound = typeComputer
.computeUpperBound(typeParameter, { stopAtTypeParameterType: true })
.computeUpperBound(typeParameter, { stopAtTypeVariable: true })
.substituteTypeParameters(type.substitutions);

if (!typeChecker.isSubtypeOf(typeArgumentType, upperBound)) {
Expand Down Expand Up @@ -400,7 +400,7 @@ export const typeParameterDefaultValueMustMatchUpperBound = (services: SafeDsSer
}

const defaultValueType = typeComputer.computeType(node.defaultValue);
const upperBoundType = typeComputer.computeUpperBound(node, { stopAtTypeParameterType: true });
const upperBoundType = typeComputer.computeUpperBound(node, { stopAtTypeVariable: true });

if (!typeChecker.isSubtypeOf(defaultValueType, upperBoundType)) {
accept('error', `Expected type '${upperBoundType}' but got '${defaultValueType}'.`, {
Expand Down
Loading

0 comments on commit f6330a1

Please sign in to comment.