Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions server/src/compiler_analyzer/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ import {
import {
getSourceNodeName,
isSourceNodeClassOrInterface,
isSourcePrimitiveType,
PrimitiveType,
SymbolFunction,
SymbolObject,
SymbolType,
Expand Down Expand Up @@ -516,7 +514,7 @@ function analyzeReturn(scope: SymbolScope, nodeReturn: NodeReturn) {
}

const expectedReturn = functionReturn.returnType?.symbolType;
if (expectedReturn instanceof SymbolType && expectedReturn?.definitionSource === PrimitiveType.Void) {
if (expectedReturn instanceof SymbolType && expectedReturn?.identifierText === 'void') {
if (nodeReturn.assign === undefined) return;
diagnostic.addError(getNodeLocation(nodeReturn.nodeRange), `Function does not return a value.`);
} else {
Expand Down Expand Up @@ -727,7 +725,7 @@ function analyzeBuiltinConstructorCaller(
if (constructorType.sourceScope === undefined) return undefined;

if (constructorType.symbolType instanceof SymbolType
&& getSourceNodeName(constructorType.symbolType.definitionSource) === NodeName.Enum) {
&& getSourceNodeName(constructorType.symbolType.sourceNode) === NodeName.Enum) {
// Constructor for enum
const argList = callerArgList.argList;
if (argList.length != 1 || canTypeConvert(
Expand Down Expand Up @@ -787,7 +785,7 @@ function analyzeExprPostOp1(scope: SymbolScope, exprPostOp: NodeExprPostOp1, exp
const identifier = isMemberMethod ? member.identifier : member;
if (identifier === undefined) return undefined;

if (isSourceNodeClassOrInterface(exprValue.symbolType.definitionSource) === false) {
if (isSourceNodeClassOrInterface(exprValue.symbolType.sourceNode) === false) {
diagnostic.addError(identifier.location, `'${identifier.text}' is not a member.`);
return undefined;
}
Expand Down Expand Up @@ -1130,7 +1128,7 @@ function analyzeOperatorAlias(
return undefined;
}

if (isSourcePrimitiveType(lhs.symbolType.definitionSource)) {
if (lhs.symbolType.isSystemType()) {
diagnostic.addError(
operator.location,
`Operator '${alias}' of '${stringifyResolvedType(lhs)}' is not defined.`);
Expand Down Expand Up @@ -1177,7 +1175,7 @@ function analyzeBitOp(
assert(alias !== undefined);

// If the left-hand side is a primitive type, use the operator of the right-hand side type
return lhs.symbolType instanceof SymbolType && isSourcePrimitiveType(lhs.symbolType.definitionSource)
return lhs.symbolType instanceof SymbolType && lhs.symbolType.isSystemType()
? analyzeOperatorAlias(scope, operator, rhs, lhs, rightRange, leftRange, alias[1])
: analyzeOperatorAlias(scope, operator, lhs, rhs, leftRange, rightRange, alias[0]);
}
Expand Down Expand Up @@ -1207,7 +1205,7 @@ function analyzeMathOp(
assert(alias !== undefined);

// If the left-hand side is a primitive type, use the operator of the right-hand side type
return lhs.symbolType instanceof SymbolType && isSourcePrimitiveType(lhs.symbolType.definitionSource)
return lhs.symbolType instanceof SymbolType && lhs.symbolType.isSystemType()
? analyzeOperatorAlias(scope, operator, rhs, lhs, rightRange, leftRange, alias[1])
: analyzeOperatorAlias(scope, operator, lhs, rhs, leftRange, rightRange, alias[0]);
}
Expand Down Expand Up @@ -1268,7 +1266,7 @@ function analyzeAssignOp(
): ResolvedType | undefined {
if (lhs === undefined || rhs === undefined) return undefined;
if (lhs.symbolType instanceof SymbolType && rhs.symbolType instanceof SymbolType) {
if (lhs.symbolType.definitionSource === PrimitiveType.Number && rhs.symbolType.definitionSource === PrimitiveType.Number) return lhs;
if (lhs.symbolType.isNumberType() && rhs.symbolType.isNumberType()) return lhs;
}

if (operator.text === '=') {
Expand Down
100 changes: 100 additions & 0 deletions server/src/compiler_analyzer/checkConversion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Check if the source type can be converted to the destination type.
* @param src
* @param dest
*/
import {ResolvedType} from "./resolvedType";
import {SymbolFunction} from "./symbolObject";

export enum ConversionType {
Implicit = 'Implicit', // asIC_IMPLICIT_CONV
ExplicitRefCast = 'ExplicitRefCast', // asIC_EXPLICIT_REF_CAST
ExplicitValueCast = 'ExplicitValue', // asIC_EXPLICIT_VAL_CAST
}

enum ConversionConst {
NoConv = 0,
ConstConv = 1,
EnumSameSizeConv = 2,
EnumDiffSizeConv = 3,
PrimitiveSizeUpConv = 4,
PrimitiveSizeDownConv = 5,
SignedToUnsignedConv = 6,
UnsignedToSignedConv = 7,
IntToFloatConv = 8,
FloatToIntConv = 9,
RefConv = 10,
ObjToPrimitiveConv = 12,
ToObjectConv = 14,
VariableConv = 16,

Unknown = 255,
}

export function evaluateConversionCost(
src: ResolvedType | undefined,
dest: ResolvedType | undefined,
type: ConversionType = ConversionType.Implicit
) {
if (src === undefined || dest === undefined) return ConversionConst.Unknown;

const srcType = src.symbolType;
const destType = dest.symbolType;

if (srcType instanceof SymbolFunction || destType instanceof SymbolFunction) {
// TODO
return ConversionConst.NoConv;
}

// FIXME: Handle init list?

if (srcType.identifierText === 'void') return ConversionConst.NoConv;

// FIXME?
if (srcType.identifierText === '?') return ConversionConst.VariableConv;
if (srcType.identifierText === 'auto') return ConversionConst.VariableConv;

if (destType.isSystemType()) {
// Destination is a primitive type
if (srcType.isSystemType()) {
// Source is a primitive type
return evaluateConvPrimitiveToPrimitive(src, dest, type);
} else {
// Source is an object type
return evaluateConvObjectToPrimitive(src, dest, type);
}
} else {
// Destination is a user-defined type
// TODO
}

return ConversionConst.NoConv;
}

function evaluateConvPrimitiveToPrimitive(
src: ResolvedType,
dest: ResolvedType,
type: ConversionType,
) {
// const srcType = src.symbolType.identifierText;
return ConversionConst.PrimitiveSizeUpConv;
}

// TODO: Use this for evaluating object to primitive
const numberConversionCostTable = new Map<string, string[]>([
['double', ['float', 'int64', 'uint64', 'int', 'uint', 'int16', 'uint16', 'int8', 'uint8']],
['float', ['double', 'int64', 'uint64', 'int', 'uint', 'int16', 'uint16', 'int8', 'uint8']],
['int64', ['uint64', 'int', 'uint', 'int16', 'uint16', 'int8', 'uint8', 'double', 'float']],
['uint64', ['int64', 'uint', 'int', 'uint16', 'int16', 'uint8', 'int8', 'double', 'float']],
['int', ['uint', 'int64', 'uint64', 'int16', 'uint16', 'int8', 'uint8', 'double', 'float']],
['uint', ['int', 'uint64', 'int64', 'uint16', 'int16', 'uint8', 'int8', 'double', 'float']],
['int16', ['uint16', 'int', 'uint', 'int64', 'uint64', 'int8', 'uint8', 'double', 'float']],
['uint16', ['int16', 'uint', 'int', 'uint64', 'int64', 'uint8', 'int8', 'double', 'float']],
['int8', ['uint8', 'int16', 'uint16', 'int', 'uint', 'int64', 'uint64', 'double', 'float']],
['uint8', ['int8', 'uint16', 'int16', 'uint', 'int', 'uint64', 'int64', 'double', 'float']],
]);

function evaluateConvObjectToPrimitive(src: ResolvedType, dest: ResolvedType, type: ConversionType) {
// TODO
return ConversionConst.ObjToPrimitiveConv;
}
83 changes: 44 additions & 39 deletions server/src/compiler_analyzer/checkType.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {
isSourcePrimitiveType,
PrimitiveType,
DefinitionSource,
TypeSourceNode,
SymbolFunction,
SymbolObject,
SymbolType,
SymbolType, isSourceNodeClassOrInterface,
} from "./symbolObject";
import {AccessModifier, NodeName, ParsedRange} from "../compiler_parser/nodes";
import {getNodeLocation} from "../compiler_parser/nodesUtils";
Expand All @@ -14,6 +12,7 @@ import assert = require("assert");
import {findSymbolShallowly, resolveTemplateType, stringifyResolvedType} from "./symbolUtils";
import {getGlobalSettings} from "../code/settings";
import {ResolvedType} from "./resolvedType";
import {isSameToken} from "../compiler_tokenizer/tokenUtils";

/**
* Check if the source type can be converted to the destination type.
Expand All @@ -29,7 +28,9 @@ export function checkTypeMatch(
): boolean {
if (canTypeConvert(src, dest)) return true;

diagnostic.addError(getNodeLocation(nodeRange), `'${stringifyResolvedType(src)}' cannot be converted to '${stringifyResolvedType(dest)}'.`);
diagnostic.addError(
getNodeLocation(nodeRange),
`'${stringifyResolvedType(src)}' cannot be converted to '${stringifyResolvedType(dest)}'.`);
return false;
}

Expand Down Expand Up @@ -67,27 +68,27 @@ function isTypeMatchInternal(

// Are we trying to pass something into ?
if (destType instanceof SymbolType)
if (destType.definitionSource === PrimitiveType.Any) return true;
if (destType.identifierText === '?') return true;

// if (dest.isHandler === false) return false; // FIXME: Handler Checking?
return isFunctionHandlerMatch(srcType, destType);
} else if (destType instanceof SymbolFunction) {
return false;
}

const srcNode = srcType.definitionSource;
const destNode = destType.definitionSource;
const srcNode = srcType.sourceNode;
const destNode = destType.sourceNode;

if (destNode === PrimitiveType.Any || destNode === PrimitiveType.Auto) return true;
if (destType.identifierText === '?' || destType.identifierText === 'auto') return true;

if (isSourcePrimitiveType(srcNode)) {
if (srcType.isSystemType()) {
// Succeeds if it can be cast from one primitive type to another primitive type.
if (canCastFromPrimitiveType(srcType, destType)) return true;
} else {
// Succeeds if they both point to the same type.
if (srcType.declaredPlace === destType.declaredPlace) return true;

if (srcNode.nodeName === NodeName.Enum && destNode === PrimitiveType.Number) return true;
if (srcNode?.nodeName === NodeName.Enum && destType.isNumberType()) return true;

// Succeeds if any of the inherited types in the source match the destination.
if (canDownCast(srcType, destType)) return true;
Expand All @@ -104,7 +105,7 @@ function isTypeMatchInternal(
}

// Fails if the destination type is not a class.
if (isSourcePrimitiveType(destNode) || destNode.nodeName !== NodeName.Class) return false;
if (destType.isSystemType() || destNode?.nodeName !== NodeName.Class) return false;

// Determine if it matches the constructor.
const destIdentifier = destNode.identifier.text;
Expand All @@ -127,12 +128,12 @@ function isFunctionHandlerMatch(srcType: SymbolFunction, destType: SymbolType |
function canDownCast(
srcType: SymbolType, destType: SymbolType
): boolean {
const srcNode = srcType.definitionSource;
if (isSourcePrimitiveType(srcNode)) return false;
const srcNode = srcType.sourceNode;
if (srcType.isSystemType()) return false;

if (srcType.definitionSource === destType.definitionSource) return true;
if (srcType.sourceNode === destType.sourceNode) return true;

if (srcNode.nodeName === NodeName.Class || srcNode.nodeName === NodeName.Interface) {
if (isSourceNodeClassOrInterface(srcNode)) {
if (srcType.baseList === undefined) return false;
for (const srcBase of srcType.baseList) {
if (srcBase?.symbolType === undefined) continue;
Expand All @@ -145,9 +146,8 @@ function canDownCast(
}

// Judge if the class has a metadata that indicates it is a built-in string type.
function isSourceBuiltinString(source: DefinitionSource): boolean {
if (isSourcePrimitiveType(source)) return false;

function isSourceBuiltinString(source: TypeSourceNode | undefined): boolean {
if (source === undefined) return false;
if (source.nodeName != NodeName.Class) return false;

const builtinStringMetadata = "BuiltinString";
Expand All @@ -157,30 +157,33 @@ function isSourceBuiltinString(source: DefinitionSource): boolean {
function canCastFromPrimitiveType(
srcType: SymbolType, destType: SymbolType
) {
const srcNode = srcType.definitionSource;
const destNode = destType.definitionSource;
const srcNode = srcType.sourceNode;
const destNode = destType.sourceNode;

if (srcType.isTypeParameter) {
return destType.isTypeParameter && isSameToken(srcType.declaredPlace, destType.declaredPlace);
}

switch (srcNode) {
case PrimitiveType.Template:
return destNode === PrimitiveType.Template && srcType.declaredPlace === destType.declaredPlace;
case PrimitiveType.String: {
if (srcType.identifierText === 'string') { // TODO: fix this
const destName = destType.declaredPlace.text;
if (isSourceBuiltinString(destNode)) return true;
return getGlobalSettings().builtinStringTypes.includes(destName);
}
case PrimitiveType.Void:

if (srcType.identifierText === 'void') {
return false;
case PrimitiveType.Number:
return destType.definitionSource === PrimitiveType.Number;
case PrimitiveType.Bool:
return destType.definitionSource === PrimitiveType.Bool;
case PrimitiveType.Any:
return true;
case PrimitiveType.Auto:
return true;
default:
assert(false);
}

if (srcType.isNumberType()) {
return destType.isNumberType();
}

if (srcType.identifierText === 'bool') {
return destType.identifierText === 'bool';
}

// FIXME?
return true;
}

function canConstructImplicitly(
Expand All @@ -198,16 +201,18 @@ function canConstructImplicitly(
const constructor = findSymbolShallowly(constructorScope, destIdentifier);
if (constructor === undefined || constructor instanceof SymbolFunction === false) return false;

return canConstructBy(constructor, srcType.definitionSource);
if (srcType.sourceNode === undefined) return true; // FIXME?

return canConstructBy(constructor, srcType.sourceNode);
}

function canConstructBy(constructor: SymbolFunction, srcType: DefinitionSource): boolean {
function canConstructBy(constructor: SymbolFunction, srcType: TypeSourceNode): boolean {
// Succeeds if the constructor has one argument and that argument matches the source type.
if (constructor.parameterTypes.length === 1) {
const paramType = constructor.parameterTypes[0];
if (paramType !== undefined
&& paramType.symbolType instanceof SymbolType
&& paramType.symbolType.definitionSource === srcType
&& paramType.symbolType.sourceNode === srcType
) {
return true;
}
Expand Down
Loading