| 1 | /******************************************************************************* |
|---|
| 2 | Copyright 2009 Sun Microsystems, Inc., |
|---|
| 3 | 4150 Network Circle, Santa Clara, California 95054, U.S.A. |
|---|
| 4 | All rights reserved. |
|---|
| 5 | |
|---|
| 6 | U.S. Government Rights - Commercial software. |
|---|
| 7 | Government users are subject to the Sun Microsystems, Inc. standard |
|---|
| 8 | license agreement and applicable provisions of the FAR and its supplements. |
|---|
| 9 | |
|---|
| 10 | Use is subject to license terms. |
|---|
| 11 | |
|---|
| 12 | This distribution may include materials developed by third parties. |
|---|
| 13 | |
|---|
| 14 | Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered |
|---|
| 15 | trademarks of Sun Microsystems, Inc. in the U.S. and other countries. |
|---|
| 16 | ******************************************************************************/ |
|---|
| 17 | |
|---|
| 18 | package com.sun.fortress.scala_src.typechecker.impls |
|---|
| 19 | |
|---|
| 20 | import com.sun.fortress.compiler.index.Method |
|---|
| 21 | import com.sun.fortress.exceptions.StaticError.errorMsg |
|---|
| 22 | import com.sun.fortress.nodes._ |
|---|
| 23 | import com.sun.fortress.nodes_util.NodeFactory |
|---|
| 24 | import com.sun.fortress.nodes_util.NodeUtil |
|---|
| 25 | import com.sun.fortress.scala_src.typechecker._ |
|---|
| 26 | import com.sun.fortress.scala_src.typechecker.ScalaConstraintUtil._ |
|---|
| 27 | import com.sun.fortress.scala_src.nodes._ |
|---|
| 28 | import com.sun.fortress.scala_src.useful.Lists._ |
|---|
| 29 | import com.sun.fortress.scala_src.useful.Options._ |
|---|
| 30 | import com.sun.fortress.scala_src.useful.Sets._ |
|---|
| 31 | import com.sun.fortress.scala_src.useful.SExprUtil._ |
|---|
| 32 | import com.sun.fortress.scala_src.useful.STypesUtil._ |
|---|
| 33 | import com.sun.fortress.useful.NI |
|---|
| 34 | |
|---|
| 35 | /** |
|---|
| 36 | * Provides the implementation of cases relating to functionals and functional |
|---|
| 37 | * application. |
|---|
| 38 | * |
|---|
| 39 | * This trait must be mixed in with an `STypeChecker with Common` instance |
|---|
| 40 | * in order to provide the full type checker implementation. |
|---|
| 41 | * |
|---|
| 42 | * (The self-type annotation at the beginning declares that this trait must be |
|---|
| 43 | * mixed into STypeChecker along with the Common helpers. This is what |
|---|
| 44 | * allows this trait to implement abstract members of STypeChecker and to |
|---|
| 45 | * access its protected members.) |
|---|
| 46 | */ |
|---|
| 47 | trait Functionals { self: STypeChecker with Common => |
|---|
| 48 | |
|---|
| 49 | // --------------------------------------------------------------------------- |
|---|
| 50 | // HELPER METHODS ------------------------------------------------------------ |
|---|
| 51 | |
|---|
| 52 | /** |
|---|
| 53 | * Given a type, which could be a VarType, Intersection or Union, return the TraitTypes |
|---|
| 54 | * that this type could be used as for the purposes of calling methods and fields. |
|---|
| 55 | */ |
|---|
| 56 | protected def traitTypesCallable(typ: Type): Set[TraitType] = typ match { |
|---|
| 57 | case t:TraitType => Set(t) |
|---|
| 58 | |
|---|
| 59 | // Combine all the trait types callable from constituents. |
|---|
| 60 | case typ:IntersectionType => |
|---|
| 61 | conjuncts(typ).filter(NodeUtil.isTraitType).flatMap(traitTypesCallable) |
|---|
| 62 | |
|---|
| 63 | // Get the trait types callable from the upper bounds of this parameter. |
|---|
| 64 | case SVarType(_, name, _) => toOption(env.staticParam(name)) match { |
|---|
| 65 | case Some(s@SStaticParam(_, _, ts, _, _, SKindType(_))) => |
|---|
| 66 | Set(ts:_*).filter(NodeUtil.isTraitType).flatMap(traitTypesCallable) |
|---|
| 67 | case _ => Set.empty[TraitType] |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | case SUnionType(_, ts) => |
|---|
| 71 | signal(typ, errorMsg("You should be able to call methods on this type,", |
|---|
| 72 | "but this is not yet implemented.")) |
|---|
| 73 | Set.empty[TraitType] |
|---|
| 74 | |
|---|
| 75 | case _ => Set.empty[TraitType] |
|---|
| 76 | } |
|---|
| 77 | |
|---|
| 78 | /** |
|---|
| 79 | * Not yet implemented. |
|---|
| 80 | * Waiting for _RewriteFnApp to be implemented. |
|---|
| 81 | */ |
|---|
| 82 | protected def findMethodsInTraitHierarchy(methodName: IdOrOpOrAnonymousName, |
|---|
| 83 | receiverType: Type) |
|---|
| 84 | : Set[Method] = { |
|---|
| 85 | |
|---|
| 86 | val traitTypes = traitTypesCallable(receiverType) |
|---|
| 87 | val ttAsWheres = traitTypes.map(NodeFactory.makeTraitTypeWhere) |
|---|
| 88 | val allMethods = inheritedMethods(ttAsWheres.toList) |
|---|
| 89 | toSet(allMethods.matchFirst(methodName)) |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | /** |
|---|
| 93 | * Determines if the given overloading is dynamically applicable. |
|---|
| 94 | */ |
|---|
| 95 | protected def isDynamicallyApplicable(overloading: Overloading, |
|---|
| 96 | smaArrow: ArrowType, |
|---|
| 97 | inferredStaticArgs: List[StaticArg]) |
|---|
| 98 | : Option[Overloading] = { |
|---|
| 99 | // Is this arrow type applicable. |
|---|
| 100 | def arrowTypeIsApplicable(overloadingType: ArrowType): Option[Type] = { |
|---|
| 101 | val typ = |
|---|
| 102 | // If static args given, then instantiate the overloading first. |
|---|
| 103 | if (inferredStaticArgs.isEmpty) |
|---|
| 104 | overloadingType |
|---|
| 105 | else |
|---|
| 106 | staticInstantiation(inferredStaticArgs, overloadingType). |
|---|
| 107 | getOrElse(return None).asInstanceOf[ArrowType] |
|---|
| 108 | |
|---|
| 109 | if (isSubtype(typ.getDomain, smaArrow.getDomain)) |
|---|
| 110 | Some(typ) |
|---|
| 111 | else |
|---|
| 112 | None |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | // If overloading type is an intersection, check that any of its |
|---|
| 116 | // constituents is applicable. |
|---|
| 117 | val applicableArrows = conjuncts(toOption(overloading.getType).get). |
|---|
| 118 | map(_.asInstanceOf[ArrowType]). |
|---|
| 119 | flatMap(arrowTypeIsApplicable) |
|---|
| 120 | |
|---|
| 121 | val overloadingType = applicableArrows.toList match { |
|---|
| 122 | case Nil => return None |
|---|
| 123 | case t::Nil => t |
|---|
| 124 | case _ => NodeFactory.makeIntersectionType(applicableArrows) |
|---|
| 125 | } |
|---|
| 126 | Some(SOverloading(overloading.getInfo, |
|---|
| 127 | overloading.getUnambiguousName, |
|---|
| 128 | Some(overloadingType))) |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | /** |
|---|
| 132 | * Calls the other overloading with the conjuncts of the given function type. |
|---|
| 133 | */ |
|---|
| 134 | protected def staticallyMostApplicableArrow(fnType: Type, |
|---|
| 135 | argType: Type, |
|---|
| 136 | expectedType: Option[Type]) |
|---|
| 137 | : Option[(ArrowType, List[StaticArg])] = { |
|---|
| 138 | |
|---|
| 139 | val arrows = conjuncts(fnType).toList.map(_.asInstanceOf[ArrowType]) |
|---|
| 140 | staticallyMostApplicableArrow(arrows, argType, expectedType) |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | /** |
|---|
| 144 | * Return the statically most applicable arrow type along with the static args |
|---|
| 145 | * that instantiated that arrow type. This method assumes that all the arrow |
|---|
| 146 | * types in fnType have already been instantiated if any static args were |
|---|
| 147 | * supplied. |
|---|
| 148 | */ |
|---|
| 149 | protected def staticallyMostApplicableArrow(allArrows: List[ArrowType], |
|---|
| 150 | argType: Type, |
|---|
| 151 | expectedType: Option[Type]) |
|---|
| 152 | : Option[(ArrowType, List[StaticArg])] = { |
|---|
| 153 | |
|---|
| 154 | // Filter applicable arrows and their instantiated args. |
|---|
| 155 | val arrowsAndInstantiations = |
|---|
| 156 | allArrows.flatMap(ty => checkApplicable(ty.asInstanceOf[ArrowType], |
|---|
| 157 | argType, |
|---|
| 158 | expectedType)) |
|---|
| 159 | |
|---|
| 160 | // Define an ordering relation on arrows with their instantiations. |
|---|
| 161 | def lessThan(overloading1: (ArrowType, List[StaticArg]), |
|---|
| 162 | overloading2: (ArrowType, List[StaticArg])): Boolean = { |
|---|
| 163 | |
|---|
| 164 | val SArrowType(_, domain1, range1, _, _) = overloading1._1 |
|---|
| 165 | val SArrowType(_, domain2, range2, _, _) = overloading2._1 |
|---|
| 166 | |
|---|
| 167 | if (equivalentTypes(domain1, domain2)) false |
|---|
| 168 | else isSubtype(domain1, domain2) |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | // Sort the arrows and instantiations to find the statically most |
|---|
| 172 | // applicable. Return None if none were applicable. |
|---|
| 173 | arrowsAndInstantiations.sort(lessThan).firstOption |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | /** |
|---|
| 177 | * Checks whether an arrow type if applicable to the given args. If so, then |
|---|
| 178 | * the [possible instantiated] arrow type along with any inferred statics args |
|---|
| 179 | * are returned. |
|---|
| 180 | */ |
|---|
| 181 | protected def checkApplicable(fnType: ArrowType, |
|---|
| 182 | argType: Type, |
|---|
| 183 | expectedType: Option[Type]) |
|---|
| 184 | : Option[(ArrowType, List[StaticArg])] = { |
|---|
| 185 | |
|---|
| 186 | val sparams = getStaticParams(fnType) |
|---|
| 187 | |
|---|
| 188 | // Substitute inference variables for static parameters in fnType. |
|---|
| 189 | |
|---|
| 190 | // 1. build substitution S = [T_i -> $T_i] |
|---|
| 191 | // 2. instantiate fnType with S to get an arrow type with inf vars, infArrow |
|---|
| 192 | val sargs = sparams.map(makeInferenceArg) |
|---|
| 193 | val infArrow = staticInstantiation(sargs, sparams, fnType). |
|---|
| 194 | getOrElse(return None).asInstanceOf[ArrowType] |
|---|
| 195 | |
|---|
| 196 | // 3. argType <:? dom(infArrow) yields a constraint, C1 |
|---|
| 197 | val domainConstraint = checkSubtype(argType, infArrow.getDomain) |
|---|
| 198 | |
|---|
| 199 | // 4. if expectedType given, C := C1 AND range(infArrow) <:? expectedType |
|---|
| 200 | val rangeConstraint = expectedType.map( |
|---|
| 201 | t => checkSubtype(infArrow.getRange, t)).getOrElse(TRUE_FORMULA) |
|---|
| 202 | val constraint = domainConstraint.scalaAnd(rangeConstraint, isSubtype) |
|---|
| 203 | |
|---|
| 204 | // Get an inference variable type out of a static arg. |
|---|
| 205 | def staticArgType(sarg: StaticArg): Option[_InferenceVarType] = sarg match { |
|---|
| 206 | case sarg:TypeArg => Some(sarg.getTypeArg.asInstanceOf) |
|---|
| 207 | case _ => None |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | // 5. build bounds map B = [$T_i -> S(UB(T_i))] |
|---|
| 211 | val infVars = sargs.flatMap(staticArgType) |
|---|
| 212 | val sparamBounds = sparams.flatMap(staticParamBoundType). |
|---|
| 213 | map(t => insertStaticParams(t, sparams)) |
|---|
| 214 | val boundsMap = Map(infVars.zip(sparamBounds): _*) |
|---|
| 215 | |
|---|
| 216 | // 6. solve C to yield a substitution S' = [$T_i -> U_i] |
|---|
| 217 | val subst = constraint.scalaSolve(boundsMap).getOrElse(return None) |
|---|
| 218 | |
|---|
| 219 | // 7. instantiate infArrow with [U_i] to get resultArrow |
|---|
| 220 | val resultArrow = substituteTypesForInferenceVars(subst, infArrow). |
|---|
| 221 | asInstanceOf[ArrowType] |
|---|
| 222 | |
|---|
| 223 | // 8. return (resultArrow,StaticArgs([U_i])) |
|---|
| 224 | val resultArgs = infVars.map((t) => |
|---|
| 225 | NodeFactory.makeTypeArg(resultArrow.getInfo.getSpan, subst.apply(t))) |
|---|
| 226 | Some((resultArrow,resultArgs)) |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | /** |
|---|
| 230 | * Given an applicand, the statically most applicable arrow type for it, |
|---|
| 231 | * and the static args from the application, return the applicand updated |
|---|
| 232 | * with the dynamically applicable overloadings, arrow type, and static args. |
|---|
| 233 | */ |
|---|
| 234 | protected def rewriteApplicand(fn: Expr, |
|---|
| 235 | arrow: ArrowType, |
|---|
| 236 | sargs: List[StaticArg]): Expr = fn match { |
|---|
| 237 | case fn: FunctionalRef => |
|---|
| 238 | |
|---|
| 239 | // Get the dynamically applicable overloadings. |
|---|
| 240 | val overloadings = |
|---|
| 241 | toList(fn.getNewOverloadings). |
|---|
| 242 | flatMap(o => isDynamicallyApplicable(o, arrow, sargs)) |
|---|
| 243 | |
|---|
| 244 | // Add in the filtered overloadings, the inferred static args, |
|---|
| 245 | // and the statically most applicable arrow to the fn. |
|---|
| 246 | addType( |
|---|
| 247 | addStaticArgs( |
|---|
| 248 | addOverloadings(fn, overloadings), sargs), arrow) |
|---|
| 249 | |
|---|
| 250 | case _ if !sargs.isEmpty => |
|---|
| 251 | NI.nyi("No place to put inferred static args in application.") |
|---|
| 252 | |
|---|
| 253 | // Just add the arrow type if the applicand is not a FunctionalRef. |
|---|
| 254 | case _ => addType(fn, arrow) |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | /** |
|---|
| 258 | * Signal a static error for an application for which there were no applicable |
|---|
| 259 | * functions. |
|---|
| 260 | */ |
|---|
| 261 | protected def noApplicableFunctions(application: Expr, |
|---|
| 262 | fn: Expr, |
|---|
| 263 | fnType: Type, |
|---|
| 264 | argType: Type) = { |
|---|
| 265 | val kind = fn match { |
|---|
| 266 | case _:FnRef => "function" |
|---|
| 267 | case _:OpRef => "operator" |
|---|
| 268 | case _ => "" |
|---|
| 269 | } |
|---|
| 270 | val argTypeStr = normalize(argType) match { |
|---|
| 271 | case tt:TupleType => tt.getElements.toString |
|---|
| 272 | case _ => "[" + argType.toString + "]" |
|---|
| 273 | } |
|---|
| 274 | val message = fn match { |
|---|
| 275 | case fn:FunctionalRef => |
|---|
| 276 | val name = fn.getOriginalName |
|---|
| 277 | val sargs = fn.getStaticArgs |
|---|
| 278 | if (sargs.isEmpty) |
|---|
| 279 | "Call to %s %s has invalid arguments, %s". |
|---|
| 280 | format(kind, name, argTypeStr) |
|---|
| 281 | else |
|---|
| 282 | "Call to %s %s with static arguments %s has invalid arguments, %s". |
|---|
| 283 | format(kind, name, sargs, argTypeStr) |
|---|
| 284 | case _ => |
|---|
| 285 | "Expression of type %s is not applicable to argument type %s.". |
|---|
| 286 | format(normalize(fnType), argTypeStr) |
|---|
| 287 | } |
|---|
| 288 | signal(application, message) |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | // --------------------------------------------------------------------------- |
|---|
| 292 | // CHECK IMPLEMENTATION ------------------------------------------------------ |
|---|
| 293 | |
|---|
| 294 | def checkFunctionals(node: Node): Node = node match { |
|---|
| 295 | |
|---|
| 296 | case SOverloading(info, name, _) => { |
|---|
| 297 | val checkedName = check(name).asInstanceOf[IdOrOp] |
|---|
| 298 | getTypeFromName(checkedName) match { |
|---|
| 299 | case Some(checkedType) => |
|---|
| 300 | SOverloading(info, checkedName, Some(checkedType)) |
|---|
| 301 | case None => node |
|---|
| 302 | } |
|---|
| 303 | } |
|---|
| 304 | |
|---|
| 305 | case _ => throw new Error(errorMsg("not yet implemented: ", node.getClass)) |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | // --------------------------------------------------------------------------- |
|---|
| 309 | // CHECKEXPR IMPLEMENTATION -------------------------------------------------- |
|---|
| 310 | |
|---|
| 311 | def checkExprFunctionals(expr: Expr, |
|---|
| 312 | expected: Option[Type]): Expr = expr match { |
|---|
| 313 | |
|---|
| 314 | case SSubscriptExpr(SExprInfo(span, paren, _), obj, subs, op, sargs) => { |
|---|
| 315 | val checkedObj = checkExpr(obj) |
|---|
| 316 | val checkedSubs = subs.map(checkExpr) |
|---|
| 317 | val objType = getType(checkedObj).getOrElse(return expr) |
|---|
| 318 | |
|---|
| 319 | // Convert sub types into a single type or tuple of types. |
|---|
| 320 | if (!haveTypes(checkedSubs)) return expr |
|---|
| 321 | val subsType = checkedSubs.map(s => getType(s).get) match { |
|---|
| 322 | case t :: Nil => t |
|---|
| 323 | case t => |
|---|
| 324 | NodeFactory.makeTupleType(NodeUtil.getSpan(expr), toJavaList(t)) |
|---|
| 325 | } |
|---|
| 326 | |
|---|
| 327 | // Get the methods and arrows from the op. |
|---|
| 328 | val methods = findMethodsInTraitHierarchy(op.get, objType) |
|---|
| 329 | val arrows = |
|---|
| 330 | if (sargs.isEmpty) methods.map(makeArrowFromFunctional) |
|---|
| 331 | else methods.flatMap(m => |
|---|
| 332 | staticInstantiation(sargs, makeArrowFromFunctional(m))). |
|---|
| 333 | map(_.asInstanceOf[ArrowType]) |
|---|
| 334 | |
|---|
| 335 | staticallyMostApplicableArrow(arrows.toList, subsType, None) match { |
|---|
| 336 | case Some((arrow, sargs)) => |
|---|
| 337 | SSubscriptExpr(SExprInfo(span, paren, Some(arrow.getRange)), |
|---|
| 338 | checkedObj, |
|---|
| 339 | checkedSubs, |
|---|
| 340 | op, |
|---|
| 341 | sargs) |
|---|
| 342 | case one => |
|---|
| 343 | signal(expr, "Receiver type %s does not have applicable overloading of %s for argument type %s.". |
|---|
| 344 | format(objType, op.get, subsType)) |
|---|
| 345 | expr |
|---|
| 346 | } |
|---|
| 347 | } |
|---|
| 348 | |
|---|
| 349 | case fn@SFunctionalRef(_, sargs, _, name, _, _, overloadings, _) => { |
|---|
| 350 | // Note that ExprDisambiguator inserts the static args from a |
|---|
| 351 | // FunctionalRef into each of its Overloadings. |
|---|
| 352 | |
|---|
| 353 | // Check all the overloadings and filter out any that have the wrong |
|---|
| 354 | // number or kind of static parameters. |
|---|
| 355 | def rewriteOverloading(o: Overloading): Option[Overloading] = check(o) match { |
|---|
| 356 | case SOverloading(info, name, Some(ty)) => |
|---|
| 357 | staticInstantiation(sargs, ty).map(t => SOverloading(info,name,Some(t))) |
|---|
| 358 | case _ => None |
|---|
| 359 | } |
|---|
| 360 | val checkedOverloadings = overloadings.flatMap(rewriteOverloading) |
|---|
| 361 | |
|---|
| 362 | if (checkedOverloadings.isEmpty) |
|---|
| 363 | signal(expr, errorMsg("Wrong number or kind of static arguments for function: ", |
|---|
| 364 | name)) |
|---|
| 365 | |
|---|
| 366 | // Make the intersection type of all the overloadings. |
|---|
| 367 | val overloadingTypes = checkedOverloadings.map(_.getType.unwrap) |
|---|
| 368 | val intersectionType = |
|---|
| 369 | NodeFactory.makeIntersectionType(NodeUtil.getSpan(fn), |
|---|
| 370 | toJavaList(overloadingTypes)) |
|---|
| 371 | addType(addOverloadings(fn, checkedOverloadings), intersectionType) |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | case S_RewriteFnApp(SExprInfo(span, paren, optType), fn, arg) => { |
|---|
| 375 | val checkedFn = checkExpr(fn) |
|---|
| 376 | val checkedArg = checkExpr(arg) |
|---|
| 377 | |
|---|
| 378 | // Check fn and arg and get their types. |
|---|
| 379 | (getType(checkedFn), getType(checkedArg)) match { |
|---|
| 380 | case (Some(fnType), Some(_)) if !isArrows(fnType) => |
|---|
| 381 | signal(expr, errorMsg("Applicand has a type that is not an arrow: ", |
|---|
| 382 | normalize(fnType))) |
|---|
| 383 | expr |
|---|
| 384 | case (Some(fnType), Some(argType)) => |
|---|
| 385 | |
|---|
| 386 | staticallyMostApplicableArrow(fnType, argType, None) match { |
|---|
| 387 | case Some((smostApp, sargs)) => |
|---|
| 388 | |
|---|
| 389 | // Rewrite the applicand to include the arrow and static args |
|---|
| 390 | // and update the application. |
|---|
| 391 | val newFn = rewriteApplicand(checkedFn, smostApp, sargs) |
|---|
| 392 | S_RewriteFnApp(SExprInfo(span, paren, Some(smostApp.getRange)), newFn, checkedArg) |
|---|
| 393 | |
|---|
| 394 | case None => |
|---|
| 395 | noApplicableFunctions(expr, checkedFn, fnType, argType) |
|---|
| 396 | expr |
|---|
| 397 | } |
|---|
| 398 | |
|---|
| 399 | case _ => expr |
|---|
| 400 | } |
|---|
| 401 | } |
|---|
| 402 | |
|---|
| 403 | case SOpExpr(info, fn, args) => { |
|---|
| 404 | val checkedOp = checkExpr(fn) |
|---|
| 405 | val checkedArgs = args.map(checkExpr) |
|---|
| 406 | val opType = getType(checkedOp).getOrElse(return expr) |
|---|
| 407 | if (!haveTypes(checkedArgs)) return expr |
|---|
| 408 | val argType = |
|---|
| 409 | NodeFactory.makeTupleType(info.getSpan, |
|---|
| 410 | toJavaList(checkedArgs.map(t => getType(t).get))) |
|---|
| 411 | staticallyMostApplicableArrow(opType, argType, None) match { |
|---|
| 412 | case Some((smostApp, sargs)) => |
|---|
| 413 | val newOp = rewriteApplicand(checkedOp,smostApp,sargs).asInstanceOf[OpRef] |
|---|
| 414 | addType(SOpExpr(info, newOp, checkedArgs),smostApp.getRange) |
|---|
| 415 | |
|---|
| 416 | case None => |
|---|
| 417 | noApplicableFunctions(expr, checkedOp, opType, argType) |
|---|
| 418 | expr |
|---|
| 419 | } |
|---|
| 420 | |
|---|
| 421 | } |
|---|
| 422 | |
|---|
| 423 | case _ => throw new Error(errorMsg("Not yet implemented: ", expr.getClass)) |
|---|
| 424 | } |
|---|
| 425 | } |
|---|