Browse Source

Inlined empty closures return nil. Fix #1242.

Herby Vojčík 4 years ago
parent
commit
1693b5d526

+ 21 - 14
lang/src/Compiler-Inlining.js

@@ -1659,37 +1659,44 @@ selector: "inlinedClosure:wrapFinalValueIn:",
 protocol: "inlining",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["closure", "aBlock"],
-source: "inlinedClosure: closure wrapFinalValueIn: aBlock\x0a\x09| sequence statements |\x0a\x0a\x09sequence := closure sequence.\x0a\x09statements := sequence dagChildren.\x0a\x09\x0a\x09statements ifNotEmpty: [\x0a\x09\x09| final |\x0a\x09\x09final := statements last.\x0a\x09\x09final yieldsValue ifTrue: [ sequence replace: final with: (aBlock value: final) ] ].\x0a\x0a\x09^ closure",
-referencedClasses: [],
+source: "inlinedClosure: closure wrapFinalValueIn: aBlock\x0a\x09| sequence statements final |\x0a\x0a\x09sequence := closure sequence.\x0a\x09statements := sequence dagChildren.\x0a\x09\x0a\x09sequence dagChildren ifEmpty: [ sequence add: (IRVariable new\x0a\x09\x09variable: (closure scope pseudoVars at: 'nil');\x0a\x09\x09yourself) ].\x0a\x09final := statements last.\x0a\x09final yieldsValue ifTrue: [ sequence replace: final with: (aBlock value: final) ].\x0a\x0a\x09^ closure",
+referencedClasses: ["IRVariable"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["sequence", "dagChildren", "ifNotEmpty:", "last", "ifTrue:", "yieldsValue", "replace:with:", "value:"]
+messageSends: ["sequence", "dagChildren", "ifEmpty:", "add:", "variable:", "new", "at:", "pseudoVars", "scope", "yourself", "last", "ifTrue:", "yieldsValue", "replace:with:", "value:"]
 }, function ($methodClass){ return function (closure,aBlock){
 var self=this,$self=this;
-var sequence,statements;
+var sequence,statements,final;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1;
+var $1,$3,$2,$4;
 sequence=$recv(closure)._sequence();
 statements=$recv(sequence)._dagChildren();
-$recv(statements)._ifNotEmpty_((function(){
-var final;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["dagChildren"]=1;
+//>>excludeEnd("ctx");
+$recv($recv(sequence)._dagChildren())._ifEmpty_((function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
-final=$recv(statements)._last();
-$1=$recv(final)._yieldsValue();
-if($core.assert($1)){
-return $recv(sequence)._replace_with_(final,$recv(aBlock)._value_(final));
-}
+$1=sequence;
+$3=$recv($globals.IRVariable)._new();
+$recv($3)._variable_($recv($recv($recv(closure)._scope())._pseudoVars())._at_("nil"));
+$2=$recv($3)._yourself();
+return $recv($1)._add_($2);
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx2) {$ctx2.fillBlock({final:final},$ctx1,1)});
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
 //>>excludeEnd("ctx");
 }));
+final=$recv(statements)._last();
+$4=$recv(final)._yieldsValue();
+if($core.assert($4)){
+$recv(sequence)._replace_with_(final,$recv(aBlock)._value_(final));
+}
 return closure;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"inlinedClosure:wrapFinalValueIn:",{closure:closure,aBlock:aBlock,sequence:sequence,statements:statements})});
+}, function($ctx1) {$ctx1.fill(self,"inlinedClosure:wrapFinalValueIn:",{closure:closure,aBlock:aBlock,sequence:sequence,statements:statements,final:final})});
 //>>excludeEnd("ctx");
 }; }),
 $globals.IRSendInliner);

+ 6 - 5
lang/src/Compiler-Inlining.st

@@ -425,15 +425,16 @@ inlineSend: anIRSend
 !
 
 inlinedClosure: closure wrapFinalValueIn: aBlock
-	| sequence statements |
+	| sequence statements final |
 
 	sequence := closure sequence.
 	statements := sequence dagChildren.
 	
-	statements ifNotEmpty: [
-		| final |
-		final := statements last.
-		final yieldsValue ifTrue: [ sequence replace: final with: (aBlock value: final) ] ].
+	sequence dagChildren ifEmpty: [ sequence add: (IRVariable new
+		variable: (closure scope pseudoVars at: 'nil');
+		yourself) ].
+	final := statements last.
+	final yieldsValue ifTrue: [ sequence replace: final with: (aBlock value: final) ].
 
 	^ closure
 ! !

+ 36 - 0
lang/src/Compiler-Tests.js

@@ -964,6 +964,42 @@ return self;
 }; }),
 $globals.AbstractCompilerTest);
 
+$core.addMethod(
+$core.method({
+selector: "testRegression1242",
+protocol: "tests",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRegression1242\x0a\x09self should: '\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := nil ifNil: [].\x0a\x09\x09\x09^ x\x0a\x09' return: nil.\x0a\x09\x0a\x09self should: '\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := 1 ifNotNil: [].\x0a\x09\x09\x09^ x\x0a\x09' return: nil.\x0a\x09\x0a\x09self should: '\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := false ifFalse: [].\x0a\x09\x09\x09^ x\x0a\x09' return: nil.\x0a\x09\x0a\x09self should: '\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := true ifTrue: [].\x0a\x09\x09\x09^ x\x0a\x09' return: nil.",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["should:return:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._should_return_("\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := nil ifNil: [].\x0a\x09\x09\x09^ x\x0a\x09",nil);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["should:return:"]=1;
+//>>excludeEnd("ctx");
+$self._should_return_("\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := 1 ifNotNil: [].\x0a\x09\x09\x09^ x\x0a\x09",nil);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["should:return:"]=2;
+//>>excludeEnd("ctx");
+$self._should_return_("\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := false ifFalse: [].\x0a\x09\x09\x09^ x\x0a\x09",nil);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["should:return:"]=3;
+//>>excludeEnd("ctx");
+$self._should_return_("\x0a\x09\x09foo\x0a\x09\x09\x09|x|\x0a\x09\x09\x09x := 2.\x0a\x09\x09\x09x := true ifTrue: [].\x0a\x09\x09\x09^ x\x0a\x09",nil);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRegression1242",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.AbstractCompilerTest);
+
 $core.addMethod(
 $core.method({
 selector: "testRegression1244",

+ 34 - 0
lang/src/Compiler-Tests.st

@@ -232,6 +232,40 @@ testPragmaJSStatement
 	self should: 'foo < inlineJS: ''return 2+3'' >' return: 5
 !
 
+testRegression1242
+	self should: '
+		foo
+			|x|
+			x := 2.
+			x := nil ifNil: [].
+			^ x
+	' return: nil.
+	
+	self should: '
+		foo
+			|x|
+			x := 2.
+			x := 1 ifNotNil: [].
+			^ x
+	' return: nil.
+	
+	self should: '
+		foo
+			|x|
+			x := 2.
+			x := false ifFalse: [].
+			^ x
+	' return: nil.
+	
+	self should: '
+		foo
+			|x|
+			x := 2.
+			x := true ifTrue: [].
+			^ x
+	' return: nil.
+!
+
 testRegression1244
 	self should: 'foo [ ^ true ifTrue: [1] ifFalse: [2] ] value' return: 1
 !

+ 2 - 0
lang/src/Kernel-Tests.js

@@ -1892,7 +1892,9 @@ $self._should_raise_((function(){
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
 if($core.assert("")){
+return nil;
 } else {
+return nil;
 }
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});