aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/src/dotty/tools/backend/jvm/GenBCode.scala22
-rw-r--r--compiler/src/dotty/tools/dotc/ast/untpd.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/transform/PostTyper.scala10
-rw-r--r--compiler/src/dotty/tools/dotc/transform/TailRec.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/transform/TreeChecker.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Checking.scala33
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--doc-tool/resources/_layouts/main.html1
-rw-r--r--doc-tool/resources/_layouts/sidebar.html9
-rw-r--r--doc-tool/resources/css/dottydoc.css118
-rw-r--r--tests/neg/leak-type.scala13
-rw-r--r--tests/pos/i1130.scala4
-rw-r--r--tests/pos/leak-inferred.scala12
-rw-r--r--tests/pos/tailcall/i2024.scala4
-rw-r--r--tests/pos/tailcall/return.scala2
18 files changed, 186 insertions, 62 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala
index 65dcb6c79..8fd6d1bc0 100644
--- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala
+++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala
@@ -1,19 +1,18 @@
package dotty.tools.backend.jvm
import dotty.tools.dotc.CompilationUnit
-import dotty.tools.dotc.ast.Trees.{ValDef, PackageDef}
+import dotty.tools.dotc.ast.Trees.{PackageDef, ValDef}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.core.Names.TypeName
import scala.collection.mutable
-import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor}
+import scala.tools.asm.{ClassVisitor, CustomAttr, FieldVisitor, MethodVisitor}
import scala.tools.nsc.Settings
import scala.tools.nsc.backend.jvm._
import dotty.tools.dotc
import dotty.tools.dotc.backend.jvm.DottyPrimitives
import dotty.tools.dotc.transform.Erasure
-
import dotty.tools.dotc.interfaces
import java.util.Optional
@@ -27,14 +26,15 @@ import Symbols._
import Denotations._
import Phases._
import java.lang.AssertionError
-import java.io.{ File => JFile }
+import java.io.{FileOutputStream, File => JFile}
+
import scala.tools.asm
import scala.tools.asm.tree._
-import dotty.tools.dotc.util.{Positions, DotClass}
+import dotty.tools.dotc.util.{DotClass, Positions}
import tpd._
import StdNames._
-import scala.reflect.io.{Directory, PlainDirectory, AbstractFile}
+import scala.reflect.io.{AbstractFile, Directory, PlainDirectory}
import scala.tools.nsc.backend.jvm.opt.LocalOpt
class GenBCode extends Phase {
@@ -205,7 +205,15 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
if (claszSymbol.isClass) // @DarkDimius is this test needed here?
for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) {
val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary)
- (if (mirrorC ne null) mirrorC else plainC).visitAttribute(dataAttr)
+ val store = if (mirrorC ne null) mirrorC else plainC
+ store.visitAttribute(dataAttr)
+ if (ctx.settings.emitTasty.value) {
+ val outTastyFile = getFileForClassfile(outF, store.name, ".tasty").file
+ val fos = new FileOutputStream(outTastyFile, false)
+ fos.write(binary)
+ fos.close()
+
+ }
}
// -------------- bean info class, if needed --------------
diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala
index 7020e4dac..a5feecc77 100644
--- a/compiler/src/dotty/tools/dotc/ast/untpd.scala
+++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala
@@ -486,7 +486,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case SymbolLit(str) =>
cpy.SymbolLit(tree)(str)
case InterpolatedString(id, segments) =>
- cpy.InterpolatedString(tree)(id, segments.map(transform(_)))
+ cpy.InterpolatedString(tree)(id, segments.mapConserve(transform))
case Function(args, body) =>
cpy.Function(tree)(transform(args), transform(body))
case InfixOp(left, op, right) =>
diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
index 318295751..49b64d869 100644
--- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -71,6 +71,7 @@ class ScalaSettings extends Settings.SettingGroup {
val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)")
val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error")
val log = PhasesSetting("-Ylog", "Log operations during")
+ val emitTasty = BooleanSetting("-YemitTasty", "Generate tasty in separate *.tasty file.")
val Ylogcp = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.")
val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.")
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala
index 6c7982d78..86e5be2e2 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala
@@ -159,7 +159,6 @@ class TreeBuffer extends TastyBuffer(50000) {
val tree = it.next
treeAddrs.get(tree) match {
case addr: Addr => treeAddrs.put(tree, adjusted(addr))
- case addrs: List[Addr] => treeAddrs.put(tree, addrs.map(adjusted))
}
}
}
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index a9ea49ad1..88b6eef7a 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -736,7 +736,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
// no longer necessary.
goto(end)
setPos(start, tree)
- sym.info = ta.avoidPrivateLeaks(sym, tree.pos)
+ if (!sym.isType) { // Only terms might have leaky aliases, see the documentation of `checkNoPrivateLeaks`
+ sym.info = ta.avoidPrivateLeaks(sym, tree.pos)
+ }
tree
}
diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
index 8dff58dea..9821757e8 100644
--- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
+++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala
@@ -2,7 +2,7 @@ package dotty.tools.dotc
package transform
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
-import dotty.tools.dotc.ast.{Trees, tpd}
+import dotty.tools.dotc.ast.{Trees, tpd, untpd}
import scala.collection.{ mutable, immutable }
import ValueClasses._
import scala.annotation.tailrec
@@ -258,15 +258,15 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran
)
case Import(expr, selectors) =>
val exprTpe = expr.tpe
- def checkIdent(ident: Ident): Unit = {
+ def checkIdent(ident: untpd.Ident): Unit = {
val name = ident.name.asTermName.encode
if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists)
ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos)
}
selectors.foreach {
- case ident: Ident => checkIdent(ident)
- case Thicket((ident: Ident) :: _) => checkIdent(ident)
- case _ =>
+ case ident: untpd.Ident => checkIdent(ident)
+ case Thicket((ident: untpd.Ident) :: _) => checkIdent(ident)
+ case _ =>
}
super.transform(tree)
case tree =>
diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala
index aa0845605..8a695bf22 100644
--- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala
@@ -325,6 +325,10 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
else
rewriteApply(tree, meth)
+ case TypeApply(fun, targs) =>
+ val meth = fun.symbol
+ rewriteApply(tree, meth)
+
case tree@Block(stats, expr) =>
tpd.cpy.Block(tree)(
noTailTransforms(stats),
diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
index 51e2469b2..dd4d95257 100644
--- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -139,13 +139,13 @@ class TreeChecker extends Phase with SymTransformer {
class Checker(phasesToCheck: Seq[Phase]) extends ReTyper with Checking {
val nowDefinedSyms = new mutable.HashSet[Symbol]
- val everDefinedSyms = new mutable.HashMap[Symbol, Tree]
+ val everDefinedSyms = new mutable.HashMap[Symbol, untpd.Tree]
// don't check value classes after typer, as the constraint about constructors doesn't hold after transform
override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = ()
def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match {
- case tree: DefTree =>
+ case tree: untpd.DefTree =>
val sym = tree.symbol
assert(isValidJVMName(sym.name), s"${sym.fullName} name is invalid on jvm")
everDefinedSyms.get(sym) match {
@@ -160,7 +160,7 @@ class TreeChecker extends Phase with SymTransformer {
if (ctx.settings.YcheckMods.value) {
tree match {
- case t: MemberDef =>
+ case t: untpd.MemberDef =>
if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}")
// todo: compare trees inside annotations
case _ =>
diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala
index b43391592..148ccd6e3 100644
--- a/compiler/src/dotty/tools/dotc/typer/Checking.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala
@@ -348,12 +348,23 @@ object Checking {
/** Check the type signature of the symbol `M` defined by `tree` does not refer
* to a private type or value which is invisible at a point where `M` is still
- * visible. As an exception, we allow references to type aliases if the underlying
- * type of the alias is not a leak. So type aliases are transparent as far as
- * leak testing is concerned.
+ * visible.
+ *
+ * As an exception, we allow references to type aliases if the underlying
+ * type of the alias is not a leak, and if `sym` is not a type. The rationale
+ * for this is that the inferred type of a term symbol might contain leaky
+ * aliases which should be removed (see leak-inferred.scala for an example),
+ * but a type symbol definition will not contain leaky aliases unless the
+ * user wrote them, so we can ask the user to change his definition. The more
+ * practical reason for not transforming types is that `checkNoPrivateLeaks`
+ * can force a lot of denotations, and this restriction means that we never
+ * need to run `TypeAssigner#avoidPrivateLeaks` on type symbols when
+ * unpickling, which avoids some issues related to forcing order.
+ *
+ * See i997.scala for negative tests, and i1130.scala for a case where it
+ * matters that we transform leaky aliases away.
+ *
* @return The `info` of `sym`, with problematic aliases expanded away.
- * See i997.scala for tests, i1130.scala for a case where it matters that we
- * transform leaky aliases away.
*/
def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = {
class NotPrivate extends TypeMap {
@@ -388,7 +399,7 @@ object Checking {
tp
}
else mapOver(tp)
- if ((errors ne prevErrors) && tp.info.isAlias) {
+ if ((errors ne prevErrors) && !sym.isType && tp.info.isAlias) {
// try to dealias to avoid a leak error
val savedErrors = errors
errors = prevErrors
@@ -400,8 +411,14 @@ object Checking {
case tp: ClassInfo =>
tp.derivedClassInfo(
prefix = apply(tp.prefix),
- classParents = tp.parentsWithArgs.map(p =>
- apply(p).underlyingClassRef(refinementOK = false).asInstanceOf[TypeRef]))
+ classParents =
+ tp.parentsWithArgs.map { p =>
+ apply(p).underlyingClassRef(refinementOK = false) match {
+ case ref: TypeRef => ref
+ case _ => defn.ObjectType // can happen if class files are missing
+ }
+ }
+ )
case _ =>
mapOver(tp)
}
diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
index f7dd725c8..f96b8ae1d 100644
--- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -539,7 +539,7 @@ object ProtoTypes {
/** Dummy tree to be used as an argument of a FunProto or ViewProto type */
object dummyTreeOfType {
def apply(tp: Type): Tree = untpd.Literal(Constant(null)) withTypeUnchecked tp
- def unapply(tree: Tree): Option[Type] = tree match {
+ def unapply(tree: untpd.Tree): Option[Type] = tree match {
case Literal(Constant(null)) => Some(tree.typeOpt)
case _ => None
}
diff --git a/doc-tool/resources/_layouts/main.html b/doc-tool/resources/_layouts/main.html
index 7c63ec610..eb0dd51fd 100644
--- a/doc-tool/resources/_layouts/main.html
+++ b/doc-tool/resources/_layouts/main.html
@@ -13,6 +13,7 @@
integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi"
crossorigin="anonymous"
>
+
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"
diff --git a/doc-tool/resources/_layouts/sidebar.html b/doc-tool/resources/_layouts/sidebar.html
index b3947c884..24ed1bed7 100644
--- a/doc-tool/resources/_layouts/sidebar.html
+++ b/doc-tool/resources/_layouts/sidebar.html
@@ -33,9 +33,12 @@ layout: main
</ul>
</div>
<div id="content-body">
- <div id="menu-toggle" onclick="toggleMenu()">
- <i class="fa fa-bars" aria-hidden="true"></i>
- </div>
+ <button type="button" id="menu-toggle" onclick="toggleMenu()" aria-expanded="false">
+ <span class="sr-only" aria-hidden="true">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
{{ content }}
</div>
</div>
diff --git a/doc-tool/resources/css/dottydoc.css b/doc-tool/resources/css/dottydoc.css
index a89e23375..140f15eba 100644
--- a/doc-tool/resources/css/dottydoc.css
+++ b/doc-tool/resources/css/dottydoc.css
@@ -1,23 +1,31 @@
+html, body {
+ overflow-x: hidden;
+}
+
html {
height: 100%;
}
+
body {
min-height: 100%;
}
div#content-wrapper {
min-height: 100vh;
- padding-left: 250px;
- transition: all 0.5s ease;
+ padding-left: 0;
}
-div#content-wrapper.toggled {
- padding-left: 0;
+div#content-wrapper div#content-body {
+ background-color: #f4f3f4;
+ border-left: 1px solid #e0e0e0;
+ box-shadow: -3px 0 5px -2px rgba(0, 0, 0, 0.14);
+ padding: 40px 1px 1px 1px;
+ position: relative;
+ min-height: 100vh;
}
-div.index-wrapper {
+div#content-wrapper div.index-wrapper {
background-color: #fafafa;
- width: 250px;
position: fixed;
top: 0;
left: 0;
@@ -26,6 +34,77 @@ div.index-wrapper {
overflow-x: hidden;
}
+/** Animations: Easing for content body slide on toggle */
+div#content-wrapper,
+div#content-wrapper div#content-body {
+ -webkit-transition: all .25s ease-out;
+ -o-transition: all .25s ease-out;
+ transition: all .25s ease-out;
+}
+
+/** Mobile (x < 576px) sidebar: Defaults closed with 60% wide sidebar */
+div#content-wrapper {}
+div#content-wrapper div#content-body { left: 0; }
+div#content-wrapper div.index-wrapper { width: 60%; }
+
+div#content-wrapper.toggled {}
+div#content-wrapper.toggled div#content-body { left: 60%; }
+div#content-wrapper.toggled div.index-wrapper {}
+
+/** Tablet (576px <= x < 768px) sidebar: Defaults closed with 250px wide sidebar */
+@media screen and (min-width: 576px) {
+
+ div#content-wrapper {}
+ div#content-wrapper div#content-body {}
+ div#content-wrapper div.index-wrapper { width: 250px; }
+
+ div#content-wrapper.toggled {}
+ div#content-wrapper.toggled div#content-body { left: 250px; }
+ div#content-wrapper.toggled div.index-wrapper {}
+
+}
+
+/** Desktop (x >= 768px) sidebar: Defaults open with 250px wide sidebar */
+@media screen and (min-width: 768px) {
+
+ div#content-wrapper { padding-left: 250px; }
+ div#content-wrapper div#content-body {}
+ div#content-wrapper div.index-wrapper { width: 250px; }
+
+ div#content-wrapper.toggled { padding-left: 0; }
+ div#content-wrapper.toggled div#content-body { left: 0; }
+ div#content-wrapper.toggled div.index-wrapper {}
+
+}
+
+div#content-wrapper button#menu-toggle {
+ background: rgba(244, 243, 244, 0.4) none;
+ border: 1px solid transparent;
+ color: #837f84;
+ margin: -30px 0 0 10px;
+ padding: 9px 10px;
+ position: absolute;
+}
+
+div#content-wrapper button#menu-toggle:hover {
+ cursor: pointer;
+}
+
+div#content-wrapper button#menu-toggle:focus {
+ outline: none;
+}
+
+div#content-wrapper button#menu-toggle span.icon-bar {
+ background-color: #837f84;
+ border-radius: 2px;
+ border-color: #837f84;
+ color: #837f84;
+ display: block;
+ margin: 3px 0;
+ width: 15px;
+ height: 2px;
+}
+
div#doc-page-container > h1 {
border-bottom: 1px solid #eee;
padding-bottom: 0.3em;
@@ -93,27 +172,6 @@ div#doc-page-container > h6 > a:focus {
outline: none;
}
-div#content-body {
- border-left: 1px solid #e0e0e0;
- box-shadow: -3px 0px 5px -2px rgba(0,0,0,0.14);
- position: relative;
- padding: 10px;
- background-color: #f4f3f4;
- min-height: 100vh;
-}
-
-div#menu-toggle {
- color: #837F84;
- outline: none;
- padding-left: 20px;
- padding-top: 10px;
-}
-
-div#menu-toggle:hover {
- color: rgba(0, 0, 0, 0.4);
- cursor: pointer;
-}
-
ul.index-entities {
list-style-type: none;
padding-left: 0;
@@ -195,7 +253,7 @@ ul.index-entities > li > a.entity-name {
font-size: 13px;
display: block;
padding: 0 0 0 24px;
- color: rgba(0,0,0,.87);
+ color: rgba(0, 0, 0, 0.87);
background: transparent;
cursor: pointer;
float: left;
@@ -245,7 +303,7 @@ ul.toc > li > ul.hide {
ul.index-entities > li.index-title > span {
font-size: 16px;
font-weight: bold;
- color: rgba(0,0,0,.87);
+ color: rgba(0, 0, 0, 0.87);
padding: 0 24px;
}
@@ -319,7 +377,7 @@ pre {
background: rgba(244, 243, 244, 0.6);
border-radius: 2px;
margin-top: 20px;
- border: 1px solid rgba(0,0,0,0.1);
+ border: 1px solid rgba(0, 0, 0, 0.1);
}
pre > code.language-none,
diff --git a/tests/neg/leak-type.scala b/tests/neg/leak-type.scala
new file mode 100644
index 000000000..30ecab70b
--- /dev/null
+++ b/tests/neg/leak-type.scala
@@ -0,0 +1,13 @@
+trait A {
+ private type Foo = Int
+
+
+ class Inner[T <: Foo] { // error: non-private type T refers to private type Foo in its type signature
+ def get: T = ???
+ }
+}
+class B extends A {
+ def foo(x: Inner[_]): Unit = {
+ val a = x.get // error: cannot resolve reference to type B(B.this).Foo
+ }
+}
diff --git a/tests/pos/i1130.scala b/tests/pos/i1130.scala
index 8d71de5e8..c28eaa169 100644
--- a/tests/pos/i1130.scala
+++ b/tests/pos/i1130.scala
@@ -3,4 +3,6 @@ trait A {
def foo: Foo = 1
}
-class B extends A
+class B extends A {
+ foo
+}
diff --git a/tests/pos/leak-inferred.scala b/tests/pos/leak-inferred.scala
new file mode 100644
index 000000000..5d8a7e3bc
--- /dev/null
+++ b/tests/pos/leak-inferred.scala
@@ -0,0 +1,12 @@
+class A {
+ private val x = List(1,2)
+
+ val elem = x.head
+}
+
+class B extends A {
+ val a: Int = elem
+ // Without `checkNoPrivateLeaks`, we get:
+ // found: B.this.x.scala$collection$immutable$List$$A(B.this.elem)
+ // required: Int
+}
diff --git a/tests/pos/tailcall/i2024.scala b/tests/pos/tailcall/i2024.scala
new file mode 100644
index 000000000..aba1db2e5
--- /dev/null
+++ b/tests/pos/tailcall/i2024.scala
@@ -0,0 +1,4 @@
+object Test {
+// def main(args: Array[String]): Unit = { }
+ def assume[T]: Any = assume
+}
diff --git a/tests/pos/tailcall/return.scala b/tests/pos/tailcall/return.scala
index 454686c3d..3724b5b32 100644
--- a/tests/pos/tailcall/return.scala
+++ b/tests/pos/tailcall/return.scala
@@ -1,4 +1,4 @@
-object Test {
+object Return {
def foo(x: Int): Int = return 3