From 5048b52be602451f64a227016dfb0da7be034ef7 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Tue, 15 Sep 2026 17:53:07 +0100 Subject: [PATCH] Say which part of a function signature does not match Assigning a function to a function type it does not match reported only the two types: () => s32 is not matching type () => number invalid cast from () => s32 to string | () => number Neither says what is wrong, and the second names the whole union rather than the member that was meant. The return type is the usual reason: an integer literal is s32 here, so `function one() { return 1; }` is `() => s32` and does not match `() => number`. emitFunctionTypeMismatch now names the part that differs - argument count, argument position, or return type - and for a return type points at declaring it: can't assign '() => s32' to '() => number': return type 's32' is not 'number'. Declare the return type to get one, for example 'function f(): number' A function assigned to a union reports against the union's function member instead of the union. Diagnostics only: what compiles and what does not is unchanged. Co-Authored-By: Claude Opus 5 --- tslang/lib/TypeScript/MLIRGenCast.cpp | 68 +++++++++++++++++++++++++-- tslang/lib/TypeScript/MLIRGenImpl.h | 3 ++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/tslang/lib/TypeScript/MLIRGenCast.cpp b/tslang/lib/TypeScript/MLIRGenCast.cpp index 656d5beff..1c3a9f3df 100644 --- a/tslang/lib/TypeScript/MLIRGenCast.cpp +++ b/tslang/lib/TypeScript/MLIRGenCast.cpp @@ -1228,6 +1228,48 @@ namespace mlirgen return std::nullopt; } + // Names the part of the signature that does not match, rather than only the two types. The return + // type is the common one: an integer literal is `s32` here, so a function with an inferred return + // (`function one() { return 1; }` is `() => s32`) does not match `() => number` - hence the hint to + // declare the return type. + void MLIRGenImpl::emitFunctionTypeMismatch(mlir::Location location, mlir::Type valueType, mlir::Type type, MatchResult match) + { + auto inFuncType = mth.GetFunctionType(valueType); + auto resFuncType = mth.GetFunctionType(type); + + auto diag = emitError(location) << "can't assign '" << to_print(valueType) << "' to '" << to_print(type) << "'"; + switch (match.result) + { + case MatchResultType::NotMatchArgCount: + diag << ": it takes " << inFuncType.getNumInputs() << " argument(s), " << resFuncType.getNumInputs() + << " expected"; + break; + case MatchResultType::NotMatchArg: + diag << ": argument #" << (match.index + 1) << " does not match"; + break; + case MatchResultType::NotMatchResultCount: + diag << ": one returns a value and the other does not"; + break; + case MatchResultType::NotMatchResult: + if (match.index < inFuncType.getNumResults() && match.index < resFuncType.getNumResults()) + { + auto inResultType = inFuncType.getResult(match.index); + auto resResultType = resFuncType.getResult(match.index); + diag << ": return type '" << to_print(inResultType) << "' is not '" << to_print(resResultType) + << "'. Declare the return type to get one, for example 'function f(): " + << to_print(resResultType) << "'"; + } + else + { + diag << ": the return types do not match"; + } + + break; + default: + break; + } + } + mlir::LogicalResult MLIRGenImpl::verifyFunctionCastRules(mlir::Location location, mlir::Type type, mlir::Value value, mlir::Type valueType, const GenContext &genContext) { if (mth.isAnyFunctionType(valueType) && mth.isAnyFunctionType(type)) { @@ -1249,10 +1291,10 @@ namespace mlirgen if (!mth.isGenericType(type) && !mth.isGenericType(valueType)) { // test fun types - auto test = mth.TestFunctionTypesMatchWithObjectMethods(location, valueType, type).result == MatchResultType::Match; - if (!test) + auto test = mth.TestFunctionTypesMatchWithObjectMethods(location, valueType, type); + if (test.result != MatchResultType::Match) { - emitError(location) << to_print(valueType) << " is not matching type " << to_print(type); + emitFunctionTypeMismatch(location, valueType, type, test); return mlir::failure(); } } @@ -1288,6 +1330,26 @@ namespace mlirgen && !isa(type) && !isa(type) && !isa(type)) { + // a union is what the function was most likely meant for: say which of its function members + // it does not match, instead of naming the whole union + if (auto unionType = dyn_cast(type)) + { + for (auto memberType : unionType.getTypes()) + { + if (!mth.isAnyFunctionType(memberType)) + { + continue; + } + + auto test = mth.TestFunctionTypesMatchWithObjectMethods(location, valueType, memberType); + if (test.result != MatchResultType::Match) + { + emitFunctionTypeMismatch(location, valueType, memberType, test); + return mlir::failure(); + } + } + } + emitError(location, "invalid cast from ") << to_print(valueType) << " to " << to_print(type); return mlir::failure(); } diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 8256525ee..3ebd90705 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -10915,6 +10915,9 @@ class MLIRGenImpl // constant array literal to an array of another element type, element by element std::optional castConstArrayToArray(mlir::Location location, mlir::Type type, mlir::Value value, mlir::Type valueType, const GenContext &genContext); + // reports which part of a function signature does not match the one it is assigned to + void emitFunctionTypeMismatch(mlir::Location location, mlir::Type valueType, mlir::Type type, MatchResult match); + // optional // TODO: it is in CastLogic as well, review usage and remove from here // but if optional points to interface then it will not work