#lang lean4
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Attributes
import Lean.Compiler.Util

namespace Lean.Compiler

inductive InlineAttributeKind
| inline | noinline | macroInline | inlineIfReduce

namespace InlineAttributeKind

instance : Inhabited InlineAttributeKind := ⟨InlineAttributeKind.inline⟩

protected def beq : InlineAttributeKind → InlineAttributeKind → Bool
| inline, inline => true
| noinline, noinline => true
| macroInline, macroInline => true
| inlineIfReduce, inlineIfReduce => true
| _, _ => false

instance : HasBeq InlineAttributeKind := ⟨InlineAttributeKind.beq⟩

end InlineAttributeKind

initialize inlineAttrs : EnumAttributes InlineAttributeKind ←
registerEnumAttributes `inlineAttrs
  [(`inline, "mark definition to always be inlined", InlineAttributeKind.inline),
   (`inlineIfReduce, "mark definition to be inlined when resultant term after reduction is not a `cases_on` application", InlineAttributeKind.inlineIfReduce),
   (`noinline, "mark definition to never be inlined", InlineAttributeKind.noinline),
   (`macroInline, "mark definition to always be inlined before ANF conversion", InlineAttributeKind.macroInline)]
  (fun declName _ => do
    let env ← getEnv
    ofExcept $ checkIsDefinition env declName)

private partial def hasInlineAttrAux (env : Environment) (kind : InlineAttributeKind) : Name → Bool
| n =>
  /- We never inline auxiliary declarations created by eager lambda lifting -/
  if isEagerLambdaLiftingName n then false
  else match inlineAttrs.getValue env n with
    | some k => kind == k
    | none   => if n.isInternal then hasInlineAttrAux env kind n.getPrefix else false

@[export lean_has_inline_attribute]
def hasInlineAttribute (env : Environment) (n : Name) : Bool :=
hasInlineAttrAux env InlineAttributeKind.inline n

@[export lean_has_inline_if_reduce_attribute]
def hasInlineIfReduceAttribute (env : Environment) (n : Name) : Bool :=
hasInlineAttrAux env InlineAttributeKind.inlineIfReduce n

@[export lean_has_noinline_attribute]
def hasNoInlineAttribute (env : Environment) (n : Name) : Bool :=
hasInlineAttrAux env InlineAttributeKind.noinline n

@[export lean_has_macro_inline_attribute]
def hasMacroInlineAttribute (env : Environment) (n : Name) : Bool :=
hasInlineAttrAux env InlineAttributeKind.macroInline n

def setInlineAttribute (env : Environment) (declName : Name) (kind : InlineAttributeKind) : Except String Environment :=
inlineAttrs.setValue env declName kind

end Lean.Compiler
