Effects: add DSL for declaration of contracts

- Introduce DSL for declaration of contracts. Functions and classes in
this DSL do not bear any computational semantics: in fact, they all do
not exist on runtime. Whole work of extracting semantics from such calls
is done by special parts of compiler

- To make distinguishing those declarations more robust and
convenient, introduce internal annotation @ContractsDSL

- Turn off InlineChecker inside contracts DSL. We do this, because some
functions from DSL should take any possible lambda, so we can't write
any type besides 'Function<R>'. However, InlineChecker will complain if
we will pass inlined lambda in such function -- though, this is false
positive because we know that functions from contracts DSL will never be
executed

- Change testData on HierarchyTestWithLib, where new enum from
kotlin.internal changed expected output

==========
Effect System introduction: 7/18
This commit is contained in:
Dmitry Savvinov
2017-10-03 15:02:34 +03:00
parent b347e31fc4
commit 2c0ef592e6
5 changed files with 99 additions and 5 deletions
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.contracts.parsing.isFromContractDsl
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
@@ -59,10 +60,15 @@ internal class InlineChecker(private val descriptor: FunctionDescriptor) : CallC
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val expression = resolvedCall.call.calleeExpression ?: return val expression = resolvedCall.call.calleeExpression ?: return
supportDefaultValueInline = context.languageVersionSettings.supportsFeature(LanguageFeature.InlineDefaultFunctionalParameters) supportDefaultValueInline = context.languageVersionSettings.supportsFeature(LanguageFeature.InlineDefaultFunctionalParameters)
//checking that only invoke or inlinable extension called on function parameter //checking that only invoke or inlinable extension called on function parameter
val targetDescriptor = resolvedCall.resultingDescriptor val targetDescriptor = resolvedCall.resultingDescriptor
// Omit inline checks for 'contract'-call because those calls will never be executed, so inline checking is pointless
if (targetDescriptor.isFromContractDsl()) return
checkCallWithReceiver(context, targetDescriptor, resolvedCall.dispatchReceiver, expression) checkCallWithReceiver(context, targetDescriptor, resolvedCall.dispatchReceiver, expression)
checkCallWithReceiver(context, targetDescriptor, resolvedCall.extensionReceiver, expression) checkCallWithReceiver(context, targetDescriptor, resolvedCall.extensionReceiver, expression)
+6 -5
View File
@@ -9,9 +9,11 @@
<node text="PrintMode in JTable (javax.swing)"/> <node text="PrintMode in JTable (javax.swing)"/>
<node text="ElementType (java.lang.annotation)"/> <node text="ElementType (java.lang.annotation)"/>
<node text="State in Thread (java.lang)"/> <node text="State in Thread (java.lang)"/>
<node text="RetentionPolicy (java.lang.annotation)"/>
<node text="Type in Window (java.awt)"/> <node text="Type in Window (java.awt)"/>
<node text="Kind in KParameter (kotlin.reflect)"/> <node text="RetentionPolicy (java.lang.annotation)"/>
<node text="InvocationKind (kotlin.internal.contracts)"/>
<node text="One ()"/>
<node text="G ()"/>
<node text="FileWalkDirection (kotlin.io)"/> <node text="FileWalkDirection (kotlin.io)"/>
<node text="OnErrorAction (kotlin.io)"/> <node text="OnErrorAction (kotlin.io)"/>
<node text="RegexOption (kotlin.text)"/> <node text="RegexOption (kotlin.text)"/>
@@ -22,9 +24,8 @@
<node text="AnnotationTarget (kotlin.annotation)"/> <node text="AnnotationTarget (kotlin.annotation)"/>
<node text="AnnotationRetention (kotlin.annotation)"/> <node text="AnnotationRetention (kotlin.annotation)"/>
<node text="State (kotlin.collections)"/> <node text="State (kotlin.collections)"/>
<node text="DeprecationLevel (kotlin)"/>
<node text="LazyThreadSafetyMode (kotlin)"/> <node text="LazyThreadSafetyMode (kotlin)"/>
<node text="One ()"/> <node text="DeprecationLevel (kotlin)"/>
<node text="G ()"/> <node text="Kind in KParameter (kotlin.reflect)"/>
</node> </node>
</node> </node>
@@ -102,3 +102,9 @@ internal enum class RequireKotlinVersionKind {
COMPILER_VERSION, COMPILER_VERSION,
API_VERSION, API_VERSION,
} }
/**
* Specifies that this declaration is a part of special DSL, used for constructing function's contract.
*/
@Retention(AnnotationRetention.BINARY)
internal annotation class ContractsDsl
@@ -0,0 +1,40 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.internal.contracts
import kotlin.internal.ContractsDsl
import kotlin.internal.InlineOnly
@ContractsDsl
internal interface ContractBuilder {
@ContractsDsl fun returns(): Returns
@ContractsDsl fun returns(value: Any?): Returns
@ContractsDsl fun returnsNotNull(): ReturnsNotNull
@ContractsDsl fun <R> callsInPlace(lambda: Function<R>, kind: InvocationKind = InvocationKind.UNKNOWN): CallsInPlace
}
@ContractsDsl
internal enum class InvocationKind {
@ContractsDsl AT_MOST_ONCE,
@ContractsDsl AT_LEAST_ONCE,
@ContractsDsl EXACTLY_ONCE,
@ContractsDsl UNKNOWN
}
@ContractsDsl
@InlineOnly
internal inline fun contract(builder: ContractBuilder.() -> Unit) { }
@@ -0,0 +1,41 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin.internal.contracts
import kotlin.internal.ContractsDsl
@ContractsDsl
internal interface Effect
@ContractsDsl
internal interface ConditionalEffect : Effect
@ContractsDsl
internal interface SimpleEffect {
@ContractsDsl
infix fun implies(booleanExpression: Boolean): ConditionalEffect
}
@ContractsDsl
internal interface Returns : SimpleEffect
@ContractsDsl
internal interface ReturnsNotNull : SimpleEffect
@ContractsDsl
internal interface CallsInPlace : SimpleEffect