diff --git a/tslang/lib/TypeScript/MLIRGenGenerics.cpp b/tslang/lib/TypeScript/MLIRGenGenerics.cpp index e09e857a4..f9217efab 100644 --- a/tslang/lib/TypeScript/MLIRGenGenerics.cpp +++ b/tslang/lib/TypeScript/MLIRGenGenerics.cpp @@ -992,7 +992,12 @@ namespace mlirgen if (typeParam.getValue().first->getConstraint()) { - auto reason = testConstraint(location, genericTypeGenContext.typeParamsWithArgs, typeParamValue.first, widenType, instantiateGenContext); + // a type predicate with a failed constraint is a compile-time type test that folds to + // `false` (below), not an error: don't warn for it + auto isTypePredicate = functionGenericTypeInfo->funcType.getNumResults() > 0 + && mlir::isa(functionGenericTypeInfo->funcType.getResult(0)); + auto reason = testConstraint(location, genericTypeGenContext.typeParamsWithArgs, typeParamValue.first, widenType, instantiateGenContext, + /*reportFailure*/ !isTypePredicate); if (reason == Reason::Failure) { LLVM_DEBUG(llvm::dbgs() << "\n!! skip. failed. should be resolved later\n";); @@ -1001,11 +1006,10 @@ namespace mlirgen if (reason == Reason::FailedConstraint) { - if (functionGenericTypeInfo->funcType.getNumResults() > 0 - && mlir::isa(functionGenericTypeInfo->funcType.getResult(0))) + if (isTypePredicate) { return { - mlir::success(), + mlir::success(), mlir_ts::FunctionType::get(builder.getContext(), {}, { getBooleanLiteral(false) }, false), "" }; diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 9b8e88f9d..a35607e16 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -12732,7 +12732,8 @@ class MLIRGenImpl mlir::Type getSecondTypeFromTypeArguments(NodeArray &typeArguments, const GenContext &genContext); Reason testConstraint(mlir::Location location, llvm::StringMap> &pairs, - const ts::TypeParameterDOM::TypePtr &typeParam, mlir::Type type, const GenContext &genContext); + const ts::TypeParameterDOM::TypePtr &typeParam, mlir::Type type, const GenContext &genContext, + bool reportFailure = true); std::tuple zipTypeParameterWithArgument( mlir::Location location, llvm::StringMap> &pairs, diff --git a/tslang/lib/TypeScript/MLIRGenTypes.cpp b/tslang/lib/TypeScript/MLIRGenTypes.cpp index 83fdcf282..524501b66 100644 --- a/tslang/lib/TypeScript/MLIRGenTypes.cpp +++ b/tslang/lib/TypeScript/MLIRGenTypes.cpp @@ -295,7 +295,8 @@ namespace mlirgen } Reason MLIRGenImpl::testConstraint(mlir::Location location, llvm::StringMap> &pairs, - const ts::TypeParameterDOM::TypePtr &typeParam, mlir::Type type, const GenContext &genContext) { + const ts::TypeParameterDOM::TypePtr &typeParam, mlir::Type type, const GenContext &genContext, + bool reportFailure) { // we need to add current type into context to be able to use it in resolving "extends" constraints GenContext constraintGenContext(genContext); for (auto &typeParamWithArg : pairs) @@ -340,8 +341,11 @@ namespace mlirgen LLVM_DEBUG(llvm::dbgs() << "Type " << type << " does extend " << constraintType << ".";); - emitWarning(location, "") << "Type " << to_print(type) << " does not satisfy the constraint " - << to_print(constraintType) << "."; + if (reportFailure) + { + emitWarning(location, "") << "Type " << to_print(type) << " does not satisfy the constraint " + << to_print(constraintType) << "."; + } return Reason::FailedConstraint; } @@ -407,7 +411,7 @@ namespace mlirgen if (constraint) { // we ignore the test result but we need infered types, constraint will be checked later - testConstraint(location, pairs, typeParam, type, genContext); + testConstraint(location, pairs, typeParam, type, genContext, /*reportFailure*/ false); } return {mlir::success(), IsGeneric::False}; diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 092ff24c6..28e752d82 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2758,6 +2758,23 @@ add_test(NAME test-compile-integer-literal-truncation-quiet set_tests_properties(test-compile-integer-literal-truncation-quiet PROPERTIES FAIL_REGULAR_EXPRESSION "warning:|error:|Stack dump|Assertion failed") +# A type predicate whose constraint fails (the DefaultLib's `__is` / `__is_any`) +# folds to `false` without a warning; a real constraint violation warns once, not also from the +# inference pass ("Type 1 ..." beside "Type s32 ..."). +add_test(NAME test-compile-constraint-warning-type-predicate + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/constraint-warning/type_predicate.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/constraint-warning-type-predicate.obj") +set_tests_properties(test-compile-constraint-warning-type-predicate + PROPERTIES FAIL_REGULAR_EXPRESSION "does not satisfy the constraint|warning:|error:|Stack dump|Assertion failed") +add_test(NAME test-compile-constraint-warning-violation + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/constraint-warning/violation.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/constraint-warning-violation.obj") +set_tests_properties(test-compile-constraint-warning-violation + PROPERTIES PASS_REGULAR_EXPRESSION "violation\\.ts:4:25: warning: Type s32 does not satisfy the constraint string\\." + FAIL_REGULAR_EXPRESSION "Type 1 does not satisfy|Stack dump|Assertion failed") + # A switch's case of another kind of primitive (a string for a number) is an error, as it can never # match: the switch's value was cast to the case's type instead. add_test(NAME test-compile-diagnostic-location-switch-case-type diff --git a/tslang/test/tester/constraint-warning/type_predicate.ts b/tslang/test/tester/constraint-warning/type_predicate.ts new file mode 100644 index 000000000..4c7f014fb --- /dev/null +++ b/tslang/test/tester/constraint-warning/type_predicate.ts @@ -0,0 +1,26 @@ +// A type predicate whose constraint fails is a compile-time type test that folds to `false` +// (the DefaultLib's `__is` / `__is_any`): the failure is the intended answer, not a warning. +function isOf(t: T): t is V { return true; } +function isAny(t: V): t is V { return true; } + +function sameType(t: T, v: V) { + if (isOf(t)) { + return "same"; + } + + return "other"; +} + +function keyKind(key: K) { + if (isAny(key)) { + return "any"; + } + + return "typed"; +} + +function main() { + print(sameType("a", 1.5)); + print(sameType("a", "b")); + print(keyKind(1)); +} diff --git a/tslang/test/tester/constraint-warning/violation.ts b/tslang/test/tester/constraint-warning/violation.ts new file mode 100644 index 000000000..734debf19 --- /dev/null +++ b/tslang/test/tester/constraint-warning/violation.ts @@ -0,0 +1,4 @@ +// A generic called with an argument that breaks its constraint warns once, from the final check +// (the inference pass that runs first doesn't report it). +function f(t: T) { return t; } +function main() { print(f(1)); }