summaryrefslogtreecommitdiff
path: root/src/reflect
Commit message (Collapse)AuthorAgeFilesLines
* SI-7694 Add @uncheckedBounds to the libraryJason Zaugg2013-08-131-0/+13
| | | | | | | | | Followup to the previous commit that added the compiler support for opting out of bounds checking. With both pieces, we can test that the temporaries introduced by the named/default arguments transform don't trigger bounds violations.
* SI-7694 @uncheckedBounds, an opt-out from type bounds checkingJason Zaugg2013-08-132-0/+7
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Synthetic defs introduced by transforms like named/default arguments, ANF (in scala-async) often introduce a type tree (the tpt of the temporary) that are based on the types of expressions. These types are scrutinized in RefChecks to check that type parameter bounds are satisfied. However, the type of the expression might be based on slack a LUB that fails to capture constraints between type parameters. This slackness is noted in `mergePrefixAndArgs`: // Martin: I removed this, because incomplete. Not sure there is a // good way to fix it. For the moment we just err on the conservative // side, i.e. with a bound that is too high. The synthesizer can now opt out of bounds by annotating the type as follows: val temp: (<expr.tpe> @uncheckedBounds) = expr This facility is now used in named/default arguments for the temporaries used for the reciever and arguments. The annotation is hidden under scala.reflect.internal, rather than in the more prominent scala.annotation.unchecked, to reflect the intention that it should only be used in tree transformers. The library component of this change and test case will be included in the next commit. Why split like this? It shows that the 2.10.3 compiler will work with 2.10.2 scala-reflect.jar.
* Merge pull request #2809 from xeno-by/ticket/7733James Iry2013-08-133-11/+10
|\ | | | | SI-7733 reflective packages now more consistent with scalac
| * [nomaster] SI-7733 reflective packages now more consistent with scalacEugene Burmako2013-08-133-11/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Previously PackageScopes from scala.reflect ignored all classes that had $'s in non-rightmost positions in their names. Unfortunately this behaviour is inconsistent with how scalac does things, and I reconciled this as usual, by pulling corresponding logic into scala-reflect.jar and sharing it between runtime reflection and compiler. This change has seprate pull requests for 2.10.x and 2.11.0. The latter deprecates `scala.tools.nsc.util.ClassPath.isTraitImplementation` whereas the former (which you're looking at right now) does not, because we can't deprecated members in minor releases.
* | Merge pull request #2813 from xeno-by/topic/showrawGrzegorz Kossakowski2013-08-121-0/+2
|\ \ | |/ |/| showRaw now prints symbols of def trees
| * showRaw now prints symbols of def treesEugene Burmako2013-08-101-0/+2
| | | | | | | | A very useful addition that came in handy when hacking macro annotations
* | SI-7331 tb.parse returns unpositioned treesDen Shabalin2013-08-081-1/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This commit gets rid off code wrapping that was previously used by toolbox to get into correct parsing mode. Instead combination of templateStats/accept(EOF) is used. This is the same solution as the one used in repl and built-in scriptRunner This pull request doesn't attempt to generalize this approach in any way and re-use it all over the place due to the caution of possible accidental compatibility breakage. I plan to do it separately against master. Additionally there are a few more changes that make importers be aware of positions and a test for that (via @jedesah).
* | brings JavaMirrors up to speed with ClassfileParserEugene Burmako2013-07-231-2/+11
|/ | | | | | | Apparently there are still discrepancies between how the vanilla compiler turns class files into symbols and how the reflective compiler does it. Working on bringing these guys in sync, one bug at a time.
* Merge pull request #2689 from scalamacros/ticket/7617Adriaan Moors2013-07-121-0/+31
|\ | | | | SI-7617 typedAssign no longer expands lhs
| * makes it more convenient to work with SuppressMacroExpansionAttachmentEugene Burmako2013-06-281-0/+31
| |
* | Merge pull request #2629 from retronym/ticket/7558Paul Phillips2013-06-081-1/+1
|\ \ | | | | | | SI-7558 Fix capture of free local vars in toolbox compiler
| * | SI-7558 Fix capture of free local vars in toolbox compilerJason Zaugg2013-06-051-1/+1
| | | | | | | | | | | | | | | It was creating an `ObjectRef[<notype>]` because of a small bug in `capturedVariableType`.
* | | Merge pull request #2626 from retronym/ticket/7556Adriaan Moors2013-06-051-9/+4
|\ \ \ | |/ / |/| | SI-7556 Fix runtime reflection involving ScalaLongSignature
| * | SI-7556 Fix runtime reflection involving ScalaLongSignatureJason Zaugg2013-06-051-9/+4
| |/ | | | | | | | | | | | | | | | | | | | | | | Scala type information is stored in classfiles in encoded in a String in the ScalaSignature annotation. When it is too big for a single String, it is split into an array of Strings in a different annotation, ScalaLongSignature. The enclosed test, with a class containing 3000 methods, uses the latter. It exposes a bug in the way runtime reflection decodes that data. It must concatentate and *then* decode, rather that the other way around.
* / SI-7507 Fix lookup of private[this] member in presence of self type.Jason Zaugg2013-05-311-2/+10
|/ | | | | | | | | | | | | | | | | | | | | | | | In the following code: trait Cake extends Slice trait Slice { self: Cake => // must have self type that extends `Slice` private[this] val bippy = () // must be private[this] locally(bippy) } `ThisType(<Slice>)`.findMember(bippy)` excluded the private local member on the grounds that the first class in the base type sequence, `Cake`, was not contained in `Slice`. scala> val thisType = typeOf[Slice].typeSymbol.thisType thisType: $r.intp.global.Type = Slice.this.type scala> thisType.baseClasses res6: List[$r.intp.global.Symbol] = List(trait Cake, trait Slice, class Object, class Any) This commit changes `findMember` to use the symbol of the `ThisType`, rather than the first base class, as the location of the selection.
* SI-6138 Centralize and refine detection of `getClass` callsJason Zaugg2013-05-231-2/+10
| | | | | | | | | | | | | | | | | | | | | | | `getClass` is special cased in the compiler; this is described in in the comments on `Definitions.Any_getClass`. Part of this happens in `Typer#stabilize`. This was trying to determine if an Ident or Select node was a call to `getClass` by merits of the name of the tree's symbol and by checking that the its type (if it was a MethodType or PolyType) had no parameters in the primary parameter list. Overloaded user defined `getClass` methods confused this check. In the enclosed test case, the tree `definitions.this.getClass` had an `OverloadedType`, and such types always report an empty list of `params`. This commit: - changes `stabilize` to use `isGetClass`, rather than the homebrew check - changes `isGetClass` to consider a `Set[Symbol]` containing all `getClass` variants. This moves some similar code from `Erasure` to `Definitions` - keeps a fast negative path in `isGetClass` based on the symbol's name
* Merge pull request #2536 from adriaanm/ticket-7359Adriaan Moors2013-05-171-6/+11
|\ | | | | [backport #1727] SI-7359 cyclic nested java class
| * [backport #1727] SI-7359 cyclic nested java classAdriaan Moors2013-05-161-6/+11
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The original commit message (from 54a84a36d5): SI-6548 reflection correctly enters jinners When completing Java classes, runtime reflection enumerates their fields, methods, constructors and inner classes, loads them and enters them into either the instance part (ClassSymbol) or the static part (ModuleSymbol). However unlike fields, methods and constructors, inner classes don't need to be entered explicitly - they are entered implicitly when being loaded. This patch fixes the double-enter problem, make sure that enter-on-load uses the correct owner, and also hardens jclassAsScala against double enters that can occur in a different scenario.
* | [nomaster] unbreaks test.bcEugene Burmako2013-05-171-0/+3
| | | | | | | | | | | | | | | | | | | | The optimizer behaves unexpectedly smartly, stripping off unused private methods. Unfortunately, sometimes private methods might be compiled down to public Java methods, so stripping them off might lead to binary incompatibilities. This particular commit recovers from this problem caused by https://github.com/scala/scala/commit/5e715396af.
* | Merge pull request #2504 from scalamacros/ticket/7464Jason Zaugg2013-05-172-4/+4
|\ \ | |/ |/| SI-7464 allows FieldMirror.set to update vals
| * SI-7464 allows FieldMirror.set to update valsEugene Burmako2013-05-172-4/+4
| | | | | | | | | | There's no reason to leave such sentinels in place inside a facility designed to circumvent usual restrictions of static types / visibility.
* | Merge pull request #2494 from scalamacros/ticket/5923Eugene Burmako2013-05-112-2/+10
|\ \ | | | | | | makes sense of implicit macros!
| * | [nomaster] SI-7167 implicit macros decide what is divergenceEugene Burmako2013-05-112-2/+10
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Imagine a macro writer which wants to synthesize a complex implicit Complex[T] by making recursive calls to Complex[U] for its parts. E.g. if we have `class Foo(val bar: Bar)` and `class Bar(val x: Int)`, then it's quite reasonable for the macro writer to synthesize Complex[Foo] by calling `inferImplicitValue(typeOf[Complex[Bar])`. However if we didn't insert `info.sym.isMacro` check in `typedImplicit`, then under some circumstances (e.g. as described in http://groups.google.com/group/scala-internals/browse_thread/thread/545462b377b0ac0a) `dominates` might decide that `Bar` dominates `Foo` and therefore a recursive implicit search should be prohibited. Now when we yield control of divergent expansions to the macro writer, what happens next? In the worst case, if the macro writer is careless, we'll get a StackOverflowException from repeated macro calls. Otherwise, the macro writer could check `c.openMacros` and `c.openImplicits` and do `c.abort` when expansions are deemed to be divergent. Upon receiving `c.abort` the typechecker will decide that the corresponding implicit search has failed which will fail the entire stack of implicit searches, producing a nice error message provided by the macro writer. NOTE: the original commit from macro paradise also introduced a new class, which encapsulates information about implicits in flight. Unfortunately we cannot do that in 2.10.x, because of binary compatibility concerns, therefore I'm marking this commit as [nomaster] and will be resubmitting its full version in a separate pull request exclusively targetting master.
* | | Merge pull request #2505 from scalamacros/topic/macrosPaul Phillips2013-05-111-10/+2
|\ \ \ | |/ / |/| | some fixes for macros: one esoteric, and one quite critical
| * | pull request feedbackEugene Burmako2013-05-111-1/+2
| | |
| * | replaces inferBootClasspath with a simple lookup at sun.boot.class.pathEugene Burmako2013-05-091-12/+1
| | | | | | | | | | | | | | | | | | It's not like we're achieving any generality by iterating through all keys in System.getProperties and looking for ones which resemble "boot.class.path", so let's be simpler.
| * | fixes a crash in ReflectionUtils.systemPropertiesEugene Burmako2013-05-091-2/+4
| |/ | | | | | | | | | | Due to SI-7465 we were erroneously assuming that System.getProperties only contains string key and string values. This led to a CCE when there was something else.
* | Merge pull request #2456 from paulp/pr/jdk8-210xGrzegorz Kossakowski2013-05-111-5/+7
|\ \ | | | | | | SI-7398 Add support for java8 default methods
| * | SI-7398 Add support for java8 default methodsPaul Phillips2013-04-261-5/+7
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In classfile parser: mark symbols which represent interface methods yet have code attributes with new flag DEFAULTMETHOD. These must be kept distinct from regular method bodies so that an error can be issued when a regular concrete method is overridden without the override keyword, but not when the overridden method is a default. In java source parser: mark Modifiers of interface default methods with DEFAULTMETHOD flag. Writing the test was everything I dreamed, and more! However, % test/partest --debug test/files/run/t7398.scala Skipping java8-specific test under java version 1.7.0_21 testing: [...]/files/run/t7398.scala [ OK ] All of 1 tests were successful (elapsed time: 00:00:04) % test/partest --debug test/files/run/t7398.scala Attempting java8-specific test under java version 1.8.0-ea testing: [...]/files/run/t7398.scala [ OK ] All of 1 tests were successful (elapsed time: 00:00:13)
* / SI-7325 cleans up corner cases of percent handling in StringContext.fEugene Burmako2013-05-041-1/+1
|/ | | | | | | | | | See comments in code for the exhaustive list of the cases handled. Also note that treatment of non-formatting percents has been changed. Embedding literal %'s now requires escaping. Moreover, this commit also features exact error positions for string interpolation, something that has been impossible until the fix for SI-7271, also included in this patch.
* SI-6675 Avoid spurious warning about pattern bind arity.Jason Zaugg2013-04-211-1/+15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In 692372ce, we added a warning (under -Xlint) when binding a `TupleN` in to a single pattern binder, which wasn't allowed before 2.10.0, and more often than not represents a bug. However, that warning overstretched, and warned even when using a Tuple Pattern to bind to the elements of such a value. This commit checks for this case, and avoids the spurious warnings. A new test case is added for this case to go with the existing test for SI-6675: $ ./tools/partest-ack 6675 % tests-with-matching-paths ... 3 % tests-with-matching-code ... 2 # 3 tests to run. test/partest --show-diff --show-log \ test/files/neg/t6675-old-patmat.scala \ test/files/neg/t6675.scala \ test/files/pos/t6675.scala \ "" Testing individual files testing: [...]/files/pos/t6675.scala [ OK ] Testing individual files testing: [...]/files/neg/t6675-old-patmat.scala [ OK ] testing: [...]/files/neg/t6675.scala [ OK ] All of 3 tests were successful (elapsed time: 00:00:03)
* Merge pull request #2367 from vigdorchik/si-6387-revertAdriaan Moors2013-04-091-24/+11
|\ | | | | Revert "SI-6387 Clones accessor before name expansion"
| * Revert "SI-6387 Clones accessor before name expansion"Eugene Vigdorchik2013-04-081-24/+11
| | | | | | | | | | | | This reverts commit 4e10b2c833fa846c68b81e94a08d867e7de656aa. Add 6387 test to pending and 7341 to up-to-date.
* | Merge pull request #2354 from adriaanm/ticket-7289Adriaan Moors2013-04-091-7/+10
|\ \ | | | | | | SI-7289 Less strict type application for TypeVar.
| * | SI-7289 Less strict type application for TypeVar.Adriaan Moors2013-04-081-7/+10
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When a type constructor variable is applied to the wrong number of arguments, return a new type variable whose instance is `ErrorType`. Dissection of the reported test case by @retronym: Define the first implicit: scala> trait Schtroumpf[T] defined trait Schtroumpf scala> implicit def schtroumpf[T, U <: Coll[T], Coll[X] <: Traversable[X]] | (implicit minorSchtroumpf: Schtroumpf[T]): Schtroumpf[U] = ??? schtroumpf: [T, U <: Coll[T], Coll[X] <: Traversable[X]](implicit minorSchtroumpf: Schtroumpf[T])Schtroumpf[U] Call it explicitly => kind error during type inference reported. scala> schtroumpf(null): Schtroumpf[Int] <console>:10: error: inferred kinds of the type arguments (Nothing,Int,Int) do not conform to the expected kinds of the type parameters (type T,type U,type Coll). Int's type parameters do not match type Coll's expected parameters: class Int has no type parameters, but type Coll has one schtroumpf(null): Schtroumpf[Int] ^ <console>:10: error: type mismatch; found : Schtroumpf[U] required: Schtroumpf[Int] schtroumpf(null): Schtroumpf[Int] ^ Add another implicit, and let implicit search weigh them up. scala> implicitly[Schtroumpf[Int]] <console>:10: error: diverging implicit expansion for type Schtroumpf[Int] starting with method schtroumpf implicitly[Schtroumpf[Int]] ^ scala> implicit val qoo = new Schtroumpf[Int]{} qoo: Schtroumpf[Int] = $anon$1@c1b9b03 scala> implicitly[Schtroumpf[Int]] <crash> Implicit search compares the two in-scope implicits in `isStrictlyMoreSpecific`, which constructs an existential type: type ET = Schtroumpf[U] forSome { type T; type U <: Coll[T]; type Coll[_] <: Traversable[_] } A subsequent subtype check `ET <:< Schtroumpf[Int]` gets to `withTypeVars`, which replaces the quantified types with type variables, checks conformance of that substitued underlying type against `Schtroumpf[Int]`, and then tries to solve the collected type constraints. The type var trace looks like: [ create] ?T ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ create] ?U ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ create] ?Coll ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ setInst] Nothing ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing ) [ setInst] scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=scala.collection.immutable.Nil.type ) [ setInst] =?scala.collection.immutable.Nil.type( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?scala.collection.immutable.Nil.type ) [ create] ?T ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ setInst] Int ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Int ) [ create] ?T ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ create] ?U ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ create] ?Coll ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]] ) [ setInst] Nothing ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], T=Nothing ) [ setInst] Int ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], U=Int ) [ setInst] =?Int ( In Test#schtroumpf[T,U <: Coll[T],Coll[_] <: Traversable[_]], Coll==?Int ) The problematic part is when `?Int` (the type var originated from `U`) is registered as a lower bound for `Coll`. That happens in `solveOne`: for (tparam2 <- tparams) tparam2.info.bounds.hi.dealias match { case TypeRef(_, `tparam`, _) => log(s"$tvar addLoBound $tparam2.tpeHK.instantiateTypeParams($tparams, $tvars)") tvar addLoBound tparam2.tpeHK.instantiateTypeParams(tparams, tvars) case _ => }
* | Merge pull request #2349 from scalamacros/ticket/6937Adriaan Moors2013-04-091-36/+2
|\ \ | |/ |/| SI-6937 core type tags are no longer referentially unique
| * SI-6937 core type tags are no longer referentially uniqueEugene Burmako2013-04-031-36/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Type tag factory used to evaluate the provided type creator in the context of the initial mirror in order to maintain referential equality of instances of standard tags. Unfortunately this evaluation might fail if the mirror provided doesn't contain the classes being referred to. Therefore I think we should avoid evaluating type creators there. Note that failure of evaluation doesn't mean that there's something bad going on. When one creates a type tag, the correct mirror / classloader to interpret that tag in might be unknown (like it happens here). This is okay, and this is exactly what the 2.10.0-M4 refactoring has addressed. Something like `res2.typeTag[A].in(currentMirror)` should be okay.
* | Simplify interplay between Uncurry Info- and Tree-TransformersJason Zaugg2013-04-021-2/+9
| | | | | | | | | | | | | | | | | | | | Now, the InfoTransformer is responsible for erasing the path dependent types of formal parameters, at the same place where it flattens nested method types. This is preferable to having the tree transformer overwrite the result of the info transformer, as used to be the case after my previous work on SI-6135 / 493197fc.
* | Refactor existential related code out of types.Jason Zaugg2013-04-021-0/+77
| | | | | | | | | | | | | | For imminent reuse in the subsequent commit. The binary compatibility whitelist is updated to ignore these, as they live in reflect.internal.
* | Add a cautionary comment to TreeSymSubstitutor.Jason Zaugg2013-04-021-0/+5
| |
* | SI-6715 Shouldn't return "" from TermNames.originalNameKato Kazuyoshi2013-04-031-1/+1
| |
* | Backport #2289's TermNames.unexpandedName as TermNames.originalNameKato Kazuyoshi2013-04-031-7/+10
|/ | | | | Because this implementation is more clear than 2.10.x's and it will simplify a further commit to fix SI-6715.
* Merge pull request #2292 from retronym/ticket/7285Adriaan Moors2013-03-271-3/+4
|\ | | | | SI-7285 Fix match analysis with nested objects
| * SI-7285 Fix match analysis with nested objects.Jason Zaugg2013-03-231-3/+4
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The fix for SI-6146 introduced `nestedMemberType` to enumerate sealed subtypes based on the (prefixed) type of the scrutinee and the symbols of its sealed subclasses. That method needed to widen `ThisType(modSym)`s to `ModuleTypeRef(modSym)` before calling `asSeenFrom`. However, this could lead to confused in the match analysis, which sees `ModuleTypeRef` as distinct from singleton types on the same modules (after all, they aren't =:=). Spurious warnings ensued. This commit makes two changes: - conditionally re-narrow the result of `asSeenFrom` in `nestedMemberType`. - present `a.b.SomeModule.type` as `SomeModule` in warnings emitted by the pattern matcher.
* | Merge pull request #2288 from paulp/pr/2273-with-editsPaul Phillips2013-03-271-11/+24
|\ \ | | | | | | SI-6387 Clones accessor before name expansion
| * | SI-6387 Clones accessor before name expansionEugene Vigdorchik2013-03-251-11/+24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | When a symbol's name is expanded due to a conflict during composition (e.g. multiple traits with same-named members, but which are not both visible at the language level in the concrete class) the compiler renames some symbols with expanded names which embed the full name of the declaring class to avoid clashes. In the rare cases when the accessor overrides the member in base class, such expansion either results in AbstractMethodError when the base method is abstract, or, even worse, can change the semantics of the program. To avoid such issues, we clone the accessor symbol, clear its ACCESSOR flag and enter the symbol with an unchanged name.
* | | Merge pull request #2257 from JamesIry/2.10.x_classfile_51Paul Phillips2013-03-251-1/+4
|\ \ \ | |_|/ |/| | Read version 51 (JDK 7) class files.
| * | Read version 51 (JDK 7) class files.James Iry2013-03-141-1/+4
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This commit makes the ClassFileReader/ICodeReader parse class files from JDK 7 (class file version 51). It does that by skipping over the method handle related entries in the constant pool and by doing some dummy processing on invoke dynamic instructions. The inliner is updated to not try to inline a method with an invoke dynamic instruction. A place holder INVOKE_DYNAMIC instruction is added to ICode but it is designed to create an error if there's ever any attempt to analyze it. Because the inliner is the only phase that ever tries to analyze ICode instructions not generated from Scala source and because Scala source will never emit an INVOKE_DYNAMIC, the place holder INVOKE_DYNAMIC should never cause any errors. A test is included that generates a class file with INVOKE_DYNAMIC and then compiles Scala code that depends on it.
* / fixes the craziness in JavaUniverse.logEugene Burmako2013-03-201-1/+1
|/ | | | | | | This long-standing, but trivial to fix nuisance in the implementation of runtime reflection actively avoided being fixed in both 2.10.0 and 2.10.1. It's finally the time to put it to a rest.
* Merge pull request #2234 from scalamacros/ticket/7240Paul Phillips2013-03-122-2/+2
|\ | | | | SI-7240 fixes language feature lookup