Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit67e77b9

Browse files
retronymdwijnand
authored andcommitted
[fport] More accurate outer checks in patterns
Avoids eliding outer checks that matter (run/t11534b.scala) andavoids emitting checks that don't (pos/t11534.scala) which avoidscompiler warnings when the tested class doesn't have an outerfield.The latter stops the annoying unchecked warning that appeared sincea recent refactoring made `TermName` a final class.(cherry picked from commit1115959)
1 parentb74ea55 commit67e77b9

File tree

5 files changed

+239
-18
lines changed

5 files changed

+239
-18
lines changed

‎src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala‎

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,6 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
369369
defeqTest(pat:Tree,testedBinder:Symbol)=REF(testedBinder)OBJ_EQ pat
370370

371371
overridedefwithOuterTest(orig:Tree)(testedBinder:Symbol,expectedTp:Type):Tree= {
372-
valexpectedPrefix= expectedTp.prefix
373-
valtestedPrefix= testedBinder.info.prefix
374-
375372
// Check if a type is defined in a static location. Unlike `tp.isStatic` before `flatten`,
376373
// this also includes methods and (possibly nested) objects inside of methods.
377374
defdefinedInStaticLocation(tp:Type):Boolean= {
@@ -383,20 +380,78 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
383380
tp.typeSymbol.owner== tp.prefix.typeSymbol&& isStatic(tp.prefix)
384381
}
385382

386-
if ((expectedPrefix eqNoPrefix)
387-
|| expectedTp.typeSymbol.isJava
388-
|| definedInStaticLocation(expectedTp)
389-
|| testedPrefix=:= expectedPrefix) orig
390-
else gen.mkAttributedQualifierIfPossible(expectedPrefix)match {
391-
caseNone=> orig
392-
caseSome(expectedOuterRef)=>
393-
// ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix`
394-
// by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix`
395-
// if there's an outer accessor, otherwise the condition becomes `true`
396-
// TODO: centralize logic whether there's an outer accessor and use here?
397-
valsynthOuterGetter= expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags=SYNTHETIC|ARTIFACT) setInfo expectedPrefix
398-
valouterTest= (Select(codegen._asInstanceOf(testedBinder, expectedTp), synthOuterGetter))OBJ_EQ expectedOuterRef
399-
and(orig, outerTest)
383+
// In `def foo(a: b.B) match { case _: p.P }`
384+
// testedBinder.symbol.info = b.B
385+
// expectedTp = p.P
386+
387+
expectedTpmatch {
388+
case _if expectedTp.typeSymbolDirect.isAliasType=>
389+
withOuterTest(orig)(testedBinder, expectedTp.dealias)
390+
caseRefinedType(Nil, _)=> orig
391+
case rt@RefinedType(parent:: rest, scope)=>
392+
// If the pattern type is refined type, emit outer tests for each component.
393+
withOuterTest(withOuterTest(orig)(testedBinder, parent))(testedBinder, copyRefinedType(rt, rest, scope))
394+
case _=>
395+
valexpectedClass= expectedTp.typeSymbol
396+
assert(!expectedClass.isRefinementClass, orig)
397+
// .typeSymbol dealiases, so look at the prefix of the base type at the dealiased symbol,
398+
// not of expectedTp itself.
399+
valexpectedPrefix= expectedTp.baseType(expectedClass).prefix
400+
401+
// If P is a subclass of B, and b =:= p, b.B
402+
// If <fresh>.P a subtype of <fresh>.B and does b =:= p,
403+
// we can assume that a value inhabiting _#P with b.P conforms to p.P.
404+
//
405+
// It is not sufficient to show that p.P is a subtype of p.B, as this
406+
// would incorrectly elide the outer test in:
407+
//
408+
// class P extends p1.B
409+
// def test(b: p1.B) = b match { case _: p1.P }
410+
// test(new p2.P)
411+
defprefixAligns:Boolean= {
412+
expectedTpmatch {
413+
caseTypeRef(pre, _, _)if!pre.isStable=>// e.g. _: Outer#Inner
414+
false
415+
caseTypeRef(pre, sym, args)=>
416+
valtestedBinderClass= testedBinder.info.typeSymbol
417+
valtestedBinderType= testedBinder.info.baseType(testedBinderClass)
418+
419+
valtestedPrefixIsExpectedTypePrefix= pre=:= testedBinderType.prefix
420+
valtestedPrefixAndExpectedPrefixAreStaticallyIdentical:Boolean= {
421+
valfreshPrefix= prematch {
422+
caseThisType(thissym)=>
423+
ThisType(thissym.cloneSymbol(thissym.owner))
424+
case _=>
425+
valpreSym= pre.termSymbol
426+
valfreshPreSym= preSym.cloneSymbol(preSym.owner).setInfo(preSym.info)
427+
singleType(pre.prefix, freshPreSym)
428+
}
429+
valexpectedTpFromFreshPrefix=TypeRef(freshPrefix, sym, args)
430+
valbaseTypeFromFreshPrefix= expectedTpFromFreshPrefix.baseType(testedBinderClass)
431+
freshPrefix eq baseTypeFromFreshPrefix.prefix
432+
}
433+
testedPrefixAndExpectedPrefixAreStaticallyIdentical&& testedPrefixIsExpectedTypePrefix
434+
case _=>
435+
false
436+
}
437+
}
438+
439+
if ((expectedPrefix eqNoPrefix)
440+
|| expectedTp.typeSymbol.isJava
441+
|| definedInStaticLocation(expectedTp)
442+
|| testedBinder.info<:< expectedTp
443+
|| prefixAligns) orig
444+
else gen.mkAttributedQualifierIfPossible(expectedPrefix)match {
445+
caseNone=> orig
446+
caseSome(expectedOuterRef)=>
447+
// ExplicitOuter replaces `Select(q, outerSym) OBJ_EQ expectedPrefix`
448+
// by `Select(q, outerAccessor(outerSym.owner)) OBJ_EQ expectedPrefix`
449+
// if there's an outer accessor, otherwise the condition becomes `true`
450+
// TODO: centralize logic whether there's an outer accessor and use here?
451+
valsynthOuterGetter= expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags=SYNTHETIC|ARTIFACT) setInfo expectedPrefix
452+
valouterTest= (Select(codegen._asInstanceOf(testedBinder, expectedTp), synthOuterGetter))OBJ_EQ expectedOuterRef
453+
and(orig, outerTest)
454+
}
400455
}
401456
}
402457
}

‎test/files/neg/t7721.check‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ t7721.scala:49: warning: abstract type pattern B.this.Foo is unchecked since it
2222
t7721.scala:49: warning: abstract type pattern B.this.Bar is unchecked since it is eliminated by erasure
2323
case x: Foo with Bar with Concrete => x.bippy + x.barry + x.dingo + x.conco + x.bongo
2424
^
25+
t7721.scala:13: warning: The outer reference in this type test cannot be checked at run time.
26+
case x: Foo with Concrete => x.bippy + x.conco
27+
^
28+
t7721.scala:17: warning: The outer reference in this type test cannot be checked at run time.
29+
case x: Concrete with Foo => x.bippy + x.conco
30+
^
31+
t7721.scala:21: warning: The outer reference in this type test cannot be checked at run time.
32+
case x: Foo with Bar => x.bippy + x.barry
33+
^
34+
t7721.scala:41: warning: The outer reference in this type test cannot be checked at run time.
35+
case x: Foo with Concrete => x.bippy + x.dingo + x.conco
36+
^
37+
t7721.scala:45: warning: The outer reference in this type test cannot be checked at run time.
38+
case x: Concrete with Foo => x.bippy + x.dingo + x.conco
39+
^
40+
t7721.scala:49: warning: The outer reference in this type test cannot be checked at run time.
41+
case x: Foo with Bar with Concrete => x.bippy + x.barry + x.dingo + x.conco + x.bongo
42+
^
2543
error: No warnings can be incurred under -Werror.
26-
8 warnings
44+
14 warnings
2745
1 error

‎test/files/pos/t11534.scala‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
objectTest1 {
2+
valg: scala.tools.nsc.Global=???
3+
importg._
4+
deftest(sym:Symbol)= sym.namematch {
5+
case_:TermName=>
6+
}
7+
}

‎test/files/run/t11534b.scala‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
objectTest {
2+
caseclassO(i:Int) {
3+
classA
4+
classBextendsA {
5+
defbOuter=O.this
6+
}
7+
traitC {
8+
defcOuter=O.this
9+
}
10+
classDextends o2.BwithC
11+
}
12+
valo1=newO(1);
13+
valo2=newO(2);
14+
defpat1(a:Test.o1.C)= amatch {
15+
caseb:Test.o1.B=>
16+
assert(b.bOuter eqTest.o1,
17+
s"expected${o1} as outer of value conforming to pattern `b: Test.o1.B`, but got${b.bOuter}")
18+
case _=>
19+
20+
}
21+
defmain(args:Array[String]):Unit= {
22+
pat1(new o1.D)
23+
}
24+
}

‎test/files/run/t11534c.scala‎

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// scalac: -unchecked
2+
importscala.util.Try
3+
4+
objectTest {
5+
classO(vali:Int) {
6+
classA {
7+
valaOuter= i
8+
}
9+
10+
classB1extendsA {
11+
valb1Outer= i
12+
}
13+
}
14+
classM(i:Int)extendsO(i) {
15+
classB2extends m2.A {
16+
valb2Outer= i
17+
}
18+
19+
defpat1(a:M.this.A)= amatch {
20+
caseb:M.this.B1=>// can elide outer check, (a : m1.A) && (a : O#B1) implies (a : m1.B1)
21+
assertOuter(m1.i, b.b1Outer)
22+
true
23+
case _=>
24+
false
25+
}
26+
defpat2(a: m2.A)= amatch {
27+
caseb:M.this.B2=>// needs runtime outer check
28+
assertOuter(m1.i, b.b2Outer)
29+
true
30+
case _=>
31+
false
32+
}
33+
defpat3(a:M.this.B1)= amatch {
34+
caseb:M.this.A=>// can elide outer check, (a : m1.B1) && (a : O#A) implies (a : m1.B1)
35+
assertOuter(m1.i, b.aOuter)
36+
true
37+
case _=>
38+
false
39+
}
40+
defpat4(a:M.this.B2)= amatch {
41+
caseb: m2.A=>// can elide outer check, (a : m1.B2) implies (a : m2.A)
42+
assertOuter(m2.i, b.aOuter)
43+
true
44+
case _=>
45+
false
46+
}
47+
}
48+
49+
valm1=newM(1);
50+
valm2=newM(2);
51+
52+
defpat1(a: m1.A)= amatch {
53+
caseb: m1.B1=>// can elide outer check, (a : m1.A) && (a : O#B1) implies (a : m1.B1)
54+
assertOuter(m1.i, b.b1Outer)
55+
true
56+
case _=>
57+
false
58+
}
59+
defpat2(a: m2.A)= amatch {
60+
caseb: m1.B2=>// needs runtime outer check
61+
assertOuter(m1.i, b.b2Outer)
62+
true
63+
case _=>
64+
false
65+
}
66+
defpat3(a: m1.B1)= amatch {
67+
caseb: m1.A=>// can elide outer check, (a : m1.B1) && (a : O#A) implies (a : m1.B1)
68+
assertOuter(m1.i, b.aOuter)
69+
true
70+
case _=>
71+
false
72+
}
73+
defpat4(a: m1.B2)= amatch {
74+
caseb: m2.A=>// can elide outer check, (a : m1.B2) implies (a : m2.A)
75+
assertOuter(m2.i, b.aOuter)
76+
true
77+
case _=>
78+
false
79+
}
80+
81+
defpat5(a:M#B2)= amatch {
82+
caseb: m2.A=>// can elide outer check, (a : A#B2) implies (a : m2.A)
83+
assertOuter(m2.i, b.aOuter)
84+
true
85+
case _=>
86+
false
87+
}
88+
defassertOuter(expected:Int,actual:Int):Unit= {
89+
if (expected!= actual)throwWrongOuter(expected, actual)
90+
}
91+
caseclassWrongOuter(expected:Int,actual:Int)extendsRuntimeException(s"expected:$expected, actual:$actual")
92+
93+
defmain(args:Array[String]):Unit= {
94+
assert(pat1(new m1.B1))
95+
assert(m1.pat1(new m1.B1))
96+
assert(Try(pat1((new m2.B1).asInstanceOf[m1.B1])).failed.get==WrongOuter(m1.i, m2.i))
97+
assert(Try(m1.pat1((new m2.B1).asInstanceOf[m1.B1])).failed.get==WrongOuter(m1.i, m2.i))
98+
99+
assert(!pat2(new m2.B2))
100+
assert(!m1.pat2(new m2.B2))
101+
assert(pat2(new m1.B2))
102+
assert(m1.pat2(new m1.B2))
103+
104+
assert(pat3(new m1.B1))
105+
assert(m1.pat3(new m1.B1))
106+
assert(Try(pat3((new m2.B1).asInstanceOf[m1.B1])).failed.get==WrongOuter(m1.i, m2.i))
107+
assert(Try(m1.pat3((new m2.B1).asInstanceOf[m1.B1])).failed.get==WrongOuter(m1.i, m2.i))
108+
109+
assert(pat4(new m1.B2))
110+
assert(m1.pat4(new m1.B2))
111+
assert(pat4((new m2.B2).asInstanceOf[m1.B2]))
112+
assert(m1.pat4((new m2.B2).asInstanceOf[m1.B2]))
113+
114+
assert(pat5(new m1.B2))
115+
assert(pat5(new m2.B2))
116+
}
117+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp