diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e98a7808656f..a225ebcfa5dd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,7 +28,7 @@ jobs: test_non_bootstrapped: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -49,8 +49,8 @@ jobs: && github.repository == 'lampepfl/dotty' )" steps: - - name: Set JDK 15 as default - run: echo "/usr/lib/jvm/java-15-openjdk-amd64/bin" >> $GITHUB_PATH + - name: Set JDK 16 as default + run: echo "/usr/lib/jvm/java-16-openjdk-amd64/bin" >> $GITHUB_PATH - name: Checkout cleanup script uses: actions/checkout@v2 @@ -72,7 +72,7 @@ jobs: test: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -94,8 +94,8 @@ jobs: )" steps: - - name: Set JDK 15 as default - run: echo "/usr/lib/jvm/java-15-openjdk-amd64/bin" >> $GITHUB_PATH + - name: Set JDK 16 as default + run: echo "/usr/lib/jvm/java-16-openjdk-amd64/bin" >> $GITHUB_PATH - name: Checkout cleanup script uses: actions/checkout@v2 @@ -171,7 +171,7 @@ jobs: community_build_a: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -211,7 +211,7 @@ jobs: community_build_b: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -251,7 +251,7 @@ jobs: test_sbt: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -291,7 +291,7 @@ jobs: test_java8: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -338,7 +338,7 @@ jobs: publish_nightly: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -373,7 +373,7 @@ jobs: nightly_documentation: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -415,7 +415,7 @@ jobs: publish_release: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -498,7 +498,7 @@ jobs: release_documentation: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt @@ -543,7 +543,7 @@ jobs: publish_sbt_release: runs-on: [self-hosted, Linux] container: - image: lampepfl/dotty:2020-11-19 + image: lampepfl/dotty:2021-03-22 options: --cpu-shares 4096 volumes: - ${{ github.workspace }}/../../cache/sbt:/root/.sbt diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 84c606251f34..f5643cc44ddd 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -758,7 +758,7 @@ class JSCodeGen()(using genCtx: Context) { (paramName, paramInfo) <- m.info.paramNamess.flatten.zip(m.info.paramInfoss.flatten) } yield { js.ParamDef(freshLocalIdent(paramName), NoOriginalName, - toIRType(paramInfo), mutable = false, rest = false) + toIRType(paramInfo), mutable = false) } val resultType = toIRType(m.info.resultType) @@ -811,11 +811,16 @@ class JSCodeGen()(using genCtx: Context) { val flags = js.MemberFlags.empty.withMutable(mutable).withNamespace(namespace) - val irTpe = + val irTpe0 = if (isJSClass) genExposedFieldIRType(f) else if (isTopLevelExport) jstpe.AnyType else toIRType(f.info) + // scala-js/#4370 Fields cannot have type NothingType + val irTpe = + if (irTpe0 == jstpe.NothingType) encodeClassType(defn.NothingClass) + else irTpe0 + if (isJSClass && f.isJSExposed) js.JSFieldDef(flags, genExpr(f.jsName)(f.sourcePos), irTpe) :: Nil else @@ -896,7 +901,7 @@ class JSCodeGen()(using genCtx: Context) { val fqcnArg = js.StringLiteral(sym.fullName.toString) val runtimeClassArg = js.ClassOf(toTypeRef(sym.info)) val loadModuleFunArg = - js.Closure(arrow = true, Nil, Nil, genLoadModule(sym), Nil) + js.Closure(arrow = true, Nil, Nil, None, genLoadModule(sym), Nil) val stat = genApplyMethod( genLoadModule(jsdefn.ReflectModule), @@ -924,14 +929,14 @@ class JSCodeGen()(using genCtx: Context) { } yield { val paramType = js.ClassOf(toTypeRef(paramInfo)) val paramDef = js.ParamDef(freshLocalIdent(paramName), - NoOriginalName, jstpe.AnyType, mutable = false, rest = false) + NoOriginalName, jstpe.AnyType, mutable = false) val actualParam = unbox(paramDef.ref, paramInfo) (paramType, paramDef, actualParam) }).unzip3 val paramTypesArray = js.JSArrayConstr(parameterTypes) - val newInstanceFun = js.Closure(arrow = true, Nil, formalParams, { + val newInstanceFun = js.Closure(arrow = true, Nil, formalParams, None, { js.New(encodeClassName(sym), encodeMethodSym(ctor), actualParams) }, Nil) @@ -965,7 +970,7 @@ class JSCodeGen()(using genCtx: Context) { "if their companion module is JS native.", classSym.srcPos) val ctorDef = js.JSMethodDef(js.MemberFlags.empty, - js.StringLiteral("constructor"), Nil, js.Skip())( + js.StringLiteral("constructor"), Nil, None, js.Skip())( OptimizerHints.empty, None) (None, ctorDef) } else { @@ -985,7 +990,7 @@ class JSCodeGen()(using genCtx: Context) { val captureParamsWithJSSuperClass = captureParams.map { params => val jsSuperClassParam = js.ParamDef( js.LocalIdent(JSSuperClassParamName), NoOriginalName, - jstpe.AnyType, mutable = false, rest = false) + jstpe.AnyType, mutable = false) jsSuperClassParam :: params } @@ -1055,6 +1060,9 @@ class JSCodeGen()(using genCtx: Context) { if (primitives.isPrimitive(sym)) { None + } else if (sym.is(Deferred) && currentClassSym.isNonNativeJSClass) { + // scala-js/#4409: Do not emit abstract methods in non-native JS classes + None } else if (sym.is(Deferred)) { Some(js.MethodDef(js.MemberFlags.empty, methodName, originalName, jsParams, toIRType(patchedResultType(sym)), None)( @@ -1153,7 +1161,7 @@ class JSCodeGen()(using genCtx: Context) { val flags = js.MemberFlags.empty.withNamespace(staticNamespace) val thisParamDef = js.ParamDef(thisLocalIdent, thisOriginalName, - jstpe.AnyType, mutable = false, rest = false) + jstpe.AnyType, mutable = false) js.MethodDef(flags, methodName, originalName, thisParamDef :: jsParams, resultIRType, Some(genBody()))( @@ -1222,7 +1230,7 @@ class JSCodeGen()(using genCtx: Context) { private def genParamDef(sym: Symbol, ptpe: jstpe.Type, pos: Position): js.ParamDef = { js.ParamDef(encodeLocalSym(sym)(implicitly, pos, implicitly), - originalNameOfLocal(sym), ptpe, mutable = false, rest = false)(pos) + originalNameOfLocal(sym), ptpe, mutable = false)(pos) } // Generate statements and expressions ------------------------------------- @@ -1930,10 +1938,11 @@ class JSCodeGen()(using genCtx: Context) { val jsClassCaptures = originalClassDef.jsClassCaptures.getOrElse { throw new AssertionError(s"no class captures for anonymous JS class at $pos") } - val js.JSMethodDef(_, _, ctorParams, ctorBody) = constructor.getOrElse { + val js.JSMethodDef(_, _, ctorParams, ctorRestParam, ctorBody) = constructor.getOrElse { throw new AssertionError("No ctor found") } - assert(ctorParams.isEmpty, s"non-empty constructor params for anonymous JS class at $pos") + assert(ctorParams.isEmpty && ctorRestParam.isEmpty, + s"non-empty constructor params for anonymous JS class at $pos") /* The first class capture is always a reference to the super class. * This is enforced by genJSClassCapturesAndConstructor. @@ -1950,8 +1959,8 @@ class JSCodeGen()(using genCtx: Context) { def selfRef(implicit pos: ir.Position) = js.VarRef(selfName)(jstpe.AnyType) - def memberLambda(params: List[js.ParamDef], body: js.Tree)(implicit pos: ir.Position): js.Closure = - js.Closure(arrow = false, captureParams = Nil, params, body, captureValues = Nil) + def memberLambda(params: List[js.ParamDef], restParam: Option[js.ParamDef], body: js.Tree)(implicit pos: ir.Position): js.Closure = + js.Closure(arrow = false, captureParams = Nil, params, restParam, body, captureValues = Nil) val memberDefinitions0 = instanceMembers.toList.map { case fdef: js.FieldDef => @@ -1966,16 +1975,16 @@ class JSCodeGen()(using genCtx: Context) { case mdef: js.JSMethodDef => implicit val pos = mdef.pos - val impl = memberLambda(mdef.args, mdef.body) + val impl = memberLambda(mdef.args, mdef.restParam, mdef.body) js.Assign(js.JSSelect(selfRef, mdef.name), impl) case pdef: js.JSPropertyDef => implicit val pos = pdef.pos val optGetter = pdef.getterBody.map { body => - js.StringLiteral("get") -> memberLambda(params = Nil, body) + js.StringLiteral("get") -> memberLambda(params = Nil, restParam = None, body) } val optSetter = pdef.setterArgAndBody.map { case (arg, body) => - js.StringLiteral("set") -> memberLambda(params = arg :: Nil, body) + js.StringLiteral("set") -> memberLambda(params = arg :: Nil, restParam = None, body) } val descriptor = js.JSObjectConstr( optGetter.toList ::: @@ -2067,7 +2076,7 @@ class JSCodeGen()(using genCtx: Context) { } }.transform(ctorBody, isStat = true) - val closure = js.Closure(arrow = true, jsClassCaptures, Nil, + val closure = js.Closure(arrow = true, jsClassCaptures, Nil, None, js.Block(inlinedCtorStats, selfRef), jsSuperClassValue :: args) js.JSFunctionApply(closure, Nil) } @@ -2519,7 +2528,7 @@ class JSCodeGen()(using genCtx: Context) { ErrorType(msg) } - def genSelect(): js.Tree = + def genSelect(): js.AssignLhs = js.ArraySelect(genArray, genArgs(0))(toIRType(elementType)) if (isArrayGet(code)) { @@ -2643,7 +2652,11 @@ class JSCodeGen()(using genCtx: Context) { jsSuperClassValue: Option[js.Tree] = None)( implicit pos: SourcePosition): js.Tree = { - def noSpread = !args.exists(_.isInstanceOf[js.JSSpread]) + def argsNoSpread: List[js.Tree] = { + assert(!args.exists(_.isInstanceOf[js.JSSpread]), s"Unexpected spread at $pos") + args.asInstanceOf[List[js.Tree]] + } + val argc = args.size // meaningful only for methods that don't have varargs def requireNotSuper(): Unit = { @@ -2654,74 +2667,72 @@ class JSCodeGen()(using genCtx: Context) { def requireNotSpread(arg: js.TreeOrJSSpread): js.Tree = arg.asInstanceOf[js.Tree] - def hasExplicitJSEncoding = { - sym.hasAnnotation(jsdefn.JSNameAnnot) || - sym.hasAnnotation(jsdefn.JSBracketAccessAnnot) || - sym.hasAnnotation(jsdefn.JSBracketCallAnnot) + def genSuperReference(propName: js.Tree): js.AssignLhs = { + jsSuperClassValue.fold[js.AssignLhs] { + genJSSelectOrGlobalRef(receiver, propName) + } { superClassValue => + js.JSSuperSelect(superClassValue, ruleOutGlobalScope(receiver), propName) + } } - val boxedResult = sym.name match { - case JSUnaryOpMethodName(code) if argc == 0 => + def genSelectGet(propName: js.Tree): js.Tree = + genSuperReference(propName) + + def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree = + js.Assign(genSuperReference(propName), value) + + def genCall(methodName: js.Tree, args: List[js.TreeOrJSSpread]): js.Tree = { + jsSuperClassValue.fold[js.Tree] { + genJSMethodApplyOrGlobalRefApply(receiver, methodName, args) + } { superClassValue => + js.JSSuperMethodCall(superClassValue, ruleOutGlobalScope(receiver), methodName, args) + } + } + + val boxedResult = sym.jsCallingConvention match { + case JSCallingConvention.UnaryOp(code) => requireNotSuper() + assert(argc == 0, s"bad argument count ($argc) for unary op at $pos") js.JSUnaryOp(code, ruleOutGlobalScope(receiver)) - case JSBinaryOpMethodName(code) if argc == 1 => + case JSCallingConvention.BinaryOp(code) => requireNotSuper() + assert(argc == 1, s"bad argument count ($argc) for binary op at $pos") js.JSBinaryOp(code, ruleOutGlobalScope(receiver), requireNotSpread(args.head)) - case nme.apply if !hasExplicitJSEncoding => + case JSCallingConvention.Call => requireNotSuper() - if (jsdefn.isJSThisFunctionClass(sym.owner)) + if (sym.owner.isSubClass(jsdefn.JSThisFunctionClass)) js.JSMethodApply(ruleOutGlobalScope(receiver), js.StringLiteral("call"), args) else js.JSFunctionApply(ruleOutGlobalScope(receiver), args) - case _ => - def jsFunName = genExpr(jsNameOf(sym)) - - def genSuperReference(propName: js.Tree): js.Tree = { - jsSuperClassValue.fold[js.Tree] { - genJSSelectOrGlobalRef(receiver, propName) - } { superClassValue => - js.JSSuperSelect(superClassValue, ruleOutGlobalScope(receiver), propName) - } + case JSCallingConvention.Property(jsName) => + argsNoSpread match { + case Nil => + genSelectGet(genExpr(jsName)) + case value :: Nil => + genSelectSet(genExpr(jsName), value) + case _ => + throw new AssertionError(s"property methods should have 0 or 1 non-varargs arguments at $pos") } - def genSelectGet(propName: js.Tree): js.Tree = - genSuperReference(propName) - - def genSelectSet(propName: js.Tree, value: js.Tree): js.Tree = - js.Assign(genSuperReference(propName), value) - - def genCall(methodName: js.Tree, args: List[js.TreeOrJSSpread]): js.Tree = { - jsSuperClassValue.fold[js.Tree] { - genJSMethodApplyOrGlobalRefApply(receiver, methodName, args) - } { superClassValue => - js.JSSuperMethodCall(superClassValue, ruleOutGlobalScope(receiver), methodName, args) - } + case JSCallingConvention.BracketAccess => + argsNoSpread match { + case keyArg :: Nil => + genSelectGet(keyArg) + case keyArg :: valueArg :: Nil => + genSelectSet(keyArg, valueArg) + case _ => + throw new AssertionError(s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments at $pos") } - if (sym.isJSGetter) { - assert(noSpread && argc == 0) - genSelectGet(jsFunName) - } else if (sym.isJSSetter) { - assert(noSpread && argc == 1) - genSelectSet(jsFunName, requireNotSpread(args.head)) - } else if (sym.isJSBracketAccess) { - assert(noSpread && (argc == 1 || argc == 2), - s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments") - (args: @unchecked) match { - case List(keyArg) => - genSelectGet(requireNotSpread(keyArg)) - case List(keyArg, valueArg) => - genSelectSet(requireNotSpread(keyArg), requireNotSpread(valueArg)) - } - } else if (sym.isJSBracketCall) { - val (methodName, actualArgs) = extractFirstArg(args) - genCall(methodName, actualArgs) - } else { - genCall(jsFunName, args) - } + case JSCallingConvention.BracketCall => + val (methodName, actualArgs) = extractFirstArg(args) + genCall(methodName, actualArgs) + + case JSCallingConvention.Method(jsName) => + genCall(genExpr(jsName), args) } if (isStat) { @@ -2734,46 +2745,6 @@ class JSCodeGen()(using genCtx: Context) { } } - private object JSUnaryOpMethodName { - private val map = Map( - nme.UNARY_+ -> js.JSUnaryOp.+, - nme.UNARY_- -> js.JSUnaryOp.-, - nme.UNARY_~ -> js.JSUnaryOp.~, - nme.UNARY_! -> js.JSUnaryOp.! - ) - - def unapply(name: TermName): Option[js.JSUnaryOp.Code] = - map.get(name) - } - - private object JSBinaryOpMethodName { - private val map = Map( - nme.ADD -> js.JSBinaryOp.+, - nme.SUB -> js.JSBinaryOp.-, - nme.MUL -> js.JSBinaryOp.*, - nme.DIV -> js.JSBinaryOp./, - nme.MOD -> js.JSBinaryOp.%, - - nme.LSL -> js.JSBinaryOp.<<, - nme.ASR -> js.JSBinaryOp.>>, - nme.LSR -> js.JSBinaryOp.>>>, - nme.OR -> js.JSBinaryOp.|, - nme.AND -> js.JSBinaryOp.&, - nme.XOR -> js.JSBinaryOp.^, - - nme.LT -> js.JSBinaryOp.<, - nme.LE -> js.JSBinaryOp.<=, - nme.GT -> js.JSBinaryOp.>, - nme.GE -> js.JSBinaryOp.>=, - - nme.ZAND -> js.JSBinaryOp.&&, - nme.ZOR -> js.JSBinaryOp.|| - ) - - def unapply(name: TermName): Option[js.JSBinaryOp.Code] = - map.get(name) - } - /** Extract the first argument in a list of actual arguments. * * This is nothing else than decomposing into head and tail, except that @@ -3075,24 +3046,41 @@ class JSCodeGen()(using genCtx: Context) { case _ => (freshLocalIdent(), NoOriginalName) } val formalCapture = js.ParamDef(formalIdent, originalName, - toIRType(value.tpe), mutable = false, rest = false) + toIRType(value.tpe), mutable = false) val actualCapture = genExpr(value) (formalCapture, actualCapture) } val (formalCaptures, actualCaptures) = formalAndActualCaptures.unzip + val funInterfaceSym = functionalInterface.tpe.typeSymbol + val hasRepeatedParam = { + funInterfaceSym.exists && { + val Seq(samMethodDenot) = funInterfaceSym.info.possibleSamMethods + val samMethod = samMethodDenot.symbol + atPhase(elimRepeatedPhase)(samMethod.info.paramInfoss.flatten.exists(_.isRepeatedParam)) + } + } + val formalParamNames = sym.info.paramNamess.flatten.drop(envSize) val formalParamTypes = sym.info.paramInfoss.flatten.drop(envSize) - val formalParamNamesAndTypes = formalParamNames.zip(formalParamTypes) - val formalAndActualParams = formalParamNamesAndTypes.map { - case (name, tpe) => + val formalParamRepeateds = + if (hasRepeatedParam) (0 until (formalParamTypes.size - 1)).map(_ => false) :+ true + else (0 until formalParamTypes.size).map(_ => false) + + val formalAndActualParams = formalParamNames.lazyZip(formalParamTypes).lazyZip(formalParamRepeateds).map { + (name, tpe, repeated) => val formalParam = js.ParamDef(freshLocalIdent(name.toString), - OriginalName(name.toString), jstpe.AnyType, mutable = false, - rest = false) - val actualParam = unbox(formalParam.ref, tpe) + OriginalName(name.toString), jstpe.AnyType, mutable = false) + val actualParam = + if (repeated) genJSArrayToVarArgs(formalParam.ref)(tree.sourcePos) + else unbox(formalParam.ref, tpe) (formalParam, actualParam) } - val (formalParams, actualParams) = formalAndActualParams.unzip + val (formalAndRestParams, actualParams) = formalAndActualParams.unzip + + val (formalParams, restParam) = + if (hasRepeatedParam) (formalAndRestParams.init, Some(formalAndRestParams.last)) + else (formalAndRestParams, None) val genBody = { val call = if (isStaticCall) { @@ -3107,14 +3095,20 @@ class JSCodeGen()(using genCtx: Context) { box(call, sym.info.finalResultType) } - val funInterfaceSym = functionalInterface.tpe.typeSymbol + val isThisFunction = funInterfaceSym.isSubClass(jsdefn.JSThisFunctionClass) && { + val ok = formalParams.nonEmpty + if (!ok) + report.error("The SAM or apply method for a js.ThisFunction must have a leading non-varargs parameter", tree) + ok + } - if (jsdefn.isJSThisFunctionClass(funInterfaceSym)) { + if (isThisFunction) { val thisParam :: otherParams = formalParams js.Closure( arrow = false, formalCaptures, otherParams, + restParam, js.Block( js.VarDef(thisParam.name, thisParam.originalName, thisParam.ptpe, mutable = false, @@ -3122,11 +3116,9 @@ class JSCodeGen()(using genCtx: Context) { genBody), actualCaptures) } else { - val closure = js.Closure(arrow = true, formalCaptures, formalParams, genBody, actualCaptures) + val closure = js.Closure(arrow = true, formalCaptures, formalParams, restParam, genBody, actualCaptures) - if (jsdefn.isJSFunctionClass(funInterfaceSym)) { - closure - } else { + if (!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym)) { assert(!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym), s"Invalid functional interface $funInterfaceSym reached the back-end") val formalCount = formalParams.size @@ -3134,6 +3126,10 @@ class JSCodeGen()(using genCtx: Context) { val ctorName = MethodName.constructor( jstpe.ClassRef(ClassName("scala.scalajs.js.Function" + formalCount)) :: Nil) js.New(cls, js.MethodIdent(ctorName), List(closure)) + } else { + assert(funInterfaceSym.isJSType, + s"Invalid functional interface $funInterfaceSym reached the back-end") + closure } } } @@ -3894,7 +3890,7 @@ class JSCodeGen()(using genCtx: Context) { } } - private def genAssignableField(sym: Symbol, qualifier: Tree)(implicit pos: SourcePosition): (js.Tree, Boolean) = { + private def genAssignableField(sym: Symbol, qualifier: Tree)(implicit pos: SourcePosition): (js.AssignLhs, Boolean) = { def qual = genExpr(qualifier) if (sym.owner.isNonNativeJSClass) { @@ -3921,17 +3917,27 @@ class JSCodeGen()(using genCtx: Context) { val f = js.JSSelect(genLoadJSConstructor(companionClass), js.StringLiteral(jsName)) (f, true) } else { - val f = - val className = encodeClassName(sym.owner) - val fieldIdent = encodeFieldSym(sym) - val irType = toIRType(sym.info) + val className = encodeClassName(sym.owner) + val fieldIdent = encodeFieldSym(sym) + + /* #4370 Fields cannot have type NothingType, so we box them as + * scala.runtime.Nothing$ instead. They will be initialized with + * `null`, and any attempt to access them will throw a + * `ClassCastException` (generated in the unboxing code). + */ + val (irType, boxed) = toIRType(sym.info) match + case jstpe.NothingType => + (encodeClassType(defn.NothingClass), true) + case ftpe => + (ftpe, false) + val f = if sym.is(JavaStatic) then js.SelectStatic(className, fieldIdent)(irType) else js.Select(qual, className, fieldIdent)(irType) - (f, false) + (f, boxed) } } @@ -4048,7 +4054,7 @@ class JSCodeGen()(using genCtx: Context) { * Otherwise, report a compile error. */ private def genJSSelectOrGlobalRef(qual: MaybeGlobalScope, item: js.Tree)( - implicit pos: SourcePosition): js.Tree = { + implicit pos: SourcePosition): js.AssignLhs = { qual match { case MaybeGlobalScope.NotGlobalScope(qualTree) => js.JSSelect(qualTree, item) diff --git a/compiler/src/dotty/tools/backend/sjs/JSConstructorGen.scala b/compiler/src/dotty/tools/backend/sjs/JSConstructorGen.scala index da4db4760551..25ec8ff53c6b 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSConstructorGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSConstructorGen.scala @@ -24,7 +24,7 @@ object JSConstructorGen { reportError: String => Unit)( implicit pos: Position): js.JSMethodDef = { - val js.JSMethodDef(_, dispatchName, dispatchArgs, dispatchResolution) = + val js.JSMethodDef(_, dispatchName, dispatchArgs, dispatchRestParam, dispatchResolution) = dispatch val jsConstructorBuilder = mkJSConstructorBuilder(ctors, reportError) @@ -50,7 +50,7 @@ object JSConstructorGen { val newBody = js.Block(overloadSelection ::: prePrimaryCtorBody :: primaryCtorBody :: postPrimaryCtorBody :: js.Undefined() :: Nil) - js.JSMethodDef(js.MemberFlags.empty, dispatchName, dispatchArgs, newBody)( + js.JSMethodDef(js.MemberFlags.empty, dispatchName, dispatchArgs, dispatchRestParam, newBody)( dispatch.optimizerHints, None) } diff --git a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index 0322e56b2f41..c73821430098 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -46,8 +46,10 @@ final class JSDefinitions()(using Context) { def JSAnyClass(using Context) = JSAnyType.symbol.asClass @threadUnsafe lazy val JSObjectType: TypeRef = requiredClassRef("scala.scalajs.js.Object") def JSObjectClass(using Context) = JSObjectType.symbol.asClass - @threadUnsafe lazy val JSBaseThisFunctionType: TypeRef = requiredClassRef("scala.scalajs.js.ThisFunction") - def JSBaseThisFunctionClass(using Context) = JSBaseThisFunctionType.symbol.asClass + @threadUnsafe lazy val JSFunctionType: TypeRef = requiredClassRef("scala.scalajs.js.Function") + def JSFunctionClass(using Context) = JSFunctionType.symbol.asClass + @threadUnsafe lazy val JSThisFunctionType: TypeRef = requiredClassRef("scala.scalajs.js.ThisFunction") + def JSThisFunctionClass(using Context) = JSThisFunctionType.symbol.asClass @threadUnsafe lazy val PseudoUnionType: TypeRef = requiredClassRef("scala.scalajs.js.|") def PseudoUnionClass(using Context) = PseudoUnionType.symbol.asClass @@ -66,11 +68,6 @@ final class JSDefinitions()(using Context) { @threadUnsafe lazy val JSDynamicType: TypeRef = requiredClassRef("scala.scalajs.js.Dynamic") def JSDynamicClass(using Context) = JSDynamicType.symbol.asClass - @threadUnsafe lazy val JSFunctionType = (0 to 22).map(n => requiredClassRef("scala.scalajs.js.Function" + n)).toArray - def JSFunctionClass(n: Int)(using Context) = JSFunctionType(n).symbol.asClass - @threadUnsafe lazy val JSThisFunctionType = (0 to 21).map(n => requiredClassRef("scala.scalajs.js.ThisFunction" + n)).toArray - def JSThisFunctionClass(n: Int)(using Context) = JSThisFunctionType(n).symbol.asClass - @threadUnsafe lazy val RuntimeExceptionType: TypeRef = requiredClassRef("java.lang.RuntimeException") def RuntimeExceptionClass(using Context) = RuntimeExceptionType.symbol.asClass @threadUnsafe lazy val JavaScriptExceptionType: TypeRef = requiredClassRef("scala.scalajs.js.JavaScriptException") @@ -236,27 +233,6 @@ final class JSDefinitions()(using Context) { allRefClassesCache } - /** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */ - private def scalajsClassName(cls: Symbol)(using Context): TypeName = - if (cls.isClass && cls.owner == ScalaJSJSPackageClass) cls.asClass.name - else EmptyTypeName - - /** Is the given `cls` a class of the form `scala.scalajs.js.prefixN` where - * `N` is a number. - * - * This is similar to `isVarArityClass` in `Definitions.scala`. - */ - private def isScalaJSVarArityClass(cls: Symbol, prefix: String): Boolean = { - val name = scalajsClassName(cls) - name.startsWith(prefix) && name.toString.drop(prefix.length).forall(_.isDigit) - } - - def isJSFunctionClass(cls: Symbol): Boolean = - isScalaJSVarArityClass(cls, str.Function) - - def isJSThisFunctionClass(cls: Symbol): Boolean = - isScalaJSVarArityClass(cls, "ThisFunction") - /** Definitions related to the treatment of JUnit bootstrappers. */ object junit { @threadUnsafe lazy val TestAnnotType: TypeRef = requiredClassRef("org.junit.Test") diff --git a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala index def0afec0b76..c571a08980a1 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala @@ -390,7 +390,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { None } else { val formalArgsRegistry = new FormalArgsRegistry(1, false) - val List(arg) = formalArgsRegistry.genFormalArgs() + val (List(arg), None) = formalArgsRegistry.genFormalArgs() val body = genExportSameArgc(jsName, formalArgsRegistry, setters.map(Exported.apply), static, None) Some((arg, body)) } @@ -425,7 +425,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { val formalArgsRegistry = new FormalArgsRegistry(minArgc, needsRestParam) // Generate the list of formal parameters - val formalArgs = formalArgsRegistry.genFormalArgs() + val (formalArgs, restParam) = formalArgsRegistry.genFormalArgs() /* Generate the body * We have a fast-path for methods that are not overloaded. In addition to @@ -440,7 +440,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { if (alts.tail.isEmpty) genApplyForSingleExported(formalArgsRegistry, alts.head, static) else genExportMethodMultiAlts(formalArgsRegistry, maxNonRepeatedArgc, alts, jsName, static) - js.JSMethodDef(flags, genExpr(jsName), formalArgs, body)(OptimizerHints.empty, None) + js.JSMethodDef(flags, genExpr(jsName), formalArgs, restParam, body)(OptimizerHints.empty, None) } private def genExportMethodMultiAlts(formalArgsRegistry: FormalArgsRegistry, @@ -855,7 +855,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { implicit val pos: Position = sym.span for ((name, info) <- namesAndInfosNow) yield { js.ParamDef(freshLocalIdent(name.mangledString), NoOriginalName, toIRType(info), - mutable = false, rest = false) + mutable = false) } } @@ -1047,18 +1047,19 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { if (needsRestParam) freshLocalIdent("rest")(NoPosition).name else null - def genFormalArgs()(implicit pos: Position): List[js.ParamDef] = { + def genFormalArgs()(implicit pos: Position): (List[js.ParamDef], Option[js.ParamDef]) = { val fixedParamDefs = fixedParamNames.toList.map { paramName => - js.ParamDef(js.LocalIdent(paramName), NoOriginalName, jstpe.AnyType, mutable = false, rest = false) + js.ParamDef(js.LocalIdent(paramName), NoOriginalName, jstpe.AnyType, mutable = false) } - if (needsRestParam) { - val restParamDef = - js.ParamDef(js.LocalIdent(restParamName), NoOriginalName, jstpe.AnyType, mutable = false, rest = true) - fixedParamDefs :+ restParamDef - } else { - fixedParamDefs + val restParam = { + if (needsRestParam) + Some(js.ParamDef(js.LocalIdent(restParamName), NoOriginalName, jstpe.AnyType, mutable = false)) + else + None } + + (fixedParamDefs, restParam) } def genArgRef(index: Int)(implicit pos: Position): js.Tree = { diff --git a/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala index 7fefe7ff456d..98dae40c3504 100644 --- a/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala @@ -23,8 +23,7 @@ class SJSPlatform()(using Context) extends JavaPlatform { /** Is the SAMType `cls` also a SAM under the rules of the Scala.js back-end? */ override def isSam(cls: ClassSymbol)(using Context): Boolean = defn.isFunctionClass(cls) - || jsDefinitions.isJSFunctionClass(cls) - || jsDefinitions.isJSThisFunctionClass(cls) + || cls.superClass == jsDefinitions.JSFunctionClass /** Is the given class symbol eligible for Java serialization-specific methods? * diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala index 9cb26f6f73a6..c11bc16d85cf 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala @@ -24,6 +24,8 @@ import Types._ import dotty.tools.backend.sjs.JSDefinitions.jsdefn +import org.scalajs.ir.{Trees => js} + /** Additional extensions for `Symbol`s that are only relevant for Scala.js. */ object JSSymUtils { /** The result type for `sym.jsName`. @@ -41,6 +43,55 @@ object JSSymUtils { } } + enum JSCallingConvention { + case Call, BracketAccess, BracketCall + case Method(name: JSName) + case Property(name: JSName) + case UnaryOp(code: js.JSUnaryOp.Code) + case BinaryOp(code: js.JSBinaryOp.Code) + + def displayName(using Context): String = this match { + case Call => "function application" + case BracketAccess => "bracket access" + case BracketCall => "bracket call" + case Method(name) => "method '" + name.displayName + "'" + case Property(name) => "property '" + name.displayName + "'" + case UnaryOp(code) => "unary operator" + case BinaryOp(code) => "binary operator" + } + } + + object JSCallingConvention { + def of(sym: Symbol)(using Context): JSCallingConvention = { + assert(sym.isTerm, s"got non-term symbol: $sym") + + if (isJSBracketAccess(sym)) { + BracketAccess + } else if (isJSBracketCall(sym)) { + BracketCall + } else { + def default = { + val jsName = sym.jsName + if (sym.isJSProperty) Property(jsName) + else Method(jsName) + } + + if (!sym.hasAnnotation(jsdefn.JSNameAnnot)) { + lazy val pc = sym.info.paramNamess.map(_.size).sum + + sym.name match { + case nme.apply => Call + case JSUnaryOpMethodName(code) if pc == 0 => UnaryOp(code) + case JSBinaryOpMethodName(code) if pc == 1 => BinaryOp(code) + case _ => default + } + } else { + default + } + } + } + } + extension (sym: Symbol) { /** Is this symbol a JavaScript type? */ def isJSType(using Context): Boolean = @@ -66,10 +117,9 @@ object JSSymUtils { /** Should this symbol be translated into a JS getter? */ def isJSGetter(using Context): Boolean = { - sym.is(Module) || ( - sym.is(Method) - && sym.info.firstParamTypes.isEmpty - && atPhaseNoLater(erasurePhase)(sym.info.isParameterless)) + sym.is(Module) + || !sym.is(Method) + || (sym.info.firstParamTypes.isEmpty && atPhaseNoLater(erasurePhase)(sym.info.isParameterless)) } /** Should this symbol be translated into a JS setter? */ @@ -108,6 +158,12 @@ object JSSymUtils { } } + def jsCallingConvention(using Context): JSCallingConvention = + JSCallingConvention.of(sym) + + def hasJSCallCallingConvention(using Context): Boolean = + sym.jsCallingConvention == JSCallingConvention.Call + /** Gets the unqualified JS name of the symbol. * * If it is not explicitly specified with an `@JSName` annotation, the @@ -128,4 +184,44 @@ object JSSymUtils { if (sym.isTerm) sym.asTerm.name.unexpandedName.getterName.toString() else sym.name.unexpandedName.stripModuleClassSuffix.toString() } + + private object JSUnaryOpMethodName { + private val map = Map( + nme.UNARY_+ -> js.JSUnaryOp.+, + nme.UNARY_- -> js.JSUnaryOp.-, + nme.UNARY_~ -> js.JSUnaryOp.~, + nme.UNARY_! -> js.JSUnaryOp.! + ) + + def unapply(name: TermName): Option[js.JSUnaryOp.Code] = + map.get(name) + } + + private object JSBinaryOpMethodName { + private val map = Map( + nme.ADD -> js.JSBinaryOp.+, + nme.SUB -> js.JSBinaryOp.-, + nme.MUL -> js.JSBinaryOp.*, + nme.DIV -> js.JSBinaryOp./, + nme.MOD -> js.JSBinaryOp.%, + + nme.LSL -> js.JSBinaryOp.<<, + nme.ASR -> js.JSBinaryOp.>>, + nme.LSR -> js.JSBinaryOp.>>>, + nme.OR -> js.JSBinaryOp.|, + nme.AND -> js.JSBinaryOp.&, + nme.XOR -> js.JSBinaryOp.^, + + nme.LT -> js.JSBinaryOp.<, + nme.LE -> js.JSBinaryOp.<=, + nme.GT -> js.JSBinaryOp.>, + nme.GE -> js.JSBinaryOp.>=, + + nme.ZAND -> js.JSBinaryOp.&&, + nme.ZOR -> js.JSBinaryOp.|| + ) + + def unapply(name: TermName): Option[js.JSBinaryOp.Code] = + map.get(name) + } } diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala index 8fe0083fb61f..9efe57a5c982 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala @@ -298,7 +298,7 @@ object PrepJSExports { case _: ExportDestination.TopLevel => if (sym.is(Lazy)) report.error("You may not export a lazy val to the top level", exportPos) - else if (!sym.is(Accessor) && sym.isTerm && sym.isJSProperty) + else if (sym.is(Method, butNot = Accessor) && sym.isJSProperty) report.error("You may not export a getter or a setter to the top level", exportPos) /* Disallow non-static methods. diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala index e8f2f1395a6e..8e9e11aef048 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala @@ -241,6 +241,32 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP private def transformStatOrExpr(tree: Tree)(using Context): Tree = { tree match { + case Closure(env, call, functionalInterface) => + val tpeSym = functionalInterface.tpe.typeSymbol + if (tpeSym.isJSType) { + def reportError(reasonAndExplanation: String): Unit = { + report.error( + "Using an anonymous function as a SAM for the JavaScript type " + + i"${tpeSym.fullName} is not allowed because " + + reasonAndExplanation, + tree) + } + if (!tpeSym.is(Trait) || tpeSym.asClass.superClass != jsdefn.JSFunctionClass) { + reportError( + "it is not a trait extending js.Function. " + + "Use an anonymous class instead.") + } else if (tpeSym.hasAnnotation(jsdefn.JSNativeAnnot)) { + reportError( + "it is a native JS type. " + + "It is not possible to directly implement it.") + } else if (!tree.tpe.possibleSamMethods.exists(_.symbol.hasJSCallCallingConvention)) { + reportError( + "its single abstract method is not named `apply`. " + + "Use an anonymous class instead.") + } + } + super.transform(tree) + // Validate js.constructorOf[T] case TypeApply(ctorOfTree, List(tpeArg)) if ctorOfTree.symbol == jsdefn.JSPackage_constructorOf => @@ -395,7 +421,8 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP val infoStr = if (sym1.is(Module)) "" else i" of type $info" - i"${if (showLocation) sym1.showLocated else sym1}$infoStr with JS name '${sym.jsName.displayName}'" + val ccStr = s" called from JS as '${sym.jsCallingConvention.displayName}'" + i"${if (showLocation) sym1.showLocated else sym1}$infoStr$ccStr" } def infoString(sym: Symbol): String = infoString0(sym, sym.owner != clsSym) @@ -409,8 +436,8 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP } // Check for overrides with different JS names - issue scala-js/scala-js#1983 - if (overriding.jsName != overridden.jsName) - emitOverrideError("has a different JS name") + if (overriding.jsCallingConvention != overridden.jsCallingConvention) + emitOverrideError("has a different JS calling convention") /* Cannot override a non-@JSOptional with an @JSOptional. Unfortunately * at this point the symbols do not have @JSOptional yet, so we need @@ -584,18 +611,12 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP assert(!sym.isLocalToBlock, i"$tree at ${tree.span}") sym.name match { - case nme.apply if !sym.hasAnnotation(jsdefn.JSNameAnnot) => - if (!sym.is(Method) || sym.isJSGetter) { - report.error( - "A member named apply represents function application in JavaScript. " + - "A parameterless member should be exported as a property. " + - "You must add @JSName(\"apply\")", - sym) - } else if (enclosingOwner is OwnerKind.JSNonNative) { - report.error( - "A non-native JS class cannot declare a method named `apply` without `@JSName`", - sym) - } + case nme.apply if !sym.hasAnnotation(jsdefn.JSNameAnnot) && (!sym.is(Method) || sym.isJSGetter) => + report.error( + "A member named apply represents function application in JavaScript. " + + "A parameterless member should be exported as a property. " + + "You must add @JSName(\"apply\")", + sym) case nme.equals_ if sym.info.matches(defn.Any_equals.info) => report.error( @@ -617,40 +638,75 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP if (sym.isJSSetter) checkSetterSignature(sym, tree, exported = false) - if (sym.isJSBracketAccess) { - if (enclosingOwner is OwnerKind.JSNonNative) { - report.error("@JSBracketAccess is not allowed in non-native JS classes", tree) - } else { - val allParamInfos = sym.info.paramInfoss.flatten - - allParamInfos.size match { - case 1 => - // ok - case 2 => - if (!sym.info.finalResultType.isRef(defn.UnitClass)) - report.error("@JSBracketAccess methods with two parameters must return Unit", tree) - case _ => - report.error("@JSBracketAccess methods must have one or two parameters", tree) - } + if (enclosingOwner is OwnerKind.JSNonNative) { + JSCallingConvention.of(sym) match { + case JSCallingConvention.Property(_) => // checked above + case JSCallingConvention.Method(_) => // no checks needed + + case JSCallingConvention.Call if !sym.is(Deferred) => + report.error("A non-native JS class cannot declare a concrete method named `apply` without `@JSName`", tree) - if (allParamInfos.exists(_.isRepeatedParam)) - report.error("@JSBracketAccess methods may not have repeated parameters", tree) + case JSCallingConvention.Call => // if sym.isDeferred + /* Allow an abstract `def apply` only if the owner is a plausible + * JS function SAM trait. + */ + val owner = sym.owner + val isPlausibleJSFunctionType = { + owner.is(Trait) && + owner.asClass.superClass == jsdefn.JSFunctionClass && + owner.typeRef.possibleSamMethods.map(_.symbol) == Seq(sym) && + !sym.info.isInstanceOf[PolyType] + } + if (!isPlausibleJSFunctionType) { + report.error( + "A non-native JS type can only declare an abstract method named `apply` without `@JSName` " + + "if it is the SAM of a trait that extends js.Function", + tree) + } + + case JSCallingConvention.BracketAccess => + report.error("@JSBracketAccess is not allowed in non-native JS classes", tree) + case JSCallingConvention.BracketCall => + report.error("@JSBracketCall is not allowed in non-native JS classes", tree) + case JSCallingConvention.UnaryOp(_) => + report.error("A non-native JS class cannot declare a method named like a unary operation without `@JSName`", tree) + case JSCallingConvention.BinaryOp(_) => + report.error("A non-native JS class cannot declare a method named like a binary operation without `@JSName`", tree) + } + } else { + def checkNoDefaultOrRepeated(subject: String) = { + if (sym.info.paramInfoss.flatten.exists(_.isRepeatedParam)) + report.error(s"$subject may not have repeated parameters", tree) if (sym.hasDefaultParams) - report.error("@JSBracketAccess methods may not have default parameters", tree) + report.error(s"$subject may not have default parameters", tree) } - } - if (sym.isJSBracketCall) { - if (enclosingOwner is OwnerKind.JSNonNative) { - report.error("@JSBracketCall is not allowed in non-native JS classes", tree) - } else { - // JS bracket calls must have at least one non-repeated parameter - sym.info.stripPoly match { - case mt: MethodType if mt.paramInfos.nonEmpty && !mt.paramInfos.head.isRepeatedParam => - // ok - case _ => - report.error("@JSBracketCall methods must have at least one non-repeated parameter", tree) - } + JSCallingConvention.of(sym) match { + case JSCallingConvention.Property(_) => // checked above + case JSCallingConvention.Method(_) => // no checks needed + case JSCallingConvention.Call => // no checks needed + case JSCallingConvention.UnaryOp(_) => // no checks needed + + case JSCallingConvention.BinaryOp(_) => + checkNoDefaultOrRepeated("methods representing binary operations") + + case JSCallingConvention.BracketAccess => + val paramCount = sym.info.paramNamess.map(_.size).sum + if (paramCount != 1 && paramCount != 2) + report.error("@JSBracketAccess methods must have one or two parameters", tree) + else if (paramCount == 2 && !sym.info.finalResultType.isRef(defn.UnitClass)) + report.error("@JSBracketAccess methods with two parameters must return Unit", tree) + + checkNoDefaultOrRepeated("@JSBracketAccess methods") + + case JSCallingConvention.BracketCall => + // JS bracket calls must have at least one non-repeated parameter + sym.info.stripPoly match { + case mt: MethodType if mt.paramInfos.nonEmpty && !mt.paramInfos.head.isRepeatedParam => + // ok + case _ => + report.error("@JSBracketCall methods must have at least one non-repeated parameter", tree) + } } } diff --git a/project/plugins.sbt b/project/plugins.sbt index 7d89d19568a0..8b88da956299 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,7 +2,7 @@ // // e.g. addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.4.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.0") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.6") diff --git a/sbt-dotty/sbt-test/scala2-compat/erasure-scalajs/build.sbt b/sbt-dotty/sbt-test/scala2-compat/erasure-scalajs/build.sbt index 4186b8167e93..6f30dbadc25b 100644 --- a/sbt-dotty/sbt-test/scala2-compat/erasure-scalajs/build.sbt +++ b/sbt-dotty/sbt-test/scala2-compat/erasure-scalajs/build.sbt @@ -1,8 +1,7 @@ lazy val scala2Lib = project.in(file("scala2Lib")) .enablePlugins(ScalaJSPlugin) .settings( - // TODO: switch to 2.13.5 once we've upgrade sbt-scalajs to 1.5.0 - scalaVersion := "2.13.4" + scalaVersion := "2.13.5" ) lazy val dottyApp = project.in(file("dottyApp")) diff --git a/tests/neg-scalajs/js-custom-function-types-backend.scala b/tests/neg-scalajs/js-custom-function-types-backend.scala new file mode 100644 index 000000000000..df2aaf9faf37 --- /dev/null +++ b/tests/neg-scalajs/js-custom-function-types-backend.scala @@ -0,0 +1,13 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +trait BadThisFunction1 extends js.ThisFunction { + def apply(): Int +} +trait BadThisFunction2 extends js.ThisFunction { + def apply(args: Int*): Int +} +class A { + val badThisFunction1: BadThisFunction1 = () => 42 // error + val badThisFunction2: BadThisFunction2 = args => args.size // error +} diff --git a/tests/neg-scalajs/js-custom-function-types-prepjsinterop.scala b/tests/neg-scalajs/js-custom-function-types-prepjsinterop.scala new file mode 100644 index 000000000000..eb5f51fc31a5 --- /dev/null +++ b/tests/neg-scalajs/js-custom-function-types-prepjsinterop.scala @@ -0,0 +1,24 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +class BadFunctionIsClass extends js.Function { + def apply(x: Int): Int // error +} +trait BadFunctionExtendsNonFunction extends js.Object { + def apply(x: Int): Int // error +} +class SubclassOfFunction extends js.Function +trait BadFunctionExtendsSubclassOfFunction extends SubclassOfFunction { + def apply(x: Int): Int // error +} +trait BadFunctionParametricMethod extends js.Function { + def apply[A](x: A): A // error +} +trait BadFunctionOverloaded extends js.Function { + def apply(x: Int): Int // error + def apply(x: String): String // error +} +trait BadFunctionMultipleAbstract extends js.Function { + def apply(x: Int): Int // error + def foo(x: Int): Int +} diff --git a/tests/neg-scalajs/js-non-native-members.check b/tests/neg-scalajs/js-non-native-members.check index fef81a75ed77..d4ef056b8596 100644 --- a/tests/neg-scalajs/js-non-native-members.check +++ b/tests/neg-scalajs/js-non-native-members.check @@ -1,7 +1,7 @@ -- Error: tests/neg-scalajs/js-non-native-members.scala:5:6 ------------------------------------------------------------ 5 | def apply(arg: Int): Int = arg // error - | ^ - | A non-native JS class cannot declare a method named `apply` without `@JSName` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | A non-native JS class cannot declare a concrete method named `apply` without `@JSName` -- Error: tests/neg-scalajs/js-non-native-members.scala:8:6 ------------------------------------------------------------ 7 | @JSBracketAccess 8 | def foo(index: Int, arg: Int): Int = arg // error diff --git a/tests/neg-scalajs/js-sam-of-non-function-type.scala b/tests/neg-scalajs/js-sam-of-non-function-type.scala new file mode 100644 index 000000000000..a84b411c2220 --- /dev/null +++ b/tests/neg-scalajs/js-sam-of-non-function-type.scala @@ -0,0 +1,38 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +@js.native +trait A1 extends js.Object { + def foo(x: Int): Int +} +trait A2 extends js.Object { + def bar(x: Int): Int +} +class A3 extends js.Function { + def foobar(x: Int): Int +} +class A4 { + val foo: A1 = x => x + 1 // error + val bar: A2 = x => x + 1 // error + val foobar: A3 = x => x + 1 // error +} + +@js.native +trait B1 extends js.Function { + def apply(x: Int): Int +} +@js.native +trait B2 extends js.Function { + def bar(x: Int = 5): Int +} +class B3 { + val foo: B1 = x => x + 1 // error + val bar: B2 = x => x + 1 // error +} + +trait C1 extends js.Function { + def foo(x: Int): Int +} +class C2 { + val foo: C1 = x => x + 1 // error +} diff --git a/tests/neg-scalajs/js-trait-members-override.check b/tests/neg-scalajs/js-trait-members-override.check index c3d0d1dedbe4..f4658c7817b7 100644 --- a/tests/neg-scalajs/js-trait-members-override.check +++ b/tests/neg-scalajs/js-trait-members-override.check @@ -1,30 +1,30 @@ -- Error: tests/neg-scalajs/js-trait-members-override.scala:13:15 ------------------------------------------------------ 13 | override val a1: js.UndefOr[Int] = js.undefined // error | ^ - |error overriding value a1 in class A1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a1'; - | value a1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a1' cannot override a concrete member in a non-native JS trait + |error overriding value a1 in class A1 of type scala.scalajs.js.UndefOr[Int] called from JS as 'property 'a1''; + | value a1 of type scala.scalajs.js.UndefOr[Int] called from JS as 'property 'a1'' cannot override a concrete member in a non-native JS trait -- Error: tests/neg-scalajs/js-trait-members-override.scala:16:15 ------------------------------------------------------ 16 | override def b1: js.UndefOr[Int] = js.undefined // error | ^ - |error overriding method b1 in class A1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b1'; - | method b1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b1' cannot override a concrete member in a non-native JS trait + |error overriding method b1 in class A1 of type => scala.scalajs.js.UndefOr[Int] called from JS as 'property 'b1''; + | method b1 of type => scala.scalajs.js.UndefOr[Int] called from JS as 'property 'b1'' cannot override a concrete member in a non-native JS trait -- Error: tests/neg-scalajs/js-trait-members-override.scala:27:15 ------------------------------------------------------ 27 | override val a: js.UndefOr[Int] = js.undefined // error | ^ - |error overriding value a in class B1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a'; - | value a of type scala.scalajs.js.UndefOr[Int] with JS name 'a' cannot override a concrete member in a non-native JS trait + |error overriding value a in class B1 of type scala.scalajs.js.UndefOr[Int] called from JS as 'property 'a''; + | value a of type scala.scalajs.js.UndefOr[Int] called from JS as 'property 'a'' cannot override a concrete member in a non-native JS trait -- Error: tests/neg-scalajs/js-trait-members-override.scala:28:15 ------------------------------------------------------ 28 | override def b: js.UndefOr[Int] = js.undefined // error | ^ - |error overriding method b in class B1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b'; - | method b of type => scala.scalajs.js.UndefOr[Int] with JS name 'b' cannot override a concrete member in a non-native JS trait + |error overriding method b in class B1 of type => scala.scalajs.js.UndefOr[Int] called from JS as 'property 'b''; + | method b of type => scala.scalajs.js.UndefOr[Int] called from JS as 'property 'b'' cannot override a concrete member in a non-native JS trait -- Error: tests/neg-scalajs/js-trait-members-override.scala:41:15 ------------------------------------------------------ 41 | override val a: js.UndefOr[Int] = js.undefined // error | ^ - |error overriding value a in trait C1 of type scala.scalajs.js.UndefOr[Int] with JS name 'a'; - | value a of type scala.scalajs.js.UndefOr[Int] with JS name 'a' cannot override a concrete member in a non-native JS trait + |error overriding value a in trait C1 of type scala.scalajs.js.UndefOr[Int] called from JS as 'property 'a''; + | value a of type scala.scalajs.js.UndefOr[Int] called from JS as 'property 'a'' cannot override a concrete member in a non-native JS trait -- Error: tests/neg-scalajs/js-trait-members-override.scala:42:15 ------------------------------------------------------ 42 | override def b: js.UndefOr[Int] = js.undefined // error | ^ - |error overriding method b in trait C1 of type => scala.scalajs.js.UndefOr[Int] with JS name 'b'; - | method b of type => scala.scalajs.js.UndefOr[Int] with JS name 'b' cannot override a concrete member in a non-native JS trait + |error overriding method b in trait C1 of type => scala.scalajs.js.UndefOr[Int] called from JS as 'property 'b''; + | method b of type => scala.scalajs.js.UndefOr[Int] called from JS as 'property 'b'' cannot override a concrete member in a non-native JS trait diff --git a/tests/neg-scalajs/jsname-override.check b/tests/neg-scalajs/jsname-override.check index 300dc9a2df8b..cd0a5bbb48e7 100644 --- a/tests/neg-scalajs/jsname-override.check +++ b/tests/neg-scalajs/jsname-override.check @@ -1,80 +1,80 @@ -- Error: tests/neg-scalajs/jsname-override.scala:10:15 ---------------------------------------------------------------- 10 | override def bar() = 1 // error | ^ - | error overriding method bar in class A1 of type (): Int with JS name 'foo'; - | method bar of type (): Int with JS name 'baz' has a different JS name + | error overriding method bar in class A1 of type (): Int called from JS as 'method 'foo''; + | method bar of type (): Int called from JS as 'method 'baz'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:18:15 ---------------------------------------------------------------- 18 | override def bar() = 1 // error | ^ - | error overriding method bar in class B1 of type (): Int with JS name 'foo'; - | method bar of type (): Int with JS name 'bar' has a different JS name + | error overriding method bar in class B1 of type (): Int called from JS as 'method 'foo''; + | method bar of type (): Int called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:26:15 ---------------------------------------------------------------- 26 | override def bar(): String // error | ^ - | error overriding method bar in class C1 of type (): Object with JS name 'foo'; - | method bar of type (): String with JS name 'bar' has a different JS name + | error overriding method bar in class C1 of type (): Object called from JS as 'method 'foo''; + | method bar of type (): String called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:29:15 ---------------------------------------------------------------- 29 | override def bar() = "1" // error | ^ - | error overriding method bar in class C1 of type (): Object with JS name 'foo'; - | method bar of type (): String with JS name 'bar' has a different JS name + | error overriding method bar in class C1 of type (): Object called from JS as 'method 'foo''; + | method bar of type (): String called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:37:15 ---------------------------------------------------------------- 37 | override def bar(): String // error | ^ - | error overriding method bar in class D1 of type (): Object with JS name 'bar'; - | method bar of type (): String with JS name 'foo' has a different JS name + | error overriding method bar in class D1 of type (): Object called from JS as 'method 'bar''; + | method bar of type (): String called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:40:15 ---------------------------------------------------------------- 40 | override def bar() = "1" // error | ^ - | error overriding method bar in class D2 of type (): String with JS name 'foo'; - | method bar of type (): String with JS name 'bar' has a different JS name + | error overriding method bar in class D2 of type (): String called from JS as 'method 'foo''; + | method bar of type (): String called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:48:6 ----------------------------------------------------------------- 48 | def foo: Int // error | ^ - | error overriding method foo in trait E2 of type => Int with JS name 'bar'; - | method foo in class E1 of type => Int with JS name 'foo' has a different JS name + |error overriding method foo in trait E2 of type => Int called from JS as 'property 'bar''; + | method foo in class E1 of type => Int called from JS as 'property 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:57:6 ----------------------------------------------------------------- 57 | def foo: Int // error | ^ - | error overriding method foo in trait F2 of type => Int with JS name 'foo'; - | method foo in class F1 of type => Int with JS name 'bar' has a different JS name + |error overriding method foo in trait F2 of type => Int called from JS as 'property 'foo''; + | method foo in class F1 of type => Int called from JS as 'property 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:66:15 ---------------------------------------------------------------- 66 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in class G1 of type (x: Int): Int with JS name 'bar'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in class G1 of type (x: Int): Int called from JS as 'method 'bar''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:74:15 ---------------------------------------------------------------- 74 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in trait H1 of type (x: Int): Int with JS name 'bar'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in trait H1 of type (x: Int): Int called from JS as 'method 'bar''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:82:6 ----------------------------------------------------------------- 82 | def foo(x: Int): Int // error | ^ - | error overriding method foo in trait I2 of type (x: Int): Int with JS name 'foo'; - | method foo in class I1 of type (x: Int): Int with JS name 'bar' has a different JS name + |error overriding method foo in trait I2 of type (x: Int): Int called from JS as 'method 'foo''; + | method foo in class I1 of type (x: Int): Int called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:85:15 ---------------------------------------------------------------- 85 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in class I1 of type (x: Int): Int with JS name 'bar'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in class I1 of type (x: Int): Int called from JS as 'method 'bar''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:93:6 ----------------------------------------------------------------- 93 | def foo(x: Int): Int // error | ^ - | error overriding method foo in trait J2 of type (x: Int): Int with JS name 'bar'; - | method foo in class J1 of type (x: Int): Int with JS name 'foo' has a different JS name + |error overriding method foo in trait J2 of type (x: Int): Int called from JS as 'method 'bar''; + | method foo in class J1 of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:96:15 ---------------------------------------------------------------- 96 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in trait J2 of type (x: Int): Int with JS name 'bar'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in trait J2 of type (x: Int): Int called from JS as 'method 'bar''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:106:6 ---------------------------------------------------------------- 106 |trait K3 extends K1 with K2 // error | ^ - | error overriding method foo in trait K1 of type => Int with JS name 'foo'; - | method foo in trait K2 of type => Int with JS name 'bar' has a different JS name + |error overriding method foo in trait K1 of type => Int called from JS as 'property 'foo''; + | method foo in trait K2 of type => Int called from JS as 'property 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-override.scala:115:15 --------------------------------------------------------------- 115 |abstract class L3 extends L1 with L2 // error | ^ - | error overriding method foo in trait L1 of type => Int with JS name 'foo'; - | method foo in trait L2 of type => Int with JS name 'bar' has a different JS name + |error overriding method foo in trait L1 of type => Int called from JS as 'property 'foo''; + | method foo in trait L2 of type => Int called from JS as 'property 'bar'' has a different JS calling convention diff --git a/tests/neg-scalajs/jsname-symbol-override.check b/tests/neg-scalajs/jsname-symbol-override.check index 0d62554be32e..363ed8e71262 100644 --- a/tests/neg-scalajs/jsname-symbol-override.check +++ b/tests/neg-scalajs/jsname-symbol-override.check @@ -1,90 +1,90 @@ -- Error: tests/neg-scalajs/jsname-symbol-override.scala:15:15 --------------------------------------------------------- 15 | override def bar() = 1 // error | ^ - | error overriding method bar in class A1 of type (): Int with JS name 'Syms$.sym1'; - | method bar of type (): Int with JS name 'Syms$.sym2' has a different JS name + | error overriding method bar in class A1 of type (): Int called from JS as 'method 'Syms$.sym1''; + | method bar of type (): Int called from JS as 'method 'Syms$.sym2'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:24:15 --------------------------------------------------------- 24 | override def bar() = 1 // error | ^ - | error overriding method bar in class B1 of type (): Int with JS name 'Syms$.sym1'; - | method bar of type (): Int with JS name 'baz' has a different JS name + | error overriding method bar in class B1 of type (): Int called from JS as 'method 'Syms$.sym1''; + | method bar of type (): Int called from JS as 'method 'baz'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:33:15 --------------------------------------------------------- 33 | override def bar() = 1 // error | ^ - | error overriding method bar in class C1 of type (): Int with JS name 'foo'; - | method bar of type (): Int with JS name 'Syms$.sym1' has a different JS name + | error overriding method bar in class C1 of type (): Int called from JS as 'method 'foo''; + | method bar of type (): Int called from JS as 'method 'Syms$.sym1'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:41:15 --------------------------------------------------------- 41 | override def bar() = 1 // error | ^ - | error overriding method bar in class D1 of type (): Int with JS name 'Syms$.sym1'; - | method bar of type (): Int with JS name 'bar' has a different JS name + | error overriding method bar in class D1 of type (): Int called from JS as 'method 'Syms$.sym1''; + | method bar of type (): Int called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:49:15 --------------------------------------------------------- 49 | override def bar(): String // error | ^ - | error overriding method bar in class E1 of type (): Object with JS name 'Syms$.sym1'; - | method bar of type (): String with JS name 'bar' has a different JS name + | error overriding method bar in class E1 of type (): Object called from JS as 'method 'Syms$.sym1''; + | method bar of type (): String called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:52:15 --------------------------------------------------------- 52 | override def bar() = "1" // error | ^ - | error overriding method bar in class E1 of type (): Object with JS name 'Syms$.sym1'; - | method bar of type (): String with JS name 'bar' has a different JS name + | error overriding method bar in class E1 of type (): Object called from JS as 'method 'Syms$.sym1''; + | method bar of type (): String called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:60:15 --------------------------------------------------------- 60 | override def bar(): String // error | ^ - | error overriding method bar in class F1 of type (): Object with JS name 'bar'; - | method bar of type (): String with JS name 'Syms$.sym1' has a different JS name + | error overriding method bar in class F1 of type (): Object called from JS as 'method 'bar''; + | method bar of type (): String called from JS as 'method 'Syms$.sym1'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:63:15 --------------------------------------------------------- 63 | override def bar() = "1" // error | ^ - | error overriding method bar in class F2 of type (): String with JS name 'Syms$.sym1'; - | method bar of type (): String with JS name 'bar' has a different JS name + | error overriding method bar in class F2 of type (): String called from JS as 'method 'Syms$.sym1''; + | method bar of type (): String called from JS as 'method 'bar'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:71:6 ---------------------------------------------------------- 71 | def foo: Int // error | ^ - | error overriding method foo in trait G2 of type => Int with JS name 'Syms$.sym1'; - | method foo in class G1 of type => Int with JS name 'foo' has a different JS name + |error overriding method foo in trait G2 of type => Int called from JS as 'property 'Syms$.sym1''; + | method foo in class G1 of type => Int called from JS as 'property 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:80:6 ---------------------------------------------------------- 80 | def foo: Int // error | ^ - | error overriding method foo in trait H2 of type => Int with JS name 'foo'; - | method foo in class H1 of type => Int with JS name 'Syms$.sym1' has a different JS name + |error overriding method foo in trait H2 of type => Int called from JS as 'property 'foo''; + | method foo in class H1 of type => Int called from JS as 'property 'Syms$.sym1'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:89:15 --------------------------------------------------------- 89 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in class I1 of type (x: Int): Int with JS name 'Syms$.sym1'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in class I1 of type (x: Int): Int called from JS as 'method 'Syms$.sym1''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:97:15 --------------------------------------------------------- 97 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in trait J1 of type (x: Int): Int with JS name 'Syms$.sym1'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in trait J1 of type (x: Int): Int called from JS as 'method 'Syms$.sym1''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:105:6 --------------------------------------------------------- 105 | def foo(x: Int): Int // error | ^ - | error overriding method foo in trait K2 of type (x: Int): Int with JS name 'foo'; - | method foo in class K1 of type (x: Int): Int with JS name 'Syms$.sym1' has a different JS name + |error overriding method foo in trait K2 of type (x: Int): Int called from JS as 'method 'foo''; + | method foo in class K1 of type (x: Int): Int called from JS as 'method 'Syms$.sym1'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:108:15 -------------------------------------------------------- 108 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in class K1 of type (x: Int): Int with JS name 'Syms$.sym1'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in class K1 of type (x: Int): Int called from JS as 'method 'Syms$.sym1''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:116:6 --------------------------------------------------------- 116 | def foo(x: Int): Int // error | ^ - | error overriding method foo in trait L2 of type (x: Int): Int with JS name 'Syms$.sym1'; - | method foo in class L1 of type (x: Int): Int with JS name 'foo' has a different JS name + |error overriding method foo in trait L2 of type (x: Int): Int called from JS as 'method 'Syms$.sym1''; + | method foo in class L1 of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:119:15 -------------------------------------------------------- 119 | override def foo(x: Int): Int = x // error | ^ - | error overriding method foo in trait L2 of type (x: Int): Int with JS name 'Syms$.sym1'; - | method foo of type (x: Int): Int with JS name 'foo' has a different JS name + | error overriding method foo in trait L2 of type (x: Int): Int called from JS as 'method 'Syms$.sym1''; + | method foo of type (x: Int): Int called from JS as 'method 'foo'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:129:6 --------------------------------------------------------- 129 |trait M3 extends M1 with M2 // error | ^ - | error overriding method foo in trait M1 of type => Int with JS name 'foo'; - | method foo in trait M2 of type => Int with JS name 'Syms$.sym1' has a different JS name + |error overriding method foo in trait M1 of type => Int called from JS as 'property 'foo''; + | method foo in trait M2 of type => Int called from JS as 'property 'Syms$.sym1'' has a different JS calling convention -- Error: tests/neg-scalajs/jsname-symbol-override.scala:138:15 -------------------------------------------------------- 138 |abstract class N3 extends N1 with N2 // error | ^ - | error overriding method foo in trait N1 of type => Int with JS name 'foo'; - | method foo in trait N2 of type => Int with JS name 'Syms$.sym1' has a different JS name + |error overriding method foo in trait N1 of type => Int called from JS as 'property 'foo''; + | method foo in trait N2 of type => Int called from JS as 'property 'Syms$.sym1'' has a different JS calling convention